commit 550d80f2d8fcca03e6a736127b585a4b5f9cae81
parent ef066916f4421681b9e95d54788f71e9f8590d93
Author: gearsix <gearsix@tuta.io>
Date: Sat, 2 Oct 2021 15:02:07 +0100
renamed LoadTemplateFile -> LoadTemplateFilepath
Diffstat:
5 files changed, 334 insertions(+), 22 deletions(-)
diff --git a/CHANGELOG b/CHANGELOG
@@ -1,5 +1,20 @@
# CHANGELOG
+## v0.3.0
+
+- added LoadTemplate, loads templates from io.Reader params, template language and name must be specified.
+This function is the template.go counterpart to data.go#LoadData
+- added LoadTemplateStrings loads templates from string params (calls LoadTemplate)
+
+## v0.2.2
+
+- added .Source to suti.Template, contains the filepath of the source file for the template
+- More tidyup
+
+## v0.2.1
+
+- Tidyup work
+
## v0.2.0
- massive refactored to data API:
diff --git a/TODO b/TODO
@@ -0,0 +1 @@
+- Make `loadTemplateFileX` public
diff --git a/template.go b/template.go
@@ -187,31 +187,31 @@ func loadTemplateFileMst(root string, partials ...string) (*mst.Template, error)
return mst.ParseFilePartials(root, mstfp)
}
-// LoadTemplateFile loads a Template from file `root`. All files in `partials`
+// LoadTemplateFilepath loads a Template from file `root`. All files in `partials`
// that have the same template type (identified by file extension) are also
// parsed and associated with the parsed root template.
-func LoadTemplateFile(root string, partials ...string) (t Template, e error) {
- if len(root) == 0 {
- e = fmt.Errorf("no root template specified")
+func LoadTemplateFilepath(rootPath string, partialPaths ...string) (t Template, e error) {
+ if len(rootPath) == 0 {
+ e = fmt.Errorf("no rootPath template specified")
}
- if stat, err := os.Stat(root); err != nil {
+ if stat, err := os.Stat(rootPath); err != nil {
e = err
} else if stat.IsDir() {
- e = fmt.Errorf("root path must be a file, not a directory: %s", root)
+ e = fmt.Errorf("rootPath path must be a file, not a directory: %s", rootPath)
}
if e != nil {
return
}
- t = Template{Source: root}
- ttype := getTemplateType(root)
+ t = Template{Source: rootPath}
+ ttype := getTemplateType(rootPath)
if ttype == "tmpl" || ttype == "gotmpl" {
- t.Template, e = loadTemplateFileTmpl(root, partials...)
+ t.Template, e = loadTemplateFileTmpl(rootPath, partialPaths...)
} else if ttype == "hmpl" || ttype == "gohmpl" {
- t.Template, e = loadTemplateFileHmpl(root, partials...)
+ t.Template, e = loadTemplateFileHmpl(rootPath, partialPaths...)
} else if ttype == "mst" || ttype == "mustache" {
- t.Template, e = loadTemplateFileMst(root, partials...)
+ t.Template, e = loadTemplateFileMst(rootPath, partialPaths...)
} else {
e = fmt.Errorf("'%s' is not a supported template language", ttype)
}
diff --git a/template.go.orig b/template.go.orig
@@ -0,0 +1,296 @@
+package suti
+
+/*
+Copyright (C) 2021 gearsix <gearsix@tuta.io>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+import (
+ "bytes"
+ "fmt"
+ mst "github.com/cbroglie/mustache"
+ hmpl "html/template"
+ "io/fs"
+ "os"
+ "path/filepath"
+ "reflect"
+ "strconv"
+ "strings"
+ tmpl "text/template"
+)
+
+// SupportedTemplateLangs provides a list of supported languages for template files (lower-case)
+var SupportedTemplateLangs = []string{"tmpl", "hmpl", "mst"}
+
+// IsSupportedTemplateLang provides the index of `SupportedTemplateLangs` that `lang` is at.
+// If `lang` is not in `SupportedTemplateLangs`, `-1` will be returned.
+// File extensions can be passed in `lang`, the prefixed `.` will be trimmed.
+func IsSupportedTemplateLang(lang string) int {
+ lang = strings.ToLower(lang)
+ if len(lang) > 0 && lang[0] == '.' {
+ lang = lang[1:]
+ }
+ for i, l := range SupportedTemplateLangs {
+ if lang == l {
+ return i
+ }
+ }
+ return -1
+}
+
+func getTemplateType(path string) string {
+ return strings.TrimPrefix(filepath.Ext(path), ".")
+}
+
+// Template is a generic interface to any template parsed from LoadTemplateFile
+type Template struct {
+ Source string
+ Template interface{}
+}
+
+// Execute executes `t` against `d`. Reflection is used to determine
+// the template type and call it's execution fuction.
+func (t *Template) Execute(d interface{}) (result bytes.Buffer, err error) {
+ var funcName string
+ var params []reflect.Value
+ switch (reflect.TypeOf(t.Template).String()) {
+ case "*template.Template": // golang templates
+ funcName = "Execute"
+ params = []reflect.Value{reflect.ValueOf(&result), reflect.ValueOf(d)}
+ case "*mustache.Template":
+ funcName = "FRender"
+ params = []reflect.Value{reflect.ValueOf(&result), reflect.ValueOf(d)}
+ default:
+ err = fmt.Errorf("unable to infer template type '%s'", reflect.TypeOf(t.Template).String())
+ }
+
+ if err == nil {
+ rval := reflect.ValueOf(t.Template).MethodByName(funcName).Call(params)
+ if !rval[0].IsNil() { // err != nil
+ err = rval[0].Interface().(error)
+ }
+ }
+
+ return
+}
+
+// TODO
+func LoadTemplateFileTmpl(root string, partials ...string) (*tmpl.Template, error) {
+ var stat os.FileInfo
+ t, e := tmpl.ParseFiles(root)
+
+ for i := 0; i < len(partials) && e == nil; i++ {
+ p := partials[i]
+ ptype := getTemplateType(p)
+
+ stat, e = os.Stat(p)
+ if e == nil {
+ if ptype == "tmpl" || ptype == "gotmpl" {
+ t, e = t.ParseFiles(p)
+ } else if strings.Contains(p, "*") {
+ t, e = t.ParseGlob(p)
+ } else if stat.IsDir() {
+ e = filepath.Walk(p, func(path string, info fs.FileInfo, err error) error {
+ if err == nil && !info.IsDir() {
+ ptype = getTemplateType(path)
+ if ptype == "tmpl" || ptype == "gotmpl" {
+ t, err = t.ParseFiles(path)
+ }
+ }
+ return err
+ })
+ } else {
+ return nil, fmt.Errorf("non-matching filetype (%s)", p)
+ }
+ }
+ }
+
+ return t, e
+}
+
+// TODO
+func LoadTemplateFileHmpl(root string, partials ...string) (*hmpl.Template, error) {
+ var stat os.FileInfo
+ t, e := hmpl.ParseFiles(root)
+
+ for i := 0; i < len(partials) && e == nil; i++ {
+ p := partials[i]
+ ptype := getTemplateType(p)
+
+ stat, e = os.Stat(p)
+ if e == nil {
+ if ptype == "hmpl" || ptype == "gohmpl" {
+ t, e = t.ParseFiles(p)
+ } else if strings.Contains(p, "*") {
+ t, e = t.ParseGlob(p)
+ } else if stat.IsDir() {
+ e = filepath.Walk(p, func(path string, info fs.FileInfo, err error) error {
+ if err == nil && !info.IsDir() {
+ ptype = getTemplateType(path)
+ if ptype == "hmpl" || ptype == "gohmpl" {
+ t, err = t.ParseFiles(path)
+ }
+ }
+ return err
+ })
+ } else {
+ return nil, fmt.Errorf("non-matching filetype (%s)", p)
+ }
+ }
+ }
+
+ return t, e
+}
+
+// TODO
+func LoadTemplateFileMst(root string, partials ...string) (*mst.Template, error) {
+ var err error
+ for p, partial := range partials {
+ if err != nil {
+ break
+ }
+
+ if stat, e := os.Stat(partial); e != nil {
+ partials = append(partials[:p], partials[p+1:]...)
+ err = e
+ } else if stat.IsDir() == false {
+ partials[p] = filepath.Dir(partial)
+ } else if strings.Contains(partial, "*") {
+ if paths, e := filepath.Glob(partial); e != nil {
+ partials = append(partials[:p], partials[p+1:]...)
+ err = e
+ } else {
+ partials = append(partials[:p], partials[p+1:]...)
+ partials = append(partials, paths...)
+ }
+ }
+ }
+
+ if err != nil {
+ return nil, err
+ }
+
+ mstfp := &mst.FileProvider{
+ Paths: partials,
+ Extensions: []string{".mst", ".mustache"},
+ }
+ return mst.ParseFilePartials(root, mstfp)
+}
+
+// LoadTemplateFile loads a Template from file `root`. All files in `partials`
+// that have the same template type (identified by file extension) are also
+// parsed and associated with the parsed root template.
+func LoadTemplateFile(root string, partials ...string) (t Template, e error) {
+ if len(root) == 0 {
+ e = fmt.Errorf("no root template specified")
+ }
+ if stat, err := os.Stat(root); err != nil {
+ e = err
+ } else if stat.IsDir() {
+ e = fmt.Errorf("root path must be a file, not a directory: %s", root)
+ }
+
+<<<<<<< HEAD
+ if e == nil {
+ t = Template{Source: root}
+ ttype := getTemplateType(root)
+ if ttype == "tmpl" || ttype == "gotmpl" {
+ t.Template, e = LoadTemplateFileTmpl(root, partials...)
+ } else if ttype == "hmpl" || ttype == "gohmpl" {
+ t.Template, e = LoadTemplateFileHmpl(root, partials...)
+ } else if ttype == "mst" || ttype == "mustache" {
+ t.Template, e = LoadTemplateFileMst(root, partials...)
+ } else {
+ e = fmt.Errorf("'%s' is not a supported template language", ttype)
+=======
+ if e != nil {
+ return
+ }
+
+ t = Template{Source: root}
+ ttype := getTemplateType(root)
+ if ttype == "tmpl" || ttype == "gotmpl" {
+ t.Template, e = loadTemplateFileTmpl(root, partials...)
+ } else if ttype == "hmpl" || ttype == "gohmpl" {
+ t.Template, e = loadTemplateFileHmpl(root, partials...)
+ } else if ttype == "mst" || ttype == "mustache" {
+ t.Template, e = loadTemplateFileMst(root, partials...)
+ } else {
+ e = fmt.Errorf("'%s' is not a supported template language", ttype)
+ }
+
+ return
+}
+
+// LoadTemplateString loads a Template from string `root` of type `ttype`, named `name`.
+// `ttype` must be an element in `SupportedTemplateLangs`.
+// `name` is optional, if empty the template name will be "template".
+// `root` should be a string of template, with syntax matching that of `ttype`.
+// `partials` should be a string of template, with syntax matching that of `ttype`.
+func LoadTemplateString(ttype string, name string, root string, partials ...string) (t Template, e error) {
+ if len(root) == 0 {
+ e = fmt.Errorf("no root template")
+ }
+ if IsSupportedTemplateLang(ttype) == -1 {
+ e = fmt.Errorf("invalid type '%s'", ttype)
+ }
+ if e != nil {
+ return
+ }
+
+ if len(name) == 0 {
+ name = "template"
+ }
+
+ switch(ttype) {
+ case "tmpl":
+ var template *tmpl.Template
+ if template, e = tmpl.New(name).Parse(root); e != nil {
+ break
+ }
+ for i, p := range partials {
+ if _, e = template.New(name + "-partial" + strconv.Itoa(i)).Parse(p); e != nil {
+ break
+ }
+>>>>>>> LoadTemplateString
+ }
+ t.Template = template
+ case "hmpl":
+ var template *hmpl.Template
+ if template, e = hmpl.New(name).Parse(root); e != nil {
+ break
+ }
+ for i, p := range partials {
+ if _, e = template.New(name + "-partial" + strconv.Itoa(i)).Parse(p); e != nil {
+ break
+ }
+ }
+ t.Template = template
+ case "mst":
+ var template *mst.Template
+ mstpp := new(mst.StaticProvider)
+ mstpp.Partials = make(map[string]string)
+ for p, partial := range partials {
+ mstpp.Partials[name+"-partial"+strconv.Itoa(p)] = partial
+ }
+ template, e = mst.ParseStringPartials(root, mstpp)
+ t.Template = template
+ default:
+ e = fmt.Errorf("'%s' is not a supported template language", ttype)
+ }
+
+ return
+}
+
diff --git a/template_test.go b/template_test.go
@@ -141,7 +141,7 @@ func validateTemplateFile(t *testing.T, template Template, root string, partials
}
}
-func TestLoadTemplateFile(t *testing.T) {
+func TestLoadTemplateFilepath(t *testing.T) {
var gr, gp, br, bp []string
tdir := t.TempDir()
i := 0
@@ -176,19 +176,19 @@ func TestLoadTemplateFile(t *testing.T) {
writeTestFile(t, bp[i], mstPartialBad)
for g, root := range gr { // good root, good partials
- if template, e := LoadTemplateFile(root, gp[g]); e != nil {
+ if template, e := LoadTemplateFilepath(root, gp[g]); e != nil {
t.Fatal(e)
} else {
validateTemplateFile(t, template, root, gp[g])
}
}
for _, root := range br { // bad root, good partials
- if _, e := LoadTemplateFile(root, gp...); e == nil {
+ if _, e := LoadTemplateFilepath(root, gp...); e == nil {
t.Fatalf("no error for bad template with good partials\n")
}
}
for _, root := range br { // bad root, bad partials
- if _, e := LoadTemplateFile(root, bp...); e == nil {
+ if _, e := LoadTemplateFilepath(root, bp...); e == nil {
t.Fatalf("no error for bad template with bad partials\n")
}
}
@@ -236,7 +236,7 @@ func TestLoadTemplateString(t *testing.T) {
}
}
-// func TestLoadTemplateString(t *testing.T) {} // This is tested by TestLoadTemplateFile and TestLoadTemplateString
+// func TestLoadTemplateString(t *testing.T) {} // This is tested by TestLoadTemplateFilepath and TestLoadTemplateString
func validateExecute(t *testing.T, results string, expect string, e error) {
if e != nil {
@@ -281,22 +281,22 @@ func TestExecute(t *testing.T) {
d = append(d, data)
gd["data"] = d
- if tmpl1, e = LoadTemplateFile(tmplr, tmplp); e != nil {
+ if tmpl1, e = LoadTemplateFilepath(tmplr, tmplp); e != nil {
t.Skip("setup failure:", e)
}
- if tmpl2, e = LoadTemplateFile(tmplr, tdir); e != nil {
+ if tmpl2, e = LoadTemplateFilepath(tmplr, tdir); e != nil {
t.Skip("setup failure:", e)
}
- if hmpl1, e = LoadTemplateFile(hmplr, hmplp); e != nil {
+ if hmpl1, e = LoadTemplateFilepath(hmplr, hmplp); e != nil {
t.Skip("setup failure:", e)
}
- if hmpl2, e = LoadTemplateFile(tmplr, tdir); e != nil {
+ if hmpl2, e = LoadTemplateFilepath(tmplr, tdir); e != nil {
t.Skip("setup failure:", e)
}
- if mst1, e = LoadTemplateFile(mstr, mstp); e != nil {
+ if mst1, e = LoadTemplateFilepath(mstr, mstp); e != nil {
t.Skip("setup failure:", e)
}
- if mst2, e = LoadTemplateFile(tmplr, tdir); e != nil {
+ if mst2, e = LoadTemplateFilepath(tmplr, tdir); e != nil {
t.Skip("setup failure:", e)
}