
Checks.pudu
Pudu81 lines3.4 KB
1/** @Docgen.Build.Checks — whole-site validation of fragments, outputs, and diagnostic levels */2module PuduLangDocgen.Build.Checks34import Std.Map as Map5import PuduLangDocgen.Constants.Codes as Codes6import PuduLangDocgen as Docgen7import PuduLangDocgen.Paths as Paths891011export fn fragments(pages: &Array[Docgen.Page]) -> Array[Docgen.Diagnostic] {12 var ids: Map[Str, Array[Str]] = mapOf([])13 for page in *pages { ids = Map.insert(&ids, page.path, anchors(page.body)) }14 var problems: Array[Docgen.Diagnostic] = []15 for page in *pages {16 for link in page.links {17 let parts = link.target.split("#")18 if parts.length() < 2 || parts[1].isEmpty() { continue }19 match Map.get(&ids, parts[0]) {20 case Some(found) => {21 if !found.contains(parts[1]) { problems = problems.push(Docgen.warning(Codes.FRAGMENT_MISSING, page.source, link.line, "no section " + parts[1] + " in " + parts[0])) }22 }23 case None => {}24 }25 }26 }27 problems28}293031export fn anchors(html: Str) -> Array[Str] {32 var result: Array[Str] = []33 var rest = html34 var at = rest.indexOf(" id=\"")35 while at >= 0 {36 rest = rest.drop(at + 5)37 let end = rest.indexOf("\"")38 if end < 0 { break }39 result = result.push(rest.take(end).replace("&", "&").replace(""", "\"").replace("'", "'").replace("<", "<").replace(">", ">"))40 rest = rest.drop(end)41 at = rest.indexOf(" id=\"")42 }43 result44}454647export fn outputs(paths: &Array[Str]) -> Array[Docgen.Diagnostic] {48 var problems: Array[Docgen.Diagnostic] = []49 var seen: Map[Str, Bool] = mapOf([])50 var folders: Map[Str, Bool] = mapOf([])51 for path in *paths {52 let lower = path.toLower()53 if !Paths.relative(path) { problems = problems.push(Docgen.error(Codes.OUTPUT_INVALID, path, 1, "output path is not portable")) }54 if Map.containsKey(&seen, lower) { problems = problems.push(Docgen.error(Codes.OUTPUT_COLLISION, path, 1, "two outputs share this path")) }55 seen = Map.insert(&seen, lower, true)56 let parts = lower.split("/")57 for depth in 1..parts.length() { folders = Map.insert(&folders, parts.slice(0, depth).join("/"), true) }58 }59 for path in *paths {60 if Map.containsKey(&folders, path.toLower()) { problems = problems.push(Docgen.error(Codes.OUTPUT_COLLISION, path, 1, "an output file has the path of an output folder")) }61 }62 problems63}64656667export fn ruled(diagnostics: &Array[Docgen.Diagnostic], rules: &Array[(Str, Str)], strict: Bool) -> Array[Docgen.Diagnostic] {68 var result: Array[Docgen.Diagnostic] = []69 for held in *diagnostics {70 var level = ""71 for (code, chosen) in *rules {72 if code == held.code { level = chosen }73 }74 if level == "off" { continue }75 var severity = if level == "error" { Docgen.Error } else if level == "warning" { Docgen.Warning } else if level == "info" { Docgen.Information } else { held.severity }76 if strict && severity == Docgen.Warning { severity = Docgen.Error }77 result = result.push(Docgen.Diagnostic{..held, severity: severity})78 }79 result80}81