///|
fn is_zh(lang : String) -> Bool {
lang != "en"
}
///|
fn yes_no(value : Bool) -> String {
if value {
"yes"
} else {
"no"
}
}
///|
fn yes_no_lang(value : Bool, lang : String) -> String {
if is_zh(lang) {
if value {
"是"
} else {
"否"
}
} else {
yes_no(value)
}
}
///|
fn display_path(report : ProjectReport, file : String) -> String {
let root = report.project_root
if root != "" {
let normalized_root = root.replace(old="/", new="\\")
let normalized_file = file.replace(old="/", new="\\")
if normalized_file.has_prefix(normalized_root + "\\") {
normalized_file[normalized_root.length() + 1:].to_owned()
} else {
file
}
} else {
file
}
}
///|
fn issue_location(
report : ProjectReport,
issue : DocIssue,
lang : String,
) -> String {
let path = display_path(report, issue.file)
let message = issue_message(issue, lang)
if path == message {
match issue.line {
Some(line) => path + ":" + line.to_string()
None => path
}
} else if message.has_prefix(path + " ") {
match issue.line {
Some(line) =>
path +
":" +
line.to_string() +
" " +
message[path.length() + 1:].to_owned()
None => message
}
} else {
match issue.line {
Some(line) => path + ":" + line.to_string() + " " + message
None => path + " " + message
}
}
}
///|
fn readiness_level(report : ProjectReport, lang : String) -> String {
if report.documentation_coverage >= 90 &&
report.readme.found &&
report.readme.has_usage &&
report.moon_mod.found &&
report.has_generated_mbti &&
report.ci.has_moon_check &&
report.ci.has_moon_test {
if is_zh(lang) {
"优秀"
} else {
"Excellent"
}
} else if report.documentation_coverage >= 70 &&
report.readme.found &&
report.moon_mod.found {
if is_zh(lang) {
"良好"
} else {
"Good"
}
} else if report.documentation_coverage >= 40 {
if is_zh(lang) {
"需要改进"
} else {
"Needs improvement"
}
} else if is_zh(lang) {
"高风险"
} else {
"High risk"
}
}
///|
fn readiness_comment(report : ProjectReport, lang : String) -> String {
if report.documentation_coverage >= 90 &&
report.readme.found &&
report.moon_mod.found &&
report.has_generated_mbti {
if is_zh(lang) {
"项目已经具备较好的发布前文档准备度。"
} else {
"The project has strong release-readiness signals."
}
} else if report.documentation_coverage < 40 {
if is_zh(lang) {
"公开 API 文档覆盖率是当前主要短板。"
} else {
"Public API documentation is the main blocker."
}
} else if !report.readme.found || !report.readme.has_usage {
if is_zh(lang) {
"建议优先补充 README 项目介绍和文档入口。"
} else {
"README overview and documentation entry should be improved first."
}
} else if !report.has_generated_mbti {
if is_zh(lang) {
"发布或评审前建议运行 `moon info`。"
} else {
"Run `moon info` before publishing or review."
}
} else if is_zh(lang) {
"项目基本可用,但仍有文档缺口。"
} else {
"The project is usable but still has documentation gaps."
}
}
///|
fn missing_api_files(report : ProjectReport) -> Array[FileCoverage] {
let files : Array[FileCoverage] = []
for item in report.file_coverage {
if item.public_api_missing_docs > 0 {
files.push(item)
}
}
files
}
///|
fn top_missing_files(
report : ProjectReport,
limit : Int,
) -> Array[FileCoverage] {
let files = missing_api_files(report)
files.sort_by((a, b) => {
if a.documentation_coverage != b.documentation_coverage {
a.documentation_coverage.compare(b.documentation_coverage)
} else {
b.public_api_missing_docs.compare(a.public_api_missing_docs)
}
})
if files.length() <= limit {
files
} else {
files[:limit].to_owned()
}
}
///|
fn rule_line() -> String {
"----------------------------------------"
}
///|
fn fix_hint(issue : DocIssue, lang : String) -> String {
if issue.message.contains("Missing documentation for public API") {
if is_zh(lang) {
"在该公开 API 前添加 `///` 文档注释。"
} else {
"Add a leading `///` doc comment before this public API."
}
} else if issue.message.contains("Weak documentation") {
if is_zh(lang) {
"将占位注释改成说明用途或用法的句子。"
} else {
"Replace placeholder docs with a useful purpose or usage sentence."
}
} else if issue.message.contains("README") {
if is_zh(lang) {
"在 README 中补充缺失的项目信息。"
} else {
"Update README with the missing project information."
}
} else if issue.message.contains("moon.mod") {
if is_zh(lang) {
"在 `moon.mod` 中补充缺失元数据。"
} else {
"Add the missing metadata field in `moon.mod`."
}
} else if issue.message.contains("moon info") {
if is_zh(lang) {
"运行 `moon info`,并按需保留接口摘要。"
} else {
"Run `moon info` and keep generated interface summaries when appropriate."
}
} else if is_zh(lang) {
"检查该项并完善项目文档。"
} else {
"Review this item and update the project documentation."
}
}
///|
fn issue_message(issue : DocIssue, lang : String) -> String {
if !is_zh(lang) {
issue.message
} else if issue.message.has_prefix("Missing documentation for public API `") {
match issue.message.strip_prefix("Missing documentation for public API `") {
Some(rest) => "公开 API `" + rest.to_owned()
None => issue.message
}
} else if issue.message.has_prefix("Weak documentation for public API `") {
match issue.message.strip_prefix("Weak documentation for public API `") {
Some(rest) => "公开 API 文档较弱 `" + rest.to_owned()
None => issue.message
}
} else if issue.message == "README file not found" {
"未找到 README 文件"
} else if issue.message == "README is missing usage instructions" {
"README 缺少使用说明"
} else if issue.message == "README is missing project overview" {
"README 缺少项目介绍"
} else if issue.message == "README is missing documentation entry" {
"README 缺少文档入口"
} else if issue.message == "README has no code example" {
"README 缺少代码示例"
} else if issue.message == "README does not mention development commands" {
"README 未提到开发命令"
} else if issue.message == "README does not mention the license" {
"README 未提到许可证"
} else if issue.message == "`moon.mod` file not found" {
"未找到 `moon.mod` 文件"
} else if issue.message.contains("`moon.mod` is missing") {
issue.message.replace(old="`moon.mod` is missing", new="`moon.mod` 缺少")
} else if issue.message == "GitHub Actions workflow not found" {
"未找到 GitHub Actions workflow"
} else if issue.message.has_prefix("CI does not run") {
issue.message.replace(old="CI does not run", new="CI 未运行")
} else if issue.message.has_prefix("Generated interface summary not found") {
"未找到生成的接口摘要;发布或评审前请运行 `moon info`"
} else if issue.message.has_prefix(
"Public API documentation coverage is below configured minimum",
) {
"公开 API 文档覆盖率低于配置的最低要求"
} else {
issue.message
}
}
///|
fn push_summary(
lines : Array[String],
report : ProjectReport,
lang : String,
) -> Unit {
lines.push(
if is_zh(lang) {
"公开 API 文档:"
} else {
"Public API documentation:"
},
)
lines.push(
(if is_zh(lang) { " 总数: " } else { " Total: " }) +
report.public_api_total.to_string(),
)
lines.push(
(if is_zh(lang) { " 已文档化: " } else { " Documented: " }) +
report.public_api_documented.to_string(),
)
lines.push(
(if is_zh(lang) { " 缺失: " } else { " Missing: " }) +
report.public_api_missing_docs.to_string(),
)
lines.push(
(if is_zh(lang) { " 覆盖率: " } else { " Coverage: " }) +
report.documentation_coverage.to_string() +
"%",
)
}
///|
fn push_assessment(
lines : Array[String],
report : ProjectReport,
lang : String,
) -> Unit {
lines.push(if is_zh(lang) { "总体评价:" } else { "Overall assessment:" })
lines.push(
(if is_zh(lang) { " 等级: " } else { " Level: " }) +
readiness_level(report, lang),
)
lines.push(
(if is_zh(lang) { " 说明: " } else { " Comment: " }) +
readiness_comment(report, lang),
)
lines.push(
if is_zh(lang) {
" 摘要: " +
report.public_api_total.to_string() +
" 个公开 API 中有 " +
report.public_api_missing_docs.to_string() +
" 个缺少文档。"
} else {
" Summary: " +
report.public_api_missing_docs.to_string() +
" of " +
report.public_api_total.to_string() +
" public APIs are missing docs."
},
)
}
///|
fn push_top_missing(
lines : Array[String],
report : ProjectReport,
lang : String,
) -> Unit {
let top_files = top_missing_files(report, 5)
if !top_files.is_empty() {
lines.push("")
lines.push(
if is_zh(lang) {
"缺文档重灾区:"
} else {
"Top missing-doc files:"
},
)
for summary in top_files {
lines.push(
" - " +
display_path(report, summary.file) +
": " +
summary.public_api_missing_docs.to_string() +
(if is_zh(lang) { " 个缺失," } else { " missing, " }) +
summary.documentation_coverage.to_string() +
(if is_zh(lang) { "% 覆盖率" } else { "% coverage" }),
)
}
}
}
///|
fn push_file_coverage(
lines : Array[String],
report : ProjectReport,
lang : String,
) -> Unit {
if !report.file_coverage.is_empty() {
lines.push("")
lines.push(if is_zh(lang) { "文件覆盖率:" } else { "File coverage:" })
for summary in report.file_coverage {
lines.push(
" - " +
display_path(report, summary.file) +
": " +
summary.public_api_documented.to_string() +
"/" +
summary.public_api_total.to_string() +
(if is_zh(lang) { " 已文档化 (" } else { " documented (" }) +
summary.documentation_coverage.to_string() +
"%)",
)
}
}
}
///|
fn push_project_signals(
lines : Array[String],
report : ProjectReport,
lang : String,
) -> Unit {
lines.push("")
lines.push(rule_line())
lines.push("")
lines.push("README:")
lines.push(
(if is_zh(lang) { " 找到: " } else { " Found: " }) +
yes_no_lang(report.readme.found, lang),
)
lines.push(
(if is_zh(lang) { " 有项目介绍: " } else { " Has overview: " }) +
yes_no_lang(report.readme.has_overview, lang),
)
lines.push(
(if is_zh(lang) { " 有文档入口: " } else { " Has docs entry: " }) +
yes_no_lang(report.readme.has_usage, lang),
)
lines.push(
(if is_zh(lang) {
" 提到许可证: "
} else {
" Has license mention: "
}) +
yes_no_lang(report.readme.has_license_mention, lang),
)
lines.push("")
lines.push(if is_zh(lang) { "Moon 元数据:" } else { "Moon metadata:" })
lines.push(" moon.mod: " + yes_no_lang(report.moon_mod.found, lang))
lines.push(
" moon.mod.json: " + yes_no_lang(report.moon_mod.legacy_json_found, lang),
)
lines.push(
(if is_zh(lang) { " 有描述: " } else { " Has description: " }) +
yes_no_lang(report.moon_mod.has_description, lang),
)
lines.push(
(if is_zh(lang) { " 有仓库地址: " } else { " Has repository: " }) +
yes_no_lang(report.moon_mod.has_repository, lang),
)
lines.push(
(if is_zh(lang) { " 有许可证: " } else { " Has license: " }) +
yes_no_lang(report.moon_mod.has_license, lang),
)
lines.push("")
lines.push(
if is_zh(lang) {
"生成的接口摘要:"
} else {
"Generated interface:"
},
)
lines.push(
" pkg.generated.mbti: " + yes_no_lang(report.has_generated_mbti, lang),
)
if !report.has_generated_mbti {
lines.push(
if is_zh(lang) {
" 建议: 运行 `moon info`"
} else {
" Suggestion: run `moon info`"
},
)
}
lines.push("")
lines.push(if is_zh(lang) { "示例:" } else { "Examples:" })
lines.push(
(if is_zh(lang) {
" mbt check 代码块: "
} else {
" mbt check blocks: "
}) +
report.markdown.mbt_check_blocks.to_string(),
)
lines.push(
(if is_zh(lang) {
" 普通 MoonBit 代码块: "
} else {
" ordinary MoonBit blocks: "
}) +
report.markdown.moonbit_blocks.to_string(),
)
lines.push("")
lines.push("CI:")
lines.push(
(if is_zh(lang) {
" GitHub Actions 文件数: "
} else {
" GitHub Actions files: "
}) +
report.ci.workflow_files.to_string(),
)
lines.push(" moon check: " + yes_no_lang(report.ci.has_moon_check, lang))
lines.push(" moon test: " + yes_no_lang(report.ci.has_moon_test, lang))
lines.push(
(if is_zh(lang) { " 脚本式验证: " } else { " Script validation: " }) +
yes_no_lang(report.ci.has_script_validation, lang),
)
}
///|
fn push_issues(
lines : Array[String],
report : ProjectReport,
all_issues : Bool,
lang : String,
) -> Unit {
if !report.issues.is_empty() {
lines.push("")
lines.push(rule_line())
lines.push("")
lines.push(if is_zh(lang) { "问题清单:" } else { "Issues:" })
let limit = if all_issues {
report.issues.length()
} else if report.issues.length() > 20 {
20
} else {
report.issues.length()
}
for index in 0.. limit {
lines.push(
" ... " + (report.issues.length() - limit).to_string() + " more issues",
)
lines.push(
if is_zh(lang) {
" 使用 `--all-issues` 或导出 Markdown 查看完整列表。"
} else {
" Run with `--all-issues` or `--format markdown --output DOC_REPORT.md` to view the full list."
},
)
}
}
}
///|
fn next_steps(report : ProjectReport, lang : String) -> Array[String] {
let steps : Array[String] = []
if report.public_api_missing_docs > 0 {
steps.push(
if is_zh(lang) {
"优先给缺文档重灾区中的公开 API 添加 `///` 注释。"
} else {
"Add `///` comments to public APIs in the top missing-doc files first."
},
)
}
if !report.readme.found {
steps.push(
if is_zh(lang) {
"补充 README,让用户先看到项目用途、安装或运行方式。"
} else {
"Add a README that explains the project purpose and how to run it."
},
)
} else if !report.readme.has_usage {
steps.push(
if is_zh(lang) {
"在 README 中补充项目入口、目录跳转或用户指南链接。"
} else {
"Add project entry points, a contents section, or a user guide link to the README."
},
)
}
if report.markdown.moonbit_blocks > 0 && report.markdown.mbt_check_blocks == 0 {
steps.push(
if is_zh(lang) {
"把关键 MoonBit 示例改成 ```mbt check,让示例可以被工具检查。"
} else {
"Convert key MoonBit examples to ```mbt check so they are checked."
},
)
}
if !report.moon_mod.found && !report.moon_mod.legacy_json_found {
steps.push(
if is_zh(lang) {
"补充 `moon.mod`,并填写描述、仓库地址和许可证等元数据。"
} else {
"Add `moon.mod` with description, repository, and license metadata."
},
)
} else if report.moon_mod.legacy_json_found && !report.moon_mod.found {
steps.push(
if is_zh(lang) {
"项目使用旧版 `moon.mod.json`,如准备发布建议迁移或同时说明兼容状态。"
} else {
"The project uses legacy `moon.mod.json`; consider migration or document compatibility."
},
)
} else if !report.moon_mod.has_description ||
!report.moon_mod.has_repository ||
!report.moon_mod.has_license {
steps.push(
if is_zh(lang) {
"完善 Moon 元数据中的描述、仓库地址或许可证字段。"
} else {
"Complete Moon metadata such as description, repository, or license."
},
)
}
if !report.has_generated_mbti {
steps.push(
if is_zh(lang) {
"发布或评审前运行 `moon info`,生成 `pkg.generated.mbti` 接口摘要。"
} else {
"Run `moon info` before release or review to generate `pkg.generated.mbti`."
},
)
}
if report.ci.workflow_files == 0 {
steps.push(
if is_zh(lang) {
"如果项目要持续维护,建议添加 CI 来运行 MoonBit 检查。"
} else {
"If the project will be maintained, add CI for MoonBit checks."
},
)
} else if !report.ci.has_moon_check &&
!report.ci.has_moon_test &&
!report.ci.has_script_validation {
steps.push(
if is_zh(lang) {
"在 CI 中加入 `moon check` 或 `moon test`,或明确调用验证脚本。"
} else {
"Add `moon check`, `moon test`, or an explicit validation script to CI."
},
)
}
if report.issues.length() > 20 {
steps.push(
if is_zh(lang) {
"问题较多时用 `--all-issues` 或 `--format html --output report.html` 查看完整列表。"
} else {
"For large issue lists, use `--all-issues` or `--format html --output report.html`."
},
)
}
if steps.is_empty() {
steps.push(
if is_zh(lang) {
"当前没有明显阻塞项,可以保留报告作为发布或评审记录。"
} else {
"No obvious blockers were found; keep the report as a release or review record."
},
)
}
if steps.length() > 5 {
steps[:5].to_owned()
} else {
steps
}
}
///|
fn push_next_steps(
lines : Array[String],
report : ProjectReport,
lang : String,
) -> Unit {
lines.push("")
lines.push(rule_line())
lines.push("")
lines.push(if is_zh(lang) { "下一步:" } else { "Next steps:" })
for step in next_steps(report, lang) {
lines.push(" - " + step)
}
}
///|
/// Render a project report as terminal-friendly text.
pub fn render_text(
report : ProjectReport,
all_issues? : Bool = false,
lang? : String = "zh",
) -> String {
let lines : Array[String] = []
lines.push(
if is_zh(lang) {
"MoonDocCheck 报告"
} else {
"MoonDocCheck Report"
},
)
lines.push(rule_line())
lines.push("")
lines.push("Project: " + report.project_name)
lines.push(
(if is_zh(lang) { "扫描文件数: " } else { "Files scanned: " }) +
report.files_scanned.to_string(),
)
lines.push(
(if is_zh(lang) {
"MoonBit 源文件数: "
} else {
"MoonBit source files: "
}) +
report.moonbit_source_files.to_string(),
)
lines.push("")
push_summary(lines, report, lang)
lines.push("")
push_assessment(lines, report, lang)
push_top_missing(lines, report, lang)
lines.push("")
lines.push(rule_line())
push_file_coverage(lines, report, lang)
push_project_signals(lines, report, lang)
push_issues(lines, report, all_issues, lang)
push_next_steps(lines, report, lang)
lines.join("\n")
}