feat: implement RendererManager and WatcherManager
This commit is contained in:
parent
dbf6ff32ab
commit
0262bc51cd
3
go.mod
Normal file
3
go.mod
Normal file
|
@ -0,0 +1,3 @@
|
|||
module forge.babariviere.com/babariviere/render
|
||||
|
||||
go 1.21.5
|
129
render.go
Normal file
129
render.go
Normal file
|
@ -0,0 +1,129 @@
|
|||
package render
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Manager interface {
|
||||
GetPage(page string, username string) Renderer
|
||||
GetBlock(page string, block string) Renderer
|
||||
FromRequest(r *http.Request, page string, username string) Renderer
|
||||
}
|
||||
|
||||
type RendererManager struct {
|
||||
pages map[string]Renderer
|
||||
}
|
||||
|
||||
func ParseTemplates(f fs.FS) (*RendererManager, error) {
|
||||
rm := RendererManager{
|
||||
pages: make(map[string]Renderer),
|
||||
}
|
||||
|
||||
var components []string
|
||||
err := fs.WalkDir(f, ".", func(path string, d fs.DirEntry, err error) error {
|
||||
if strings.HasPrefix(path, "pages") {
|
||||
return fs.SkipDir
|
||||
}
|
||||
if strings.HasSuffix(path, ".gohtml") {
|
||||
components = append(components, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = fs.WalkDir(f, "pages", func(path string, d fs.DirEntry, err error) error {
|
||||
if strings.HasSuffix(path, ".gohtml") {
|
||||
var cs []string
|
||||
parent := strings.Split(filepath.Dir(path), "/")
|
||||
for _, c := range components {
|
||||
if !strings.HasPrefix(c, "components") {
|
||||
fmt.Println("not component", c)
|
||||
cs = append(cs, c)
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.Count(c, "/") == 1 {
|
||||
fmt.Println("root component", c)
|
||||
cs = append(cs, c)
|
||||
continue
|
||||
}
|
||||
|
||||
parts := strings.Split(filepath.Dir(c), "/")
|
||||
if slices.Equal(parent[1:], parts[1:]) {
|
||||
fmt.Println("same prefix", parent, parts)
|
||||
cs = append(cs, c)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(path, cs)
|
||||
tmpl, err := template.ParseFS(f, append(cs, path)...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name := strings.TrimPrefix(strings.TrimSuffix(path, ".gohtml"), "pages/")
|
||||
rm.pages[name] = Renderer{
|
||||
tmpl: tmpl,
|
||||
}
|
||||
}
|
||||
return err
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &rm, nil
|
||||
}
|
||||
|
||||
func (rm RendererManager) GetPage(page string, username string) Renderer {
|
||||
p := rm.pages[page]
|
||||
p.username = username
|
||||
return p
|
||||
}
|
||||
|
||||
func (rm RendererManager) GetBlock(page string, block string) Renderer {
|
||||
p := rm.pages[page]
|
||||
p.target = block
|
||||
return p
|
||||
}
|
||||
|
||||
func (rm RendererManager) FromRequest(r *http.Request, page string, username string) Renderer {
|
||||
p := rm.pages[page]
|
||||
if r.Header.Get("HX-Boosted") == "true" {
|
||||
p.target = "content"
|
||||
} else if target := r.Header.Get("HX-Target"); target != "" {
|
||||
p.target = target
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
type Renderer struct {
|
||||
tmpl *template.Template
|
||||
target string
|
||||
username string
|
||||
}
|
||||
|
||||
func (r Renderer) Render(w io.Writer, data any) error {
|
||||
if r.target != "" {
|
||||
// TODO: allow client to use it's own template for auth
|
||||
// I use it like this since this is what I do for all my projects for now. (and it's not the best)
|
||||
return r.tmpl.ExecuteTemplate(w, r.target, layoutData{Data: data, Username: r.username})
|
||||
}
|
||||
return r.tmpl.Execute(w, data)
|
||||
}
|
||||
|
||||
type layoutData struct {
|
||||
Username string
|
||||
Data any
|
||||
}
|
57
watcher.go
Normal file
57
watcher.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package render
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var _ Manager = (*WatcherManager)(nil)
|
||||
|
||||
type WatcherManager struct {
|
||||
mu sync.RWMutex
|
||||
dir string
|
||||
manager *RendererManager
|
||||
}
|
||||
|
||||
func NewWatcher(dir string) (*WatcherManager, error) {
|
||||
renderer, err := ParseTemplates(os.DirFS(dir))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &WatcherManager{
|
||||
dir: dir,
|
||||
manager: renderer,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (wm *WatcherManager) Refresh() error {
|
||||
wm.mu.Lock()
|
||||
defer wm.mu.Unlock()
|
||||
|
||||
var err error
|
||||
wm.manager, err = ParseTemplates(os.DirFS(wm.dir))
|
||||
return err
|
||||
}
|
||||
|
||||
func (wm *WatcherManager) GetPage(page string, username string) Renderer {
|
||||
wm.mu.RLock()
|
||||
defer wm.mu.RUnlock()
|
||||
|
||||
return wm.manager.GetPage(page, username)
|
||||
}
|
||||
|
||||
func (wm *WatcherManager) GetBlock(page string, block string) Renderer {
|
||||
wm.mu.RLock()
|
||||
defer wm.mu.RUnlock()
|
||||
|
||||
return wm.manager.GetBlock(page, block)
|
||||
}
|
||||
|
||||
func (wm *WatcherManager) FromRequest(r *http.Request, page string, username string) Renderer {
|
||||
wm.mu.RLock()
|
||||
defer wm.mu.RUnlock()
|
||||
|
||||
return wm.manager.FromRequest(r, page, username)
|
||||
}
|
Loading…
Reference in a new issue