feat(templates): generalised templating from file contents to any gonf value

This commit is contained in:
Julien Dessaux 2024-02-10 09:34:02 +01:00
parent 325f6e2130
commit c1d2b8912d
Signed by: adyxax
GPG key ID: F92E51B86E07177E
4 changed files with 99 additions and 69 deletions

View file

@ -5,9 +5,7 @@ import (
"crypto/sha256"
"io"
"log/slog"
"net/url"
"os"
"text/template"
)
// ----- Globals ---------------------------------------------------------------
@ -19,26 +17,22 @@ func init() {
}
// ----- Public ----------------------------------------------------------------
func File(filename string, contents []byte) *FilePromise {
return &FilePromise{
chain: nil,
contents: contents,
err: nil,
filename: filename,
status: PROMISED,
templateFunctions: nil,
useTemplate: false,
}
type FilePromise struct {
chain []Promise
contents Value
err error
filename Value
status Status
}
type FilePromise struct {
chain []Promise
contents []byte
err error
filename string
status Status
templateFunctions map[string]any
useTemplate bool
func File(filename Value, contents Value) *FilePromise {
return &FilePromise{
chain: nil,
contents: contents,
err: nil,
filename: filename,
status: PROMISED,
}
}
func (f *FilePromise) IfRepaired(p ...Promise) Promise {
@ -52,34 +46,17 @@ func (f *FilePromise) Promise() Promise {
}
func (f *FilePromise) Resolve() {
if f.useTemplate {
tpl := template.New(f.filename)
tpl.Option("missingkey=error")
tpl.Funcs(builtinTemplateFunctions)
tpl.Funcs(f.templateFunctions)
if ttpl, err := tpl.Parse(string(f.contents)); err != nil {
f.status = BROKEN
slog.Error("file", "filename", f.filename, "status", f.status, "error", f.err)
return
} else {
var buff bytes.Buffer
if err := ttpl.Execute(&buff, 0 /* TODO */); err != nil {
f.status = BROKEN
slog.Error("file", "filename", f.filename, "status", f.status, "error", f.err)
return
}
f.contents = buff.Bytes()
}
}
filename := f.filename.String()
var sumFile []byte
sumFile, f.err = sha256sumOfFile(f.filename)
sumFile, f.err = sha256sumOfFile(filename)
if f.err != nil {
f.status = BROKEN
return
}
sumContents := sha256sum(f.contents)
contents := f.contents.Bytes()
sumContents := sha256sum(contents)
if !bytes.Equal(sumFile, sumContents) {
if f.err = writeFile(f.filename, f.contents); f.err != nil {
if f.err = writeFile(filename, contents); f.err != nil {
f.status = BROKEN
slog.Error("file", "filename", f.filename, "status", f.status, "error", f.err)
return
@ -95,24 +72,7 @@ func (f *FilePromise) Resolve() {
slog.Debug("file", "filename", f.filename, "status", f.status)
}
func Template(filename string, contents []byte) *FilePromise {
f := File(filename, contents)
f.useTemplate = true
return f
}
func TemplateWith(filename string, contents []byte, templateFunctions map[string]any) *FilePromise {
f := Template(filename, contents)
f.templateFunctions = templateFunctions
return f
}
// ----- Internal --------------------------------------------------------------
var builtinTemplateFunctions = map[string]any{
"encodeURIQueryParameter": url.QueryEscape,
"var": getVariable,
}
func resolveFiles() (status Status) {
status = KEPT
for _, f := range files {

45
gonf/templates.go Normal file
View file

@ -0,0 +1,45 @@
package gonf
import (
"bytes"
"log/slog"
"text/template"
)
// ----- Globals ---------------------------------------------------------------
var templates *template.Template
// ----- Init ------------------------------------------------------------------
func init() {
templates = template.New("")
templates.Option("missingkey=error")
templates.Funcs(builtinTemplateFunctions)
}
// ----- Public ----------------------------------------------------------------
type TemplateValue struct {
contents []byte
name string
}
func (t *TemplateValue) Bytes() []byte {
var buff bytes.Buffer
if err := templates.ExecuteTemplate(&buff, t.name, nil /* no data needed */); err != nil {
slog.Error("template", "step", "ExecuteTemplate", "name", t.name, "error", err)
return nil
}
return buff.Bytes()
}
func (t TemplateValue) String() string {
return string(t.Bytes()[:])
}
func Template(name string, contents []byte) *TemplateValue {
tpl := templates.New(name)
if _, err := tpl.Parse(string(contents)); err != nil {
slog.Error("template", "step", "Parse", "name", name, "error", err)
return nil
}
return &TemplateValue{contents, name}
}

View file

@ -1,6 +1,13 @@
package gonf
import "crypto/sha256"
import (
"crypto/sha256"
)
var builtinTemplateFunctions = map[string]any{
//"encodeURIQueryParameter": url.QueryEscape,
"var": getVariable,
}
func sha256sum(contents []byte) []byte {
h := sha256.New()

View file

@ -1,23 +1,41 @@
package gonf
type Value interface {
Equals(Value) bool
Bytes() []byte
String() string
}
// ----- String variables ------------------------------------------------------
// ----- BytesValue -----------------------------------------------------------------
type BytesValue struct {
value []byte
}
func (b BytesValue) Bytes() []byte {
return b.value
}
func (b BytesValue) String() string {
return string(b.value[:])
}
func Bytes(value []byte) *BytesValue {
return &BytesValue{value}
}
// ----- StringValue ----------------------------------------------------------------
type StringValue struct {
Value string
value string
}
func (s StringValue) Equals(v Value) bool {
sv, ok := v.(StringValue)
return ok && s.Value == sv.Value
func (s StringValue) Bytes() []byte {
return []byte(s.value)
}
func (s StringValue) String() string {
// TODO handle interpolation
return s.Value
return s.value
}
func String(value string) *StringValue {
return &StringValue{value}
}
// TODO lists