///|
pub fn render_markdown_report(
  diagnostics : Array[Diagnostic],
  title : String,
) -> String {
  let report = DiagnosticReport::new(diagnostics)
  let builder = StringBuilder()
  builder.write_string("# ")
  builder.write_string(title)
  builder.write_string("\n\n")
  builder.write_string("- Total: ")
  builder.write_object(report.total())
  builder.write_string("\n- Errors: ")
  builder.write_object(report.errors)
  builder.write_string("\n- Warnings: ")
  builder.write_object(report.warnings)
  builder.write_string("\n- Info: ")
  builder.write_object(report.infos)
  builder.write_string(
    "\n\n| Code | Severity | File | Start | Message |\n| --- | --- | --- | ---: | --- |\n",
  )
  for diagnostic in stable_diagnostic_order(diagnostics) {
    builder.write_string("| ")
    builder.write_string(diagnostic.code)
    builder.write_string(" | ")
    builder.write_string(diagnostic.severity.label())
    builder.write_string(" | ")
    builder.write_string(diagnostic.file)
    builder.write_string(" | ")
    builder.write_object(diagnostic.span.start)
    builder.write_string(" | ")
    builder.write_string(diagnostic.message.replace(old="|", new="\\|"))
    builder.write_string(" |\n")
  }
  builder.to_string()
}

///|
pub fn render_rule_catalog(rules : Array[String]) -> String {
  let builder = StringBuilder()
  builder.write_string("| Code | Description |\n| --- | --- |\n")
  for code in rules {
    builder.write_string("| ")
    builder.write_string(code)
    builder.write_string(" | active rule |\n")
  }
  builder.to_string()
}

///|
pub fn DiagnosticReport::markdown(
  self : DiagnosticReport,
  title : String,
) -> String {
  render_markdown_report(self.diagnostics, title)
}

///|
pub fn DiagnosticReport::code_counts(
  self : DiagnosticReport,
) -> Array[(String, Int)] {
  group_diagnostics_by_code(self.diagnostics)
}

///|
pub fn DiagnosticReport::files(self : DiagnosticReport) -> Array[String] {
  let result : Array[String] = []
  for diagnostic in self.diagnostics {
    if !result.contains(diagnostic.file) {
      result.push(diagnostic.file)
    }
  }
  result
}