///|
pub fn validate_manifest(manifest : BenchmarkManifest) -> ValidationSummary {
  let issues : Array[ValidationIssue] = []
  if manifest.name.trim(chars=" \t\r\n").is_empty() {
    issues.push({
      code: "empty_manifest_name",
      level: Error,
      message: "manifest name must not be empty",
      query_id: "",
      doc_id: "",
    })
  }
  if manifest.version.trim(chars=" \t\r\n").is_empty() {
    issues.push({
      code: "empty_manifest_version",
      level: Error,
      message: "manifest version must not be empty",
      query_id: "",
      doc_id: "",
    })
  }
  if manifest.license.trim(chars=" \t\r\n").is_empty() {
    issues.push({
      code: "empty_manifest_license",
      level: Error,
      message: "manifest license must not be empty",
      query_id: "",
      doc_id: "",
    })
  }
  if manifest.qrels_rows == 0 {
    issues.push({
      code: "empty_manifest_qrels",
      level: Error,
      message: "manifest qrels_rows must be positive",
      query_id: "",
      doc_id: "",
    })
  }
  let mut errors = 0
  let mut warnings = 0
  for issue in issues {
    match issue.level {
      Error => errors += 1
      Warning => warnings += 1
    }
  }
  {
    error_count: errors,
    warning_count: warnings,
    query_count: manifest.query_count,
    judged_count: manifest.qrels_rows,
    retrieved_count: manifest.run_rows,
    issues,
  }
}

///|
pub fn render_manifest(manifest : BenchmarkManifest) -> String {
  let lines : Array[String] = [
    "name=\{manifest.name}",
    "version=\{manifest.version}",
    "license=\{manifest.license}",
    "queries=\{manifest.query_count}",
    "qrels_rows=\{manifest.qrels_rows}",
    "run_rows=\{manifest.run_rows}",
    "relevant_documents=\{manifest.relevant_documents}",
    "candidate_coverage=\{format_metric(manifest.candidate_coverage)}",
  ]
  lines.join("\n")
}

///|
pub fn render_manifest_json(manifest : BenchmarkManifest) -> String {
  ToJson::to_json(manifest).stringify(indent=2)
}

///|
pub fn manifest_with_pools(
  name~ : String,
  version~ : String,
  license~ : String,
  qrels~ : Array[JudgedDoc],
  run~ : Array[RetrievedDoc],
  pools~ : Array[CandidatePool],
) -> BenchmarkManifest {
  let base = BenchmarkManifest::new(name~, version~, license~, qrels~, run~)
  {
    name: base.name,
    version: base.version,
    license: base.license,
    query_count: base.query_count,
    qrels_rows: base.qrels_rows,
    run_rows: base.run_rows,
    relevant_documents: base.relevant_documents,
    candidate_coverage: candidate_recall(qrels, pools),
  }
}