summaryrefslogtreecommitdiffhomepage
path: root/cmd/exttmpl
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/exttmpl')
-rw-r--r--cmd/exttmpl/main.go200
1 files changed, 200 insertions, 0 deletions
diff --git a/cmd/exttmpl/main.go b/cmd/exttmpl/main.go
new file mode 100644
index 0000000..ca7c15f
--- /dev/null
+++ b/cmd/exttmpl/main.go
@@ -0,0 +1,200 @@
+package main
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "slices"
+ "text/template/parse"
+
+ "golang.org/x/text/language"
+ "golang.org/x/text/message/pipeline"
+ "golang.org/x/tools/go/packages"
+)
+
+const (
+ pkgbase = "git.thomasvoss.com/euro-cash.eu"
+ srclang = "en"
+ srcdir = "./src"
+ transdir = srcdir + "/rosetta"
+ outfile = "catalog.gen.go"
+ transfn = "T"
+)
+
+func main() {
+ /* cd to the project root directory */
+ try(os.Chdir(filepath.Dir(os.Args[0])))
+
+ pkgnames := packageList(".")
+
+ var paths []string
+ pkgs := try2(packages.Load(&packages.Config{
+ Mode: packages.NeedFiles | packages.NeedEmbedFiles,
+ }, pkgnames...))
+
+ for _, pkg := range pkgs {
+ if len(pkg.Errors) != 0 {
+ for _, err := range pkg.Errors {
+ warn(err.Msg)
+ }
+ os.Exit(1)
+ }
+ for _, f := range pkg.EmbedFiles {
+ if filepath.Ext(f) == ".tmpl" {
+ paths = append(paths, f)
+ }
+ }
+ }
+
+ msgs := make([]pipeline.Message, 0, 1024)
+ for _, path := range paths {
+ f := try2(os.ReadFile(path))
+ trees := make(map[string]*parse.Tree)
+ t := parse.New("name")
+ t.Mode |= parse.SkipFuncCheck
+ try2(t.Parse(string(f), "", "", trees))
+ for _, t := range trees {
+ process(&msgs, t.Root)
+ }
+ }
+
+ pconf := &pipeline.Config{
+ Supported: languages(),
+ SourceLanguage: language.Make(srclang),
+ Packages: pkgnames,
+ Dir: transdir,
+ GenFile: outfile,
+ GenPackage: srcdir,
+ }
+
+ state := try2(pipeline.Extract(pconf))
+ state.Extracted.Messages = append(state.Extracted.Messages, msgs...)
+
+ try(state.Import())
+ try(state.Merge())
+ try(state.Export())
+ try(state.Generate())
+}
+
+func process(tmplMsgs *[]pipeline.Message, node parse.Node) {
+ switch node.Type() {
+ case parse.NodeList:
+ if ln, ok := node.(*parse.ListNode); ok {
+ for _, n := range ln.Nodes {
+ process(tmplMsgs, n)
+ }
+ }
+ case parse.NodeIf:
+ if in, ok := node.(*parse.IfNode); ok {
+ process(tmplMsgs, in.List)
+ if in.ElseList != nil {
+ process(tmplMsgs, in.ElseList)
+ }
+ }
+ case parse.NodeWith:
+ if wn, ok := node.(*parse.WithNode); ok {
+ process(tmplMsgs, wn.List)
+ if wn.ElseList != nil {
+ process(tmplMsgs, wn.ElseList)
+ }
+ }
+ case parse.NodeRange:
+ if rn, ok := node.(*parse.RangeNode); ok {
+ process(tmplMsgs, rn.List)
+ if rn.ElseList != nil {
+ process(tmplMsgs, rn.ElseList)
+ }
+ }
+ case parse.NodeAction:
+ an, ok := node.(*parse.ActionNode)
+ if !ok {
+ break
+ }
+
+ for _, cmd := range an.Pipe.Cmds {
+ if !hasIndent(cmd, transfn) {
+ continue
+ }
+ for _, arg := range cmd.Args {
+ if arg.Type() != parse.NodeString {
+ continue
+ }
+ if sn, ok := arg.(*parse.StringNode); ok {
+ *tmplMsgs = append(*tmplMsgs, pipeline.Message{
+ ID: pipeline.IDList{sn.Text},
+ Key: sn.Text,
+ Message: pipeline.Text{
+ Msg: sn.Text,
+ },
+ })
+ }
+ }
+ }
+ }
+}
+
+func hasIndent(cmd *parse.CommandNode, s string) bool {
+ if len(cmd.Args) == 0 {
+ return false
+ }
+ arg := cmd.Args[0]
+ var idents []string
+ switch arg.Type() {
+ case parse.NodeField:
+ idents = arg.(*parse.FieldNode).Ident
+ case parse.NodeVariable:
+ idents = arg.(*parse.VariableNode).Ident
+ }
+ return slices.Contains(idents, s)
+}
+
+func packageList(path string) []string {
+ ents := try2(os.ReadDir(path))
+ xs := make([]string, 0, len(ents))
+ foundOne := false
+ for _, ent := range ents {
+ switch {
+ case filepath.Ext(ent.Name()) == ".go":
+ if !foundOne {
+ xs = append(xs, pkgbase+"/"+path)
+ foundOne = true
+ }
+ case !ent.IsDir(), ent.Name() == "cmd", ent.Name() == "vendor":
+ continue
+ default:
+ xs = append(xs, packageList(path+"/"+ent.Name())...)
+ }
+ }
+ return xs
+}
+
+func languages() []language.Tag {
+ ents := try2(os.ReadDir(transdir))
+ tags := make([]language.Tag, len(ents))
+ for i, e := range ents {
+ tags[i] = language.MustParse(e.Name())
+ }
+ return tags
+}
+
+func try(err error) {
+ if err != nil {
+ die(err)
+ }
+}
+
+func try2[T any](val T, err error) T {
+ if err != nil {
+ die(err)
+ }
+ return val
+}
+
+func warn(err any) {
+ fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), err)
+}
+
+func die(err any) {
+ warn(err)
+ os.Exit(1)
+}