2024-10-30 15:49:27 -05:00
|
|
|
import std/[os, sequtils, streams, strutils, tables]
|
2024-10-30 14:22:23 -05:00
|
|
|
import yaml, hwylterm
|
|
|
|
|
|
|
|
type
|
2024-10-30 15:49:27 -05:00
|
|
|
Component = Table[string, string]
|
|
|
|
TypstTemplate = object
|
|
|
|
keys: seq[string]
|
|
|
|
src: string
|
2024-10-30 14:22:23 -05:00
|
|
|
Config = object
|
2024-10-30 15:49:27 -05:00
|
|
|
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 =
|
2024-10-30 14:22:23 -05:00
|
|
|
|
|
|
|
var s = newFileStream(configPath)
|
2024-10-30 15:49:27 -05:00
|
|
|
load(s, result)
|
|
|
|
close s
|
2024-10-30 14:22:23 -05:00
|
|
|
|
2024-10-30 15:49:27 -05:00
|
|
|
addDefaultTemplates result
|
|
|
|
checkTemplates result.templates
|
2024-10-30 14:22:23 -05:00
|
|
|
|
2024-10-30 15:49:27 -05:00
|
|
|
proc getTemplate(c: Config, component: Component): TypstTemplate =
|
|
|
|
let k = component.keys().toSeq()
|
|
|
|
for _, templ in c.templates.pairs():
|
|
|
|
if templ.keys == k:
|
|
|
|
return templ
|
2024-10-30 14:22:23 -05:00
|
|
|
|
2024-10-30 15:49:27 -05:00
|
|
|
quit($bb("[red]error[/] failed to find template for component:\n") & $component)
|
2024-10-30 14:22:23 -05:00
|
|
|
|
2024-10-30 15:49:27 -05:00
|
|
|
func toFmtArgs(comp: Component): seq[string] =
|
|
|
|
for k, v in comp.pairs: result.add k; result.add v
|
2024-10-30 14:22:23 -05:00
|
|
|
|
2024-10-30 15:49:27 -05:00
|
|
|
proc applyTemplate(c: Config, component: Component): string =
|
|
|
|
let templ = c.getTemplate(component)
|
|
|
|
result = templ % component.toFmtArgs()
|
2024-10-30 14:22:23 -05:00
|
|
|
|
2024-10-30 15:49:27 -05:00
|
|
|
proc typstGen(c: Config): string =
|
|
|
|
for component in c.components:
|
|
|
|
result &= c.applyTemplate(component)
|
2024-10-30 14:22:23 -05:00
|
|
|
|
|
|
|
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"),
|
2024-10-30 15:49:27 -05:00
|
|
|
("","check", "load config and exit"),
|
2024-10-30 14:22:23 -05:00
|
|
|
]
|
|
|
|
)
|
2024-10-30 15:49:27 -05:00
|
|
|
var
|
|
|
|
p = initOptParser(longNoVal= @["check"])
|
|
|
|
configPath = "typstgen.yml"
|
|
|
|
check: bool
|
2024-10-30 14:22:23 -05:00
|
|
|
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
|
2024-10-30 15:49:27 -05:00
|
|
|
of "check":
|
|
|
|
check = true
|
2024-10-30 14:22:23 -05:00
|
|
|
|
|
|
|
if not fileExists(configPath):
|
2024-10-30 14:25:09 -05:00
|
|
|
quit($bbfmt("file: [b]{configPath}[/] does not exist"))
|
2024-10-30 15:49:27 -05:00
|
|
|
|
|
|
|
let config = loadConfig(configPath)
|
|
|
|
if check: quit 0
|
|
|
|
echo typstGen(config)
|
2024-10-30 14:22:23 -05:00
|
|
|
|
|
|
|
|