// gendocs: a build-time codegen tool for mymoon's bundled diagnostic docs.
//
// Reads the `moonbit-docs` markdown (error codes + attributes) from a directory
// and emits a MoonBit source file defining two `Map[String, String]` (key → raw
// markdown) plus accessors. mymoon declares this as a `bin-dep` and runs it from a
// `pre-build` rule, so the docs are embedded at BUILD time — no committed blob, no
// `include_str!` (which MoonBit lacks). Mirrors rust moon's `include_dir!` of its
// `resources/error_codes/`, but as a declarative bin-dep + codegen.
//
// gendocs
//
// `` must contain `error_codes/E*.md` (key = file stem, e.g.
// "E0001") and `attributes/*.md` (key = file stem, e.g. "alert").
///|
fn main {
let args = @env.args()
if args.length() < 3 {
println("usage: gendocs ")
panic()
}
let base = args[1]
let out = args[2]
generate(base, out) catch {
e => {
println("gendocs: \{e}")
panic()
}
}
}
///|
fn generate(base : String, out : String) -> Unit raise {
let sb = StringBuilder::new()
sb.write_string(
"// Generated by tools/gendocs from moonbit-docs — DO NOT EDIT.\n\n",
)
// Emit ONLY the data maps; the accessors are a hand-written, stable API in the
// consuming `diagdocs` package (so the package is discoverable before this
// generated file exists, and the public surface isn't regenerated).
emit_map(sb, "error_docs", base + "/error_codes")
emit_map(sb, "attr_docs", base + "/attributes")
@fs.write_string_to_file(out, sb.to_string())
}
///|
/// Emit `let : Map[String, String] = { "": , … }`, reading every
/// `*.md` in `dir` (sorted, for deterministic output). Each markdown body is
/// emitted as a raw `#|` multiline literal — verbatim, no escaping, so backticks /
/// quotes / `$` in the docs pass through untouched.
fn emit_map(sb : StringBuilder, name : String, dir : String) -> Unit raise {
let files = @fs.read_dir(dir)
files.sort()
sb.write_string("///|\nlet \{name} : Map[String, String] = {\n")
for fname in files {
guard fname.has_suffix(".md") else { continue }
let key = fname[:fname.length() - 3].to_owned()
let content = @fs.read_file_to_string(dir + "/" + fname)
// Raw multiline literal, parenthesized (a bare `#|` as a map value is the
// deprecated E0027 form; `( … )` is the blessed one), one `#|` per source
// line — verbatim, no escaping.
sb.write_string(" \"\{key}\": (\n")
for line in content.split("\n") {
sb.write_string(" #|")
sb.write_string(line.to_owned())
sb.write_string("\n")
}
sb.write_string(" ),\n")
}
sb.write_string("}\n\n")
}