///|
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(
"Check Result Points Time ",
)
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(
"\{json_escape(check.title)} \{result} \{check.earned_points}/\{check.possible_points} \{check.duration_ms} ms ",
)
}
builder.write_string("
")
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()
}