Pudu programming language
Menu
Package

@chrismichaelps / pudu-lang-docgen

Documentation publishing for Pudu: articles, API references, navigation, search, and static websites

0.1.0Apache-2.01

InstallClose

Arguments.pudu

Pudu146 lines6.6 KB

GitHub ↗
1/** @Docgen.Command.Arguments — command lines read into commands, paths, and options */2module PuduLangDocgen.Command.Arguments34import Std.Path as Path5import PuduLangDocgen.Constants.Package as Package67/** @Docgen.Command.Parsed — a command, its positional arguments, and its options */8export type Parsed = { command: Str, positional: Array[Str], flags: Array[Str], values: Array[(Str, Str)] }910/// Options that take a value, with their short forms.11const VALUED: Array[(Str, Str)] = [12  ("--output", "-o"), ("--metadata", "-m"), ("--xref", "-x"), ("--template", "-t"), ("--theme", ""), ("--port", "-p"),13  ("--hostname", "-n"), ("--log", "-l"), ("--logLevel", ""), ("--open-file", "")14]1516/// Options that stand alone, with their short forms.17const FLAGS: Array[(Str, Str)] = [18  ("--force", ""), ("--dryRun", ""), ("--warningsAsErrors", ""), ("--disableGitFeatures", ""), ("--exportRawModel", ""),19  ("--exportViewModel", ""), ("--serve", "-s"), ("--watch", "-w"), ("--open-browser", ""), ("--yes", "-y"), ("--all", "-a"),20  ("--verbose", ""), ("--help", "-h")21]2223/// Command words.24const COMMAND_WORDS: Array[Str] = ["build", "metadata", "serve", "init", "pdf", "download", "merge", "template", "version", "help"]2526/// Commands and what they do, for help text.27const COMMANDS: Array[(Str, Str)] = [28  ("[config]", "Build the site, API reference included, and PDFs when a table enables them"),29  ("build [config]", "Build the site"),30  ("metadata [config]", "Write API metadata files for the configured sources"),31  ("pdf [config]", "Build the site and a PDF for every table of contents that sets pdf"),32  ("serve [folder]", "Preview a built site over HTTP"),33  ("init [folder]", "Create a new documentation project"),34  ("template list | export [name]", "List the built-in templates or copy one for customizing"),35  ("download <file> --xref <address>", "Save a cross-reference map from the web"),36  ("merge <maps...> -o <file>", "Combine cross-reference maps into one"),37  ("version", "Print the package version"),38  ("help", "Print this help")39]4041/// Option help lines.42const OPTIONS: Array[Str] = [43  "  -o, --output <folder>        Output folder instead of the configured one",44  "  -m, --metadata <key=value>   Global metadata; text, or true/false",45  "  -x, --xref <map>             Another cross-reference map address or path",46  "  -t, --template <folder>      Another template folder",47  "      --theme <folder>         Another template folder, applied after templates",48  "      --force                  Rewrite every output file",49  "      --dryRun                 Validate and plan without writing",50  "      --warningsAsErrors       Fail on any warning",51  "      --disableGitFeatures     Skip dates and edit links from git",52  "      --exportRawModel         Write each page's data model beside it",53  "      --exportViewModel        Write each page's view model beside it",54  "  -l, --log <file>             Save every diagnostic as JSON",55  "      --logLevel <level>       Print error, warning, info, verbose, or diagnostic messages",56  "      --verbose                Print verbose messages and the files written",57  "  -s, --serve                  Preview the site after building",58  "  -w, --watch                  Rebuild while serving when sources change",59  "  -n, --hostname <host>        Preview host (localhost)",60  "  -p, --port <number>          Preview port (8080)",61  "      --open-browser           Open the preview in the default browser",62  "      --open-file <path>       Open this site path in the browser",63  "  -y, --yes                    Accept every default when creating a project",64  "  -a, --all                    Export every built-in template",65  "  -h, --help                   Print this help"66]6768/// Command-line arguments read into a command, positional arguments, and options.69export fn parse(arguments: &Array[Str]) -> Result[Parsed, Str] {70  var parsed = Parsed{command: "", positional: [], flags: [], values: []}71  var index = 072  if !arguments.isEmpty() && COMMAND_WORDS.contains(arguments[0]) {73    parsed = Parsed{..parsed, command: arguments[0]}74    index = 175  }76  while index < arguments.length() {77    let argument = arguments[index]78    let valued = longName(&VALUED, argument)79    let flag = longName(&FLAGS, argument)80    if !valued.isEmpty() {81      if index + 1 >= arguments.length() { return Err("option " + argument + " needs a value") }82      parsed = Parsed{..parsed, values: parsed.values.push((valued, arguments[index + 1]))}83      index = index + 284    } else if !flag.isEmpty() {85      parsed = Parsed{..parsed, flags: parsed.flags.push(flag)}86      index = index + 187    } else if argument.startsWith("-") {88      return Err("unknown option " + argument)89    } else {90      parsed = Parsed{..parsed, positional: parsed.positional.push(argument)}91      index = index + 192    }93  }94  let most = if parsed.command == "merge" { 1000 } else if parsed.command == "template" { 2 } else { 1 }95  if parsed.positional.length() > most { return Err("expected at most " + show(most) + " path" + (if most == 1 { "" } else { "s" }) + ", found " + parsed.positional.join(" ")) }96  Ok(parsed)97}9899/// Help text listing commands and options.100export fn usage() -> Str {101  var lines = ["Usage: docgen [command] [path] [options]", "", "Commands:"]102  for (name, description) in COMMANDS { lines = lines.push("  " + pad(name, 34) + description) }103  lines.concat(["", "Options:"]).concat(OPTIONS).join("\n")104}105106/// Whether a flag was given.107export fn has(parsed: &Parsed, flag: Str) -> Bool { parsed.flags.contains(flag) }108109/// The last value given for an option, or the fallback.110export fn valueOf(parsed: &Parsed, name: Str, fallback: Str) -> Str {111  var found = fallback112  for (key, value) in parsed.values {113    if key == name { found = value }114  }115  found116}117118/// Every value given for an option, in order.119export fn valuesOf(parsed: &Parsed, name: Str) -> Array[Str] {120  var found: Array[Str] = []121  for (key, value) in parsed.values {122    if key == name { found = found.push(value) }123  }124  found125}126127/// A configuration path; a folder names its `docgen.json`.128export fn configPath(written: Str) -> Str {129  if written.endsWith(".json") || written.endsWith(".yml") || written.endsWith(".yaml") { written } else { Path.join(written, Package.CONFIG_FILE) }130}131132/// The long name of an option in a table, or the empty text when it is not listed.133fn longName(table: &Array[(Str, Str)], written: Str) -> Str {134  for (long, short) in *table {135    if written == long || (!short.isEmpty() && written == short) { return long }136  }137  ""138}139140/// Text padded with spaces to a width.141fn pad(text: Str, width: Int) -> Str {142  var pieces = [text]143  for _step in text.length()..width { pieces = pieces.push(" ") }144  pieces.join("") + " "145}146