import std/[os, sequtils, streams, strutils, tables] import yaml, hwylterm type Component = Table[string, string] TypstTemplate = object keys: seq[string] src: string Config = object components {.defaultVal: @[]}: seq[Component] templates {.defaultVal: initTable[string, TypstTemplate]()}: Table[string, TypstTemplate] func `%`(t: TypstTemplate, a: openArray[string]): string = t.src % a proc checkTemplates(templates: Table[string, TypstTemplate]) = ## quit if any templates have the same keys var keyTable: Table[string, seq[string]] for name, templ in templates.pairs(): let k = templ.keys.join(";") if k in keyTable: keyTable[k].add name else: keyTable[k] = @[name] for keys, names in keyTable.pairs(): if names.len > 1: quit("non-unique key combos in templates: " & names.join(",")) proc addDefaultTemplates(c: var Config) = # TODO: more default templates? if "raw" notin c.templates: c.templates["raw"] = TypstTemplate(keys: @["raw"], src: "$raw") if "image" notin c.templates: c.templates["image"] = TypstTemplate(keys: @["image"], src: """ #figure( image("$image"), caption: [$image] ) """ ) if "figure" notin c.templates: c.templates["figure"] = TypstTemplate(keys: @["image", "caption"], src: """ #align(center)[ #figure( image("$image"), caption: [ $image ] ) $caption ] """ ) proc loadConfig(configPath: string): Config = var s = newFileStream(configPath) load(s, result) close s addDefaultTemplates result checkTemplates result.templates proc getTemplate(c: Config, component: Component): TypstTemplate = let k = component.keys().toSeq() for _, templ in c.templates.pairs(): if templ.keys == k: return templ quit($bb("[red]error[/] failed to find template for component:\n") & $component) func toFmtArgs(comp: Component): seq[string] = for k, v in comp.pairs: result.add k; result.add v proc applyTemplate(c: Config, component: Component): string = let templ = c.getTemplate(component) result = templ % component.toFmtArgs() proc typstGen(c: Config): string = for component in c.components: result &= c.applyTemplate(component) when isMainModule: import hwylterm/[cli, parseopt3] proc writeHelp() = echo newHwylCli( "[bold]typstgen[/] [[[faint]-h[/]]", """ typstgen --config typstgen.yml""", [ ("h", "help", "show this help"), ("c","config", "path to config file"), ("","check", "load config and exit"), ] ) var p = initOptParser(longNoVal= @["check"]) configPath = "typstgen.yml" check: bool for kind, key, val in p.getopt(): case kind of cmdError: quit($bb"[red]cli error[/]: " & p.message) of cmdEnd: assert false of cmdArgument: quit($bb"[red] unexpected argument[/]: " & key) of cmdShortOption, cmdLongOption: case key of "help", "h": writeHelp(); quit 0 of "config", "c": configPath = val of "check": check = true if not fileExists(configPath): quit($bbfmt("file: [b]{configPath}[/] does not exist")) let config = loadConfig(configPath) if check: quit 0 echo typstGen(config)