///|
fn json_escape(text : StringView) -> String {
  let builder = StringBuilder()
  for ch in text {
    match ch {
      '"' => builder.write_string("\\\"")
      '\\' => builder.write_string("\\\\")
      '\n' => builder.write_string("\\n")
      '\r' => builder.write_string("\\r")
      '\t' => builder.write_string("\\t")
      _ => builder.write_char(ch)
    }
  }
  builder.to_string()
}

///|
fn status_text(report : GradeReport) -> String {
  if report.passed {
    "PASS"
  } else {
    "NEEDS WORK"
  }
}

///|
pub fn render_markdown(report : GradeReport) -> String {
  let builder = StringBuilder()
  builder.write_string("# MoonGrade report\n\n")
  builder.write_string("- Assignment: **\{report.assignment_name}**\n")
  builder.write_string("- Submission: `\{report.student_path}`\n")
  builder.write_string("- Score: **\{report.score} / \{report.max_score}**\n")
  builder.write_string("- Result: **\{status_text(report)}**\n\n")
  builder.write_string("| Check | Result | Points | Time |\n")
  builder.write_string("| --- | --- | ---: | ---: |\n")
  for check in report.checks {
    let result = if check.passed { "PASS" } else { "FAIL" }
    builder.write_string(
      "| \{check.title} | \{result} | \{check.earned_points}/\{check.possible_points} | \{check.duration_ms} ms |\n",
    )
  }
  builder.write_string("\n## Feedback\n\n")
  for check in report.checks {
    if !check.feedback.is_empty() {
      builder.write_string("### \{check.title}\n\n")
      for feedback in check.feedback {
        builder.write_string("- \{feedback}\n")
      }
      builder.write_char('\n')
    }
  }
  builder.write_string("## Command output\n\n")
  for check in report.checks {
    builder.write_string("### \{check.title}\n\n")
    if check.output_excerpt.is_empty() {
      builder.write_string("No output.\n\n")
    } else {
      builder.write_string("```text\n\{check.output_excerpt}\n```\n\n")
    }
  }
  builder.to_string()
}

///|
pub fn render_json(report : GradeReport) -> String {
  let builder = StringBuilder()
  builder.write_string("{\n")
  builder.write_string(
    "  \"assignment\": \"\{json_escape(report.assignment_name)}\",\n",
  )
  builder.write_string(
    "  \"student_path\": \"\{json_escape(report.student_path)}\",\n",
  )
  builder.write_string("  \"score\": \{report.score},\n")
  builder.write_string("  \"max_score\": \{report.max_score},\n")
  builder.write_string("  \"passed\": \{report.passed},\n")
  builder.write_string("  \"checks\": [")
  for index, check in report.checks {
    if index > 0 {
      builder.write_char(',')
    }
    builder.write_string("\n    {")
    builder.write_string("\"id\": \"\{json_escape(check.id)}\", ")
    builder.write_string("\"title\": \"\{json_escape(check.title)}\", ")
    builder.write_string("\"passed\": \{check.passed}, ")
    builder.write_string("\"earned_points\": \{check.earned_points}, ")
    builder.write_string("\"possible_points\": \{check.possible_points}, ")
    builder.write_string("\"exit_code\": \{check.exit_code}, ")
    builder.write_string("\"timed_out\": \{check.timed_out}, ")
    builder.write_string("\"duration_ms\": \{check.duration_ms}, ")
    builder.write_string("\"feedback\": [")
    for feedback_index, feedback in check.feedback {
      if feedback_index > 0 {
        builder.write_char(',')
      }
      builder.write_string("\"\{json_escape(feedback)}\"")
    }
    builder.write_string("]}")
  }
  if !report.checks.is_empty() {
    builder.write_string("\n  ")
  }
  builder.write_string("]\n}\n")
  builder.to_string()
}

///|
pub fn render_html(report : GradeReport) -> String {
  let builder = StringBuilder()
  builder.write_string(
    "MoonGrade report",
  )
  builder.write_string(
    "",
  )
  builder.write_string("

MoonGrade report

") builder.write_string("

\{json_escape(report.assignment_name)}

") builder.write_string( "
\{report.score} / \{report.max_score} points ", ) if report.passed { builder.write_string("PASS
") } else { builder.write_string("NEEDS WORK") } builder.write_string( "", ) for check in report.checks { let result = if check.passed { "PASS" } else { "FAIL" } let result_class = if check.passed { "pass" } else { "fail" } builder.write_string( "", ) } builder.write_string("
CheckResultPointsTime
\{json_escape(check.title)}\{result}\{check.earned_points}/\{check.possible_points}\{check.duration_ms} ms
") for check in report.checks { builder.write_string( "
\{json_escape(check.title)}\{check.earned_points}/\{check.possible_points}
", ) if !check.feedback.is_empty() { builder.write_string("
    ") for feedback in check.feedback { builder.write_string("
  • \{json_escape(feedback)}
  • ") } builder.write_string("
") } if !check.output_excerpt.is_empty() { builder.write_string("
\{json_escape(check.output_excerpt)}
") } builder.write_string("
") } builder.write_string("") builder.to_string() }