diff --git a/namespace.go b/namespace.go index 68e458b..4a58bb0 100644 --- a/namespace.go +++ b/namespace.go @@ -26,7 +26,7 @@ import ( // Namespace represents a collection of templates that can include each other type Namespace struct { mu sync.Mutex - tmpls map[string]*Template + tmpls map[string]Template vars map[string]reflect.Value tags map[string]Tag } @@ -34,7 +34,7 @@ type Namespace struct { // New returns a new template namespace func New() *Namespace { return &Namespace{ - tmpls: map[string]*Template{}, + tmpls: map[string]Template{}, vars: map[string]reflect.Value{}, tags: map[string]Tag{}, } @@ -72,7 +72,7 @@ func (n *Namespace) WithTagMap(m map[string]Tag) *Namespace { // GetTemplate tries to get a template from the namespace's template map. // If it finds the template, it returns the template and true. If it // doesn't find it, it returns nil and false. -func (n *Namespace) GetTemplate(name string) (*Template, bool) { +func (n *Namespace) GetTemplate(name string) (Template, bool) { n.mu.Lock() defer n.mu.Unlock() t, ok := n.tmpls[name] diff --git a/parse.go b/parse.go index b8e83f2..57eecf3 100644 --- a/parse.go +++ b/parse.go @@ -26,6 +26,7 @@ import ( "path/filepath" "reflect" "strings" + "sync" "go.elara.ws/salix/ast" "go.elara.ws/salix/parser" @@ -39,23 +40,24 @@ type NamedReader interface { // Parse parses a salix template from a NamedReader, which is an io.Reader // with a Name method that returns a string. os.File implements NamedReader. -func (n *Namespace) Parse(r NamedReader) (*Template, error) { +func (n *Namespace) Parse(r NamedReader) (Template, error) { return n.ParseWithName(r.Name(), r) } // ParseWithFilename parses a salix template from r, using the given name. -func (n *Namespace) ParseWithName(name string, r io.Reader) (*Template, error) { +func (n *Namespace) ParseWithName(name string, r io.Reader) (Template, error) { astVal, err := parser.ParseReader(name, r) if err != nil { - return nil, err + return Template{}, err } - t := &Template{ - ns: n, - name: name, - ast: astVal.([]ast.Node), - tags: map[string]Tag{}, - vars: map[string]reflect.Value{}, + t := Template{ + ns: n, + name: name, + ast: astVal.([]ast.Node), + tags: map[string]Tag{}, + vars: map[string]reflect.Value{}, + macroMtx: &sync.Mutex{}, } performWhitespaceMutations(t.ast) @@ -67,10 +69,10 @@ func (n *Namespace) ParseWithName(name string, r io.Reader) (*Template, error) { } // ParseFile parses the file at path as a salix template. It uses the path as the name. -func (t *Namespace) ParseFile(path string) (*Template, error) { +func (t *Namespace) ParseFile(path string) (Template, error) { fl, err := os.Open(path) if err != nil { - return nil, err + return Template{}, err } defer fl.Close() return t.Parse(fl) @@ -95,10 +97,10 @@ func (t *Namespace) ParseGlob(glob string) error { } // ParseFile parses a file at the given path in a filesystem. It uses the path as the name. -func (t *Namespace) ParseFS(fsys fs.FS, path string) (*Template, error) { +func (t *Namespace) ParseFS(fsys fs.FS, path string) (Template, error) { fl, err := fsys.Open(path) if err != nil { - return nil, err + return Template{}, err } defer fl.Close() return t.ParseWithName(path, fl) @@ -123,12 +125,12 @@ func (t *Namespace) ParseFSGlob(fsys fs.FS, glob string) error { } // ParseString parses a string using the given filename. -func (t *Namespace) ParseString(filename, tmpl string) (*Template, error) { +func (t *Namespace) ParseString(filename, tmpl string) (Template, error) { return t.ParseWithName(filename, strings.NewReader(tmpl)) } // ParseString parses bytes using the given filename. -func (t *Namespace) ParseBytes(filename string, tmpl []byte) (*Template, error) { +func (t *Namespace) ParseBytes(filename string, tmpl []byte) (Template, error) { return t.ParseWithName(filename, bytes.NewReader(tmpl)) } diff --git a/salix.go b/salix.go index 8460890..2ea6508 100644 --- a/salix.go +++ b/salix.go @@ -63,86 +63,44 @@ type Template struct { tags map[string]Tag vars map[string]reflect.Value - macroMtx sync.Mutex + macroMtx *sync.Mutex macros map[string][]ast.Node } // WithVarMap returns a copy of the template with its variable map set to m. -func (t *Template) WithVarMap(m map[string]any) *Template { - newTmpl := &Template{ - ns: t.ns, - name: t.name, - ast: t.ast, - - escapeHTML: t.escapeHTML, - - tags: t.tags, - vars: map[string]reflect.Value{}, - } - +func (t Template) WithVarMap(m map[string]any) Template { + t.vars = map[string]reflect.Value{} if m != nil { for k, v := range m { - newTmpl.vars[k] = reflect.ValueOf(v) + t.vars[k] = reflect.ValueOf(v) } } - - return newTmpl + return t } // WithTagMap returns a copy of the template with its tag map set to m. -func (t *Template) WithTagMap(m map[string]Tag) *Template { +func (t Template) WithTagMap(m map[string]Tag) Template { // Make sure the tag map is never nil to avoid panics if m == nil { m = map[string]Tag{} } - - return &Template{ - ns: t.ns, - name: t.name, - ast: t.ast, - - escapeHTML: t.escapeHTML, - - tags: m, - vars: t.vars, - } + t.tags = m + return t } // WithEscapeHTML returns a copy of the template with HTML escaping enabled or disabled. // The HTML escaping functionality is NOT context-aware. // Using the HTML type allows you to get around the escaping if needed. -func (t *Template) WithEscapeHTML(b bool) *Template { +func (t Template) WithEscapeHTML(b bool) Template { t.escapeHTML = true - return &Template{ - ns: t.ns, - name: t.name, - ast: t.ast, - - escapeHTML: b, - - tags: t.tags, - vars: t.vars, - } + return t } // Execute executes a parsed template and writes // the result to w. -func (t *Template) Execute(w io.Writer) error { - // Create a new template to give each execution - // its own macros - tmpl := &Template{ - ns: t.ns, - name: t.name, - ast: t.ast, - - escapeHTML: t.escapeHTML, - - tags: t.tags, - vars: t.vars, - macros: map[string][]ast.Node{}, - } - - return tmpl.execute(w, t.ast, nil) +func (t Template) Execute(w io.Writer) error { + t.macros = map[string][]ast.Node{} + return t.execute(w, t.ast, nil) } func (t *Template) execute(w io.Writer, nodes []ast.Node, local map[string]any) error {