
Actions.pudu
Pudu177 lines7.5 KB
1/** @Docgen.Command.Actions — reporting and the commands that create and exchange files */2module PuduLangDocgen.Command.Actions34import Std.Io as Io5import Std.Path as Path6import PuduLangDocgen.Command.Arguments as Arguments7import PuduLangDocgen.Constants.Package as Package8import PuduLangDocgen as Docgen9import PuduLangDocgen.Docset.Tasks as Tasks10import PuduLangDocgen.Scaffold as Scaffold11import PuduLangLog.Configuration as LogConfiguration12import PuduLangLog.Formatting.Compact as Compact13import PuduLangLog as Log14import PuduLangLog.Logger as Logger15import PuduLangLog.Sinks.Console as Console16import PuduLangLog.Sinks.File as File17import PuduLangLog.Sinks.Theme as Theme18import PuduLangLog.Value as Value192021const CONSOLE_TEMPLATE: Str = "\{Message:lj\}\{NewLine\}"222324const DIAGNOSTIC_TEMPLATE: Str = "\{Path\}:\{Line\}: \{Severity\} \{Code\}: \{Text\}"2526272829export fn logger(parsed: &Arguments.Parsed) -> Result[Logger.Logger, Str] {30 let written = Arguments.valueOf(parsed, "--logLevel", if Arguments.has(parsed, "--verbose") { "verbose" } else { "info" })31 let minimum = match levelOf(written) {32 case Some(found) => found33 case None => { return Err("log level must be error, warning, info, verbose, or diagnostic: " + written) }34 }35 var configured = LogConfiguration.create().minimumLevel(minimum).writeTo(Console.sink(Console.Options{..Console.defaults(), outputTemplate: CONSOLE_TEMPLATE, theme: Theme.none(), colored: false, standardErrorFromLevel: Some(Log.Warning)}))36 let file = Arguments.valueOf(parsed, "--log", "")37 if !file.isEmpty() { configured = configured.writeTo(File.sink(File.Options{..File.defaults(file), formatter: Compact.compact()})) }38 Ok(configured.createLogger())39}404142export fn report(log: &Logger.Logger, problems: &Array[Docgen.Diagnostic]) -> () {43 for problem in *problems {44 let values = [Value.text(problem.path), Value.int(problem.line), Value.text(severity(&problem)), Value.text(problem.code), Value.text(problem.message)]45 match problem.severity {46 case Docgen.Error => log.error(DIAGNOSTIC_TEMPLATE, values)47 case Docgen.Warning => log.warning(DIAGNOSTIC_TEMPLATE, values)48 case Docgen.Information => log.information(DIAGNOSTIC_TEMPLATE, values)49 }50 }51}525354export fn say(log: &Logger.Logger, text: Str) -> () { log.information("\{Text\}", [Value.text(text)]) }555657export fn detail(log: &Logger.Logger, text: Str) -> () { log.debug("\{Text\}", [Value.text(text)]) }585960export fn failed(log: &Logger.Logger, problems: &Array[Docgen.Diagnostic]) -> Int {61 report(log, problems)62 let errors = problems.filter(|problem: Docgen.Diagnostic| problem.severity == Docgen.Error).length()63 log.error("Failed with \{Errors\} error(s) and \{Warnings\} warning(s).", [Value.int(errors), Value.int(problems.length() - errors)])64 165}666768export fn initialize(log: &Logger.Logger, parsed: &Arguments.Parsed) -> Int {69 let folder = if parsed.positional.isEmpty() { "." } else { parsed.positional[0] }70 let name = Path.nameOf(if folder == "." { Path.directoryOf(Path.join(folder, "x")) } else { folder })71 var answers = Scaffold.defaults(name)72 if !Arguments.has(parsed, "--yes") {73 answers = Scaffold.Answers{..answers, title: ask("Site title", answers.title)}74 answers = Scaffold.Answers{..answers, api: ask("Document Pudu sources (yes/no)", "yes").toLower().startsWith("y")}75 if answers.api { answers = Scaffold.Answers{..answers, sources: ask("Source folder", answers.sources)} }76 answers = Scaffold.Answers{..answers, pdf: ask("Enable PDF (yes/no)", "no").toLower().startsWith("y")}77 }78 match Tasks.init(folder, &answers) {79 case Ok(written) => {80 for path in written { say(log, "created " + path) }81 say(log, "Run: docgen " + (if folder == "." { "" } else { folder + " " }) + "--serve")82 083 }84 case Err(problems) => failed(log, &problems)85 }86}878889export fn template(log: &Logger.Logger, parsed: &Arguments.Parsed) -> Int {90 let action = if parsed.positional.isEmpty() { "list" } else { parsed.positional[0] }91 if action == "list" {92 for name in Package.BUILT_IN_TEMPLATES { say(log, name) }93 return 094 }95 if action != "export" {96 log.error("template expects list or export", [])97 return 298 }99 let chosen = if parsed.positional.length() > 1 { parsed.positional[1] } else { "default" }100 let names = if Arguments.has(parsed, "--all") { ["default"] } else { [chosen] }101 let root = Arguments.valueOf(parsed, "--output", "_exported_templates")102 for name in names {103 if name != "default" && name != "modern" && name != "statictoc" {104 log.error("template \{Name\} has no files to export", [Value.text(name)])105 return 2106 }107 match Tasks.exportTemplate(Path.join(root, name)) {108 case Ok(written) => { for path in written { detail(log, "wrote " + Path.join(Path.join(root, name), path)) } }109 case Err(problems) => { return failed(log, &problems) }110 }111 say(log, "exported " + name + " to " + Path.join(root, name))112 }113 0114}115116117export fn download(log: &Logger.Logger, parsed: &Arguments.Parsed) -> Int {118 let address = Arguments.valueOf(parsed, "--xref", "")119 if parsed.positional.isEmpty() || address.isEmpty() {120 log.error("download needs a file and --xref <address>", [])121 return 2122 }123 match Tasks.download(address, parsed.positional[0]) {124 case Ok(count) => {125 say(log, "saved " + show(count) + " references to " + parsed.positional[0])126 0127 }128 case Err(problems) => failed(log, &problems)129 }130}131132133export fn merge(log: &Logger.Logger, parsed: &Arguments.Parsed) -> Int {134 let file = Arguments.valueOf(parsed, "--output", "")135 if parsed.positional.isEmpty() || file.isEmpty() {136 log.error("merge needs maps and --output <file>", [])137 return 2138 }139 match Tasks.merge(&parsed.positional, file) {140 case Ok(count) => {141 say(log, "merged " + show(count) + " references into " + file)142 0143 }144 case Err(problems) => failed(log, &problems)145 }146}147148149fn levelOf(name: Str) -> Option[Log.Level] {150 match name {151 case "error" => Some(Log.Error)152 case "warning" => Some(Log.Warning)153 case "info" => Some(Log.Information)154 case "verbose" => Some(Log.Debug)155 case "diagnostic" => Some(Log.Verbose)156 case _ => None157 }158}159160161fn severity(problem: &Docgen.Diagnostic) -> Str {162 match problem.severity {163 case Docgen.Error => "error"164 case Docgen.Warning => "warning"165 case Docgen.Information => "info"166 }167}168169170fn ask(question: Str, fallback: Str) -> Str {171 let _asked = Io.writeLine(question + " [" + fallback + "]: ")172 match Io.readLineOrEnd() {173 case Ok(Some(line)) => if line.trim().isEmpty() { fallback } else { line.trim() }174 case _ => fallback175 }176}177