Initial commit
This commit is contained in:
+102
@@ -0,0 +1,102 @@
|
||||
package rndr
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
log "gitlab.com/gdulai/simpleloglvl"
|
||||
)
|
||||
|
||||
// ComponentRenderer handles the associations between templates and models, and renders the template files with the appropriate backing model.
|
||||
type ComponentRenderer struct {
|
||||
templates *template.Template
|
||||
}
|
||||
|
||||
// NewRenderer creates returns the pointer of newly created ComponentRenderer, or an error.
|
||||
func NewRenderer(root string) (*ComponentRenderer, error) {
|
||||
templates, err := loadTemplates(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ComponentRenderer{templates: templates}, nil
|
||||
}
|
||||
|
||||
// Render, renders a template html file with a backing model.
|
||||
// The template file name is derived from the model type via reflection.
|
||||
func (renderer *ComponentRenderer) Render(w http.ResponseWriter, r *http.Request, model any) {
|
||||
requestId := r.Context().Value("requestId")
|
||||
modelTyp := reflect.TypeOf(model)
|
||||
for modelTyp.Kind() == reflect.Pointer {
|
||||
modelTyp = modelTyp.Elem()
|
||||
}
|
||||
|
||||
if modelTyp == nil {
|
||||
log.LogError("Could not determine model type for %s (%s)", model, requestId)
|
||||
return
|
||||
}
|
||||
templateName := toKebabCase(modelTyp.Name()) + ".html"
|
||||
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
err := renderer.templates.ExecuteTemplate(w, templateName, model)
|
||||
|
||||
if err != nil {
|
||||
log.LogError("Error while serving html teplate: %s (%s)", err, requestId)
|
||||
http.Error(w, "Component render error", http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// RenderLayout, renders layout template file, without any backing model
|
||||
func (renderer *ComponentRenderer) RenderLayout(w http.ResponseWriter, r *http.Request, templateName string) {
|
||||
requestId := r.Context().Value("requestId")
|
||||
err := renderer.templates.ExecuteTemplate(w, templateName, nil)
|
||||
|
||||
if err != nil {
|
||||
log.LogError("Error while serving html teplate: %s (%s)", err, requestId)
|
||||
http.Error(w, "Component render error", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func loadTemplates(root string) (*template.Template, error) {
|
||||
var files []string
|
||||
|
||||
err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !d.IsDir() && filepath.Ext(path) == ".html" {
|
||||
files = append(files, path)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return template.ParseFiles(files...)
|
||||
}
|
||||
|
||||
func toKebabCase(s string) string {
|
||||
var b strings.Builder
|
||||
|
||||
for i, r := range s {
|
||||
if i > 0 && unicode.IsUpper(r) {
|
||||
prev := rune(s[i-1])
|
||||
if unicode.IsLower(prev) ||
|
||||
(i+1 < len(s) && unicode.IsLower(rune(s[i+1]))) {
|
||||
b.WriteByte('-')
|
||||
}
|
||||
}
|
||||
|
||||
b.WriteRune(unicode.ToLower(r))
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
Reference in New Issue
Block a user