blob: 911b9b866d87a6ca15581396b7f175b1a8707dde [file] [log] [blame]
gio5e49bb62024-07-20 10:43:19 +04001package welcome
2
3import (
gio7fbd4ad2024-08-27 10:06:39 +04004 "bytes"
gio5e49bb62024-07-20 10:43:19 +04005 "fmt"
gio5e49bb62024-07-20 10:43:19 +04006 "io/fs"
giob54db242024-07-30 18:49:33 +04007 "sort"
gio5e49bb62024-07-20 10:43:19 +04008 "strings"
9 "text/template"
10
11 "github.com/giolekva/pcloud/core/installer"
gio5e49bb62024-07-20 10:43:19 +040012)
13
14const tmplSuffix = ".gotmpl"
15
16type AppTmplStore interface {
gio8fae3af2024-07-25 13:43:31 +040017 Types() []string
gio5e49bb62024-07-20 10:43:19 +040018 Find(appType string) (AppTmpl, error)
19}
20
21type appTmplStoreFS struct {
22 tmpls map[string]AppTmpl
23}
24
25func NewAppTmplStoreFS(fsys fs.FS) (AppTmplStore, error) {
26 entries, err := fs.ReadDir(fsys, ".")
27 if err != nil {
28 return nil, err
29 }
30 apps := map[string]AppTmpl{}
31 for _, e := range entries {
32 if !e.IsDir() {
33 continue
34 }
35 app, err := NewAppTmplFS(fsys, e.Name())
36 if err != nil {
37 return nil, err
38 }
39 apps[e.Name()] = app
40 }
41 return &appTmplStoreFS{apps}, nil
42}
43
gio8fae3af2024-07-25 13:43:31 +040044func (s *appTmplStoreFS) Types() []string {
45 var ret []string
46 for t := range s.tmpls {
47 ret = append(ret, t)
48 }
giob54db242024-07-30 18:49:33 +040049 sort.Slice(ret, func(i, j int) bool {
50 a := strings.SplitN(ret[i], ":", 2)
51 b := strings.SplitN(ret[j], ":", 2)
52 langCmp := strings.Compare(a[0], b[0])
53 if langCmp != 0 {
54 return langCmp < 0
55 }
56 // TODO(gio): compare semver?
57 return strings.Compare(a[1], b[1]) > 0
58 })
gio8fae3af2024-07-25 13:43:31 +040059 return ret
60}
61
gio5e49bb62024-07-20 10:43:19 +040062func (s *appTmplStoreFS) Find(appType string) (AppTmpl, error) {
63 if app, ok := s.tmpls[appType]; ok {
64 return app, nil
65 } else {
66 return nil, fmt.Errorf("not found")
67 }
68}
69
70type AppTmpl interface {
gioc81a8472024-09-24 13:06:19 +020071 Render(schemaAddr string, network installer.Network, subdomain string) (map[string][]byte, error)
gio5e49bb62024-07-20 10:43:19 +040072}
73
74type appTmplFS struct {
75 files map[string][]byte
76 tmpls map[string]*template.Template
77}
78
79func NewAppTmplFS(fsys fs.FS, root string) (AppTmpl, error) {
80 files := map[string][]byte{}
81 tmpls := map[string]*template.Template{}
82 if err := fs.WalkDir(fsys, root, func(path string, d fs.DirEntry, err error) error {
83 if err != nil {
84 return err
85 }
86 if d.IsDir() {
87 return nil
88 }
89 contents, err := fs.ReadFile(fsys, path)
90 if err != nil {
91 return err
92 }
93 p, _ := strings.CutPrefix(path, root)
94 if !strings.HasSuffix(p, tmplSuffix) {
95 files[p] = contents
96 return nil
97 }
98 tmpl, err := template.New(path).Parse(string(contents))
99 if err != nil {
100 return err
101 }
102 np, _ := strings.CutSuffix(p, tmplSuffix)
103 tmpls[np] = tmpl
104 return nil
105 }); err != nil {
106 return nil, err
107 }
108 return &appTmplFS{files, tmpls}, nil
109}
110
gioc81a8472024-09-24 13:06:19 +0200111func (a *appTmplFS) Render(schemaAddr string, network installer.Network, subdomain string) (map[string][]byte, error) {
gio7fbd4ad2024-08-27 10:06:39 +0400112 ret := map[string][]byte{}
113 for path, contents := range a.files {
114 ret[path] = contents
115 }
gio5e49bb62024-07-20 10:43:19 +0400116 for path, tmpl := range a.tmpls {
gio7fbd4ad2024-08-27 10:06:39 +0400117 var buf bytes.Buffer
118 if err := tmpl.Execute(&buf, map[string]any{
gioc81a8472024-09-24 13:06:19 +0200119 "SchemaAddr": schemaAddr,
120 "Network": network,
121 "Subdomain": subdomain,
gio5e49bb62024-07-20 10:43:19 +0400122 }); err != nil {
gio7fbd4ad2024-08-27 10:06:39 +0400123 return nil, err
gio5e49bb62024-07-20 10:43:19 +0400124 }
gio7fbd4ad2024-08-27 10:06:39 +0400125 ret[path] = buf.Bytes()
gio5e49bb62024-07-20 10:43:19 +0400126 }
gio7fbd4ad2024-08-27 10:06:39 +0400127 return ret, nil
gio5e49bb62024-07-20 10:43:19 +0400128}