///|
/// A mutable builder for one SARIF analysis log.
pub struct LogBuilder {
  mut tool : ToolComponent
  results : Array[SarifResult]
  mut schema : String?
}

///|
/// Create a builder with a SARIF 2.1.0 tool driver.
pub fn LogBuilder::new(
  tool_name~ : String,
  tool_version? : String = "",
) -> LogBuilder {
  let version = if tool_version.is_empty() { None } else { Some(tool_version) }
  {
    tool: {
      name: tool_name,
      version,
      semanticVersion: None,
      informationUri: None,
      rules: None,
    },
    results: [],
    schema: Some("https://json.schemastore.org/sarif-2.1.0.json"),
  }
}

///|
/// Set the JSON schema URI emitted by the builder.
pub fn LogBuilder::set_schema(self : LogBuilder, schema : String) -> Unit {
  self.schema = Some(schema)
}

///|
/// Add a rule to the tool driver.
pub fn LogBuilder::add_rule(
  self : LogBuilder,
  id~ : String,
  name? : String,
  help_uri? : String,
) -> Unit {
  let descriptor = {
    id,
    name,
    shortDescription: None,
    fullDescription: None,
    helpUri: help_uri,
  }
  let rules = match self.tool.rules {
    Some(rules) => {
      rules.push(descriptor)
      rules
    }
    None => [descriptor]
  }
  self.tool = {
    name: self.tool.name,
    version: self.tool.version,
    semanticVersion: self.tool.semanticVersion,
    informationUri: self.tool.informationUri,
    rules: Some(rules),
  }
}

///|
/// Add a result and return its zero-based index.
pub fn LogBuilder::add_result(
  self : LogBuilder,
  rule_id? : String,
  level? : String,
  message~ : Message,
  location? : Location,
) -> Int {
  let locations = match location {
    Some(value) => Some([value])
    None => None
  }
  let result = {
    ruleId: rule_id,
    ruleIndex: None,
    level,
    message,
    locations,
    relatedLocations: None,
    partialFingerprints: None,
    fingerprints: None,
    suppressions: None,
    fixes: None,
    properties: None,
    baselineState: None,
  }
  self.results.push(result)
  self.results.length() - 1
}

///|
fn LogBuilder::update_result(
  self : LogBuilder,
  result_index : Int,
  update : (SarifResult) -> SarifResult,
) -> Unit {
  self.results[result_index] = update(self.results[result_index])
}

///|
/// Replace the primary locations of a result.
pub fn LogBuilder::set_locations(
  self : LogBuilder,
  result_index : Int,
  locations : Array[Location],
) -> Unit {
  self.update_result(result_index, fn(result) {
    {
      ruleId: result.ruleId,
      ruleIndex: result.ruleIndex,
      level: result.level,
      message: result.message,
      locations: Some(locations),
      relatedLocations: result.relatedLocations,
      partialFingerprints: result.partialFingerprints,
      fingerprints: result.fingerprints,
      suppressions: result.suppressions,
      fixes: result.fixes,
      properties: result.properties,
      baselineState: result.baselineState,
    }
  })
}

///|
/// Set related locations for a result.
pub fn LogBuilder::set_related_locations(
  self : LogBuilder,
  result_index : Int,
  locations : Array[Location],
) -> Unit {
  self.update_result(result_index, fn(result) {
    {
      ruleId: result.ruleId,
      ruleIndex: result.ruleIndex,
      level: result.level,
      message: result.message,
      locations: result.locations,
      relatedLocations: Some(locations),
      partialFingerprints: result.partialFingerprints,
      fingerprints: result.fingerprints,
      suppressions: result.suppressions,
      fixes: result.fixes,
      properties: result.properties,
      baselineState: result.baselineState,
    }
  })
}

///|
/// Set partial fingerprints for a result.
pub fn LogBuilder::set_partial_fingerprints(
  self : LogBuilder,
  result_index : Int,
  fingerprints : Map[String, String],
) -> Unit {
  self.update_result(result_index, fn(result) {
    {
      ruleId: result.ruleId,
      ruleIndex: result.ruleIndex,
      level: result.level,
      message: result.message,
      locations: result.locations,
      relatedLocations: result.relatedLocations,
      partialFingerprints: Some(fingerprints),
      fingerprints: result.fingerprints,
      suppressions: result.suppressions,
      fixes: result.fixes,
      properties: result.properties,
      baselineState: result.baselineState,
    }
  })
}

