///|
fn html_escape(text : String) -> String {
  text
  .replace(old="&", new="&")
  .replace(old="<", new="<")
  .replace(old=">", new=">")
  .replace(old="\"", new=""")
}

///|
fn status_class(value : Bool) -> String {
  if value {
    "ok"
  } else {
    "bad"
  }
}

///|
fn status_text(value : Bool, lang : String) -> String {
  if value {
    if is_zh(lang) {
      "通过"
    } else {
      "OK"
    }
  } else if is_zh(lang) {
    "缺失"
  } else {
    "Missing"
  }
}

///|
fn push_metric(lines : Array[String], label : String, value : String) -> Unit {
  lines.push(
    "
" + html_escape(label) + "" + html_escape(value) + "
", ) } ///| fn push_signal( lines : Array[String], label : String, value : Bool, lang : String, ) -> Unit { lines.push( "
  • " + html_escape(label) + "" + status_text(value, lang) + "
  • ", ) } ///| fn coverage_class(value : Int) -> String { if value >= 90 { "ok" } else if value >= 70 { "warn" } else { "bad" } } ///| fn issue_kind(issue : DocIssue, lang : String) -> String { if issue.message.contains("Missing documentation") { if is_zh(lang) { "缺少 API 文档" } else { "Missing API docs" } } else if issue.message.contains("Weak documentation") { if is_zh(lang) { "弱文档" } else { "Weak docs" } } else if issue.message.contains("README") { "README" } else if issue.message.contains("moon.mod") { "moon.mod" } else if issue.message.contains("interface summary") || issue.message.contains("moon info") { "pkg.generated.mbti" } else if issue.message.contains("CI") || issue.message.contains("GitHub Actions") { "CI" } else if is_zh(lang) { "项目检查" } else { "Project check" } } ///| fn issue_kind_key(issue : DocIssue) -> String { if issue.message.contains("Missing documentation") { "api-missing" } else if issue.message.contains("Weak documentation") { "api-weak" } else if issue.message.contains("README") { "readme" } else if issue.message.contains("moon.mod") { "moon-mod" } else if issue.message.contains("interface summary") || issue.message.contains("moon info") { "mbti" } else if issue.message.contains("CI") || issue.message.contains("GitHub Actions") { "ci" } else { "project" } } ///| fn issue_explanation(issue : DocIssue, lang : String) -> String { let key = issue_kind_key(issue) if key == "api-missing" { if is_zh(lang) { "公开 API 是外部用户最先接触的接口,缺少 `///` 文档会降低可用性,也会影响后续生成文档。" } else { "Public APIs are the first surface users rely on; missing `///` docs reduce usability and generated documentation quality." } } else if key == "api-weak" { if is_zh(lang) { "占位式注释虽然能通过形式检查,但不能说明用途、参数含义或使用边界。" } else { "Placeholder comments pass a shape check but do not explain purpose, parameters, or boundaries." } } else if key == "readme" { if is_zh(lang) { "README 是用户进入项目后的第一份说明,缺少使用信息会让项目难以快速试用。" } else { "The README is the entry document; missing usage information makes the project harder to try." } } else if key == "moon-mod" { if is_zh(lang) { "Moon 元数据会影响发布、检索和评审,描述、仓库地址、许可证等字段应尽量完整。" } else { "Moon metadata supports publishing, discovery, and review; description, repository, and license should be complete." } } else if key == "mbti" { if is_zh(lang) { "`pkg.generated.mbti` 是 `moon info` 生成的公开接口摘要,适合作为发布前 API 审查依据。" } else { "`pkg.generated.mbti` is generated by `moon info` and helps review the public API surface before release." } } else if key == "ci" { if is_zh(lang) { "CI 信号说明项目是否在提交后自动执行基础检查;脚本式验证会单独标记。" } else { "CI signals show whether basic checks run after commits; script-based validation is tracked separately." } } else if is_zh(lang) { "该问题来自项目级文档准备度检查。" } else { "This issue comes from project-level documentation readiness checks." } } ///| fn push_html_head(lines : Array[String], lang : String) -> Unit { lines.push("") lines.push( "", ) lines.push("") lines.push("") lines.push( "", ) lines.push( "" + (if is_zh(lang) { "MoonDocCheck 报告" } else { "MoonDocCheck Report" }) + "", ) lines.push( ( #| ), ) lines.push("") lines.push("
    ") } ///| fn push_html_signals( lines : Array[String], report : ProjectReport, lang : String, ) -> Unit { lines.push("
    ") lines.push("

    README

      ") push_signal( lines, if is_zh(lang) { "找到" } else { "Found" }, report.readme.found, lang, ) push_signal( lines, if is_zh(lang) { "项目介绍" } else { "Overview" }, report.readme.has_overview, lang, ) push_signal( lines, if is_zh(lang) { "文档入口" } else { "Docs entry" }, report.readme.has_usage, lang, ) push_signal( lines, if is_zh(lang) { "开发命令" } else { "Development commands" }, report.readme.has_development_commands, lang, ) lines.push("
    ") lines.push( "

    " + (if is_zh(lang) { "项目元数据与 CI" } else { "Metadata and CI" }) + "

      ", ) push_signal(lines, "moon.mod", report.moon_mod.found, lang) push_signal(lines, "moon.mod.json", report.moon_mod.legacy_json_found, lang) push_signal(lines, "pkg.generated.mbti", report.has_generated_mbti, lang) push_signal(lines, "moon check", report.ci.has_moon_check, lang) push_signal(lines, "moon test", report.ci.has_moon_test, lang) push_signal( lines, if is_zh(lang) { "脚本式验证" } else { "Script validation" }, report.ci.has_script_validation, lang, ) lines.push("
    ") } ///| fn push_html_top_files( lines : Array[String], report : ProjectReport, lang : String, ) -> Unit { let top_files = top_missing_files(report, 8) if !top_files.is_empty() { lines.push( "

    " + (if is_zh(lang) { "缺文档重灾区" } else { "Top Missing-Doc Files" }) + "

    ", ) lines.push( "", ) for item in top_files { lines.push( "", ) } lines.push("
    " + (if is_zh(lang) { "文件" } else { "File" }) + "" + (if is_zh(lang) { "缺失" } else { "Missing" }) + "" + (if is_zh(lang) { "覆盖率" } else { "Coverage" }) + "
    " + html_escape(display_path(report, item.file)) + "" + item.public_api_missing_docs.to_string() + "" + item.documentation_coverage.to_string() + "%
    ") } } ///| fn push_html_file_coverage( lines : Array[String], report : ProjectReport, lang : String, ) -> Unit { if !report.file_coverage.is_empty() { lines.push( "

    " + (if is_zh(lang) { "文件覆盖率" } else { "File Coverage" }) + "

    ", ) lines.push( "", ) for item in report.file_coverage { lines.push( "", ) } lines.push( "
    " + (if is_zh(lang) { "文件" } else { "File" }) + "" + (if is_zh(lang) { "公开 API" } else { "Public APIs" }) + "" + (if is_zh(lang) { "缺失" } else { "Missing" }) + "" + (if is_zh(lang) { "覆盖率" } else { "Coverage" }) + "
    " + html_escape(display_path(report, item.file)) + "" + item.public_api_total.to_string() + "" + item.public_api_missing_docs.to_string() + "" + item.documentation_coverage.to_string() + "%

    " + (if is_zh(lang) { "没有匹配的文件。" } else { "No matching files." }) + "

    ", ) } } ///| fn push_html_issues( lines : Array[String], report : ProjectReport, all_issues : Bool, lang : String, ) -> Unit { lines.push( "

    " + (if is_zh(lang) { "问题清单" } else { "Issues" }) + "

    " + (if is_zh(lang) { "可按问题类型或路径搜索,展开单项可查看判定依据。" } else { "Filter by issue type or path. Expand an item to inspect the rationale." }) + "

    ", ) if report.issues.is_empty() { lines.push( "

    " + (if is_zh(lang) { "未发现问题。" } else { "No issues found." }) + "

    ", ) } else { lines.push( "
    ", ) let limit = if all_issues { report.issues.length() } else if report.issues.length() > 80 { 80 } else { report.issues.length() } for index in 0.. display_path(report, issue.file) + ":" + line.to_string() None => display_path(report, issue.file) } let search_text = (location + " " + issue_message(issue, lang) + " " + issue_kind(issue, lang)).to_lower() lines.push( "
    " + html_escape(location) + "
    " + html_escape(issue_kind(issue, lang)) + "
    " + html_escape(issue_message(issue, lang)) + "
    " + html_escape(fix_hint(issue, lang)) + "
    " + (if is_zh(lang) { "查看判定依据" } else { "View rationale" }) + "

    " + html_escape(issue_explanation(issue, lang)) + "

    ", ) } if report.issues.length() > limit { lines.push( "

    ... " + (report.issues.length() - limit).to_string() + (if is_zh(lang) { " 个问题未显示。使用 --all-issues 查看全部。" } else { " more issues hidden. Use --all-issues to show everything." }) + "

    ", ) } lines.push( "

    " + (if is_zh(lang) { "没有匹配的问题。" } else { "No matching issues." }) + "

    ", ) } lines.push("
    ") } ///| fn push_html_script(lines : Array[String]) -> Unit { lines.push( ( #| ), ) } ///| /// Render a project report as a standalone HTML preview. pub fn render_html( report : ProjectReport, all_issues? : Bool = true, lang? : String = "zh", ) -> String { let lines : Array[String] = [] push_html_head(lines, lang) lines.push( "

    " + (if is_zh(lang) { "MoonDocCheck 报告" } else { "MoonDocCheck Report" }) + "

    " + html_escape(report.project_name) + "
    " + html_escape(readiness_level(report, lang)) + "
    ", ) lines.push("
    ") push_metric( lines, if is_zh(lang) { "扫描文件数" } else { "Files scanned" }, report.files_scanned.to_string(), ) push_metric( lines, if is_zh(lang) { "MoonBit 源文件数" } else { "MoonBit source files" }, report.moonbit_source_files.to_string(), ) push_metric( lines, if is_zh(lang) { "公开 API" } else { "Public APIs" }, report.public_api_total.to_string(), ) push_metric( lines, if is_zh(lang) { "文档覆盖率" } else { "Coverage" }, report.documentation_coverage.to_string() + "%", ) lines.push("
    ") lines.push( "

    " + (if is_zh(lang) { "总体评价" } else { "Overall Assessment" }) + "

    " + html_escape(readiness_comment(report, lang)) + "

    " + html_escape( if is_zh(lang) { report.public_api_total.to_string() + " 个公开 API 中有 " + report.public_api_missing_docs.to_string() + " 个缺少文档。" } else { report.public_api_missing_docs.to_string() + " of " + report.public_api_total.to_string() + " public APIs are missing docs." }, ) + "

    ", ) lines.push( "

    " + (if is_zh(lang) { "下一步" } else { "Next Steps" }) + "

      ", ) for step in next_steps(report, lang) { lines.push("
    1. " + html_escape(step) + "
    2. ") } lines.push("
    ") push_html_top_files(lines, report, lang) push_html_signals(lines, report, lang) push_html_file_coverage(lines, report, lang) push_html_issues(lines, report, all_issues, lang) push_html_script(lines) lines.push("
    ") lines.join("\n") }