///|
pub fn format_sarif(result : ScanResult) -> String {
  let rule_descriptors : Array[Json] = []
  let seen_rules : Map[String, Bool] = Map([])
  for f in result.findings {
    let id = rule_id_str(f.rule_id)
    if !seen_rules.contains(id) {
      seen_rules[id] = true
      rule_descriptors.push(rule_to_sarif_descriptor(f.rule_id))
    }
  }
  let sarif_results : Array[Json] = []
  for f in result.findings {
    sarif_results.push(finding_to_sarif(f))
  }
  let sarif : Json = {
    "$schema": Json::string(
      "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json",
    ),
    "version": Json::string("2.1.0"),
    "runs": Json::array([
      {
        "tool": {
          "driver": {
            "name": Json::string("moon-audit"),
            "version": Json::string("0.1.0"),
            "rules": Json::array(rule_descriptors),
          },
        },
        "results": Json::array(sarif_results),
      },
    ]),
  }
  sarif.stringify(indent=2)
}

///|
fn rule_to_sarif_descriptor(id : RuleId) -> Json {
  let info = get_rule_info_by_id(id)
  {
    "id": Json::string(rule_id_str(id)),
    "shortDescription": { "text": Json::string(info.name) },
    "helpUri": Json::string(
      "https://cwe.mitre.org/data/definitions/" + id.cwe.to_string() + ".html",
    ),
  }
}

///|
fn finding_to_sarif(f : Finding) -> Json {
  let level = match f.severity {
    Error => "error"
    Warning => "warning"
    Info => "note"
  }
  {
    "ruleId": Json::string(rule_id_str(f.rule_id)),
    "level": Json::string(level),
    "message": { "text": Json::string(f.message) },
    "locations": Json::array([
      {
        "physicalLocation": {
          "artifactLocation": { "uri": Json::string(f.file) },
          "region": {
            "startLine": Json::number(f.line.to_double()),
            "startColumn": Json::number(f.column.to_double()),
            "endLine": Json::number(f.end_line.to_double()),
            "endColumn": Json::number(f.end_column.to_double()),
          },
        },
      },
    ]),
    "partialFingerprints": { "moon-audit/v1": Json::string(f.fingerprint) },
    "properties": { "confidence": Json::string(confidence_str(f.confidence)) },
  }
}

///|
fn get_rule_info_by_id(id : RuleId) -> RuleInfo {
  let rules = list_all_rules()
  for rule in rules {
    if rule.id.cwe == id.cwe && rule.id.variant == id.variant {
      return rule
    }
  }
  { id, name: rule_id_str(id), description: "", default_severity: Warning }
}