DodoApp: Use picocss for UI
Change-Id: I2d610c4f57e4dfbbe566a7c7f82147443e0106f1
diff --git a/core/installer/welcome/app_tmpl.go b/core/installer/welcome/app_tmpl.go
new file mode 100644
index 0000000..252d431
--- /dev/null
+++ b/core/installer/welcome/app_tmpl.go
@@ -0,0 +1,117 @@
+package welcome
+
+import (
+ "fmt"
+ "io"
+ "io/fs"
+ "strings"
+ "text/template"
+
+ "github.com/giolekva/pcloud/core/installer"
+ "github.com/giolekva/pcloud/core/installer/soft"
+)
+
+const tmplSuffix = ".gotmpl"
+
+type AppTmplStore interface {
+ Find(appType string) (AppTmpl, error)
+}
+
+type appTmplStoreFS struct {
+ tmpls map[string]AppTmpl
+}
+
+func NewAppTmplStoreFS(fsys fs.FS) (AppTmplStore, error) {
+ entries, err := fs.ReadDir(fsys, ".")
+ if err != nil {
+ return nil, err
+ }
+ apps := map[string]AppTmpl{}
+ for _, e := range entries {
+ if !e.IsDir() {
+ continue
+ }
+ app, err := NewAppTmplFS(fsys, e.Name())
+ if err != nil {
+ return nil, err
+ }
+ apps[e.Name()] = app
+ }
+ return &appTmplStoreFS{apps}, nil
+}
+
+func (s *appTmplStoreFS) Find(appType string) (AppTmpl, error) {
+ if app, ok := s.tmpls[appType]; ok {
+ return app, nil
+ } else {
+ return nil, fmt.Errorf("not found")
+ }
+}
+
+type AppTmpl interface {
+ Render(network installer.Network, subdomain string, out soft.RepoFS) error
+}
+
+type appTmplFS struct {
+ files map[string][]byte
+ tmpls map[string]*template.Template
+}
+
+func NewAppTmplFS(fsys fs.FS, root string) (AppTmpl, error) {
+ files := map[string][]byte{}
+ tmpls := map[string]*template.Template{}
+ if err := fs.WalkDir(fsys, root, func(path string, d fs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+ if d.IsDir() {
+ return nil
+ }
+ contents, err := fs.ReadFile(fsys, path)
+ if err != nil {
+ return err
+ }
+ p, _ := strings.CutPrefix(path, root)
+ if !strings.HasSuffix(p, tmplSuffix) {
+ files[p] = contents
+ return nil
+ }
+ tmpl, err := template.New(path).Parse(string(contents))
+ if err != nil {
+ return err
+ }
+ np, _ := strings.CutSuffix(p, tmplSuffix)
+ tmpls[np] = tmpl
+ return nil
+ }); err != nil {
+ return nil, err
+ }
+ return &appTmplFS{files, tmpls}, nil
+}
+
+func (a *appTmplFS) Render(network installer.Network, subdomain string, out soft.RepoFS) error {
+ for path, tmpl := range a.tmpls {
+ f, err := out.Writer(path)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ if err := tmpl.Execute(f, map[string]any{
+ "Network": network,
+ "Subdomain": subdomain,
+ }); err != nil {
+ return err
+ }
+ }
+ for path, contents := range a.files {
+ f, err := out.Writer(path)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ if _, err := io.WriteString(f, string(contents)); err != nil {
+ return err
+ }
+ }
+ return nil
+}