///|
/// Set fingerprints for a result.
pub fn LogBuilder::set_fingerprints(
  self : LogBuilder,
  result_index : Int,
  fingerprints : Map[String, String],
) -> Unit {
  self.update_result(result_index, fn(result) {
    {
      ruleId: result.ruleId,
      ruleIndex: result.ruleIndex,
      level: result.level,
      message: result.message,
      locations: result.locations,
      relatedLocations: result.relatedLocations,
      partialFingerprints: result.partialFingerprints,
      fingerprints: Some(fingerprints),
      suppressions: result.suppressions,
      fixes: result.fixes,
      properties: result.properties,
      baselineState: result.baselineState,
    }
  })
}

///|
/// Set suppressions for a result.
pub fn LogBuilder::set_suppressions(
  self : LogBuilder,
  result_index : Int,
  suppressions : Array[Suppression],
) -> Unit {
  self.update_result(result_index, fn(result) {
    {
      ruleId: result.ruleId,
      ruleIndex: result.ruleIndex,
      level: result.level,
      message: result.message,
      locations: result.locations,
      relatedLocations: result.relatedLocations,
      partialFingerprints: result.partialFingerprints,
      fingerprints: result.fingerprints,
      suppressions: Some(suppressions),
      fixes: result.fixes,
      properties: result.properties,
      baselineState: result.baselineState,
    }
  })
}

///|
/// Set fixes for a result.
pub fn LogBuilder::set_fixes(
  self : LogBuilder,
  result_index : Int,
  fixes : Array[Fix],
) -> Unit {
  self.update_result(result_index, fn(result) {
    {
      ruleId: result.ruleId,
      ruleIndex: result.ruleIndex,
      level: result.level,
      message: result.message,
      locations: result.locations,
      relatedLocations: result.relatedLocations,
      partialFingerprints: result.partialFingerprints,
      fingerprints: result.fingerprints,
      suppressions: result.suppressions,
      fixes: Some(fixes),
      properties: result.properties,
      baselineState: result.baselineState,
    }
  })
}

///|
/// Set arbitrary SARIF properties for a result.
pub fn LogBuilder::set_properties(
  self : LogBuilder,
  result_index : Int,
  properties : Map[String, Json],
) -> Unit {
  self.update_result(result_index, fn(result) {
    {
      ruleId: result.ruleId,
      ruleIndex: result.ruleIndex,
      level: result.level,
      message: result.message,
      locations: result.locations,
      relatedLocations: result.relatedLocations,
      partialFingerprints: result.partialFingerprints,
      fingerprints: result.fingerprints,
      suppressions: result.suppressions,
      fixes: result.fixes,
      properties: Some(properties),
      baselineState: result.baselineState,
    }
  })
}

///|
/// Set the baseline state for a result.
pub fn LogBuilder::set_baseline_state(
  self : LogBuilder,
  result_index : Int,
  baseline_state : String,
) -> Unit {
  self.update_result(result_index, fn(result) {
    {
      ruleId: result.ruleId,
      ruleIndex: result.ruleIndex,
      level: result.level,
      message: result.message,
      locations: result.locations,
      relatedLocations: result.relatedLocations,
      partialFingerprints: result.partialFingerprints,
      fingerprints: result.fingerprints,
      suppressions: result.suppressions,
      fixes: result.fixes,
      properties: result.properties,
      baselineState: Some(baseline_state),
    }
  })
}

///|
/// Finish the log and return the ordinary SARIF data model.
pub fn LogBuilder::finish(self : LogBuilder) -> SarifLog {
  {
    version: "2.1.0",
    schema: self.schema,
    runs: [
      {
        tool: { driver: self.tool, extensions: None },
        results: Some(self.results),
        automationDetails: None,
      },
    ],
  }
}

///|
/// Construct a text SARIF message.
pub fn message_text(text : String) -> Message {
  { text: Some(text), markdown: None }
}

///|
/// Construct a markdown SARIF message.
pub fn message_markdown(markdown : String) -> Message {
  { text: None, markdown: Some(markdown) }
}

///|
/// Construct a suppression for a result.
pub fn make_suppression(
  kind~ : String,
  justification? : String,
  location? : Location,
  guid? : String,
) -> Suppression {
  { kind, justification, location, guid }
}

///|
/// Construct a source location with a one-based start line.
pub fn make_location(
  uri~ : String,
  start_line~ : Int,
  start_column? : Int,
  end_line? : Int,
  end_column? : Int,
) -> Location {
  {
    physicalLocation: Some({
      artifactLocation: Some({ uri: Some(uri), uriBaseId: None, index: None }),
      region: Some({
        startLine: Some(start_line),
        startColumn: start_column,
        endLine: end_line,
        endColumn: end_column,
        snippet: None,
      }),
    }),
    message: None,
  }
}