commit 4316ce2c0d58463e589cb0d73e378934be6d660a
parent 558cb2cc2e31e860daae15f1d5b82803d1630e3e
Author: gearsix <gearsix@tuta.io>
Date: Sat, 2 Oct 2021 00:37:40 +0100
Merge branch 'LoadTemplateString'
Diffstat:
M | template.go | | | 98 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------- |
M | template_test.go | | | 104 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------ |
2 files changed, 167 insertions(+), 35 deletions(-)
diff --git a/template.go b/template.go
@@ -26,6 +26,7 @@ import (
"os"
"path/filepath"
"reflect"
+ "strconv"
"strings"
tmpl "text/template"
)
@@ -85,8 +86,7 @@ func (t *Template) Execute(d interface{}) (result bytes.Buffer, err error) {
return
}
-// TODO
-func LoadTemplateFileTmpl(root string, partials ...string) (*tmpl.Template, error) {
+func loadTemplateFileTmpl(root string, partials ...string) (*tmpl.Template, error) {
var stat os.FileInfo
t, e := tmpl.ParseFiles(root)
@@ -111,7 +111,7 @@ func LoadTemplateFileTmpl(root string, partials ...string) (*tmpl.Template, erro
return err
})
} else {
- return nil, fmt.Errorf("non-matching filetype")
+ return nil, fmt.Errorf("non-matching filetype (%s)", p)
}
}
}
@@ -119,8 +119,7 @@ func LoadTemplateFileTmpl(root string, partials ...string) (*tmpl.Template, erro
return t, e
}
-// TODO
-func LoadTemplateFileHmpl(root string, partials ...string) (*hmpl.Template, error) {
+func loadTemplateFileHmpl(root string, partials ...string) (*hmpl.Template, error) {
var stat os.FileInfo
t, e := hmpl.ParseFiles(root)
@@ -145,7 +144,7 @@ func LoadTemplateFileHmpl(root string, partials ...string) (*hmpl.Template, erro
return err
})
} else {
- return nil, fmt.Errorf("non-matching filetype")
+ return nil, fmt.Errorf("non-matching filetype (%s)", p)
}
}
}
@@ -153,8 +152,7 @@ func LoadTemplateFileHmpl(root string, partials ...string) (*hmpl.Template, erro
return t, e
}
-// TODO
-func LoadTemplateFileMst(root string, partials ...string) (*mst.Template, error) {
+func loadTemplateFileMst(root string, partials ...string) (*mst.Template, error) {
var err error
for p, partial := range partials {
if err != nil {
@@ -195,25 +193,85 @@ 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)
}
- 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
+ }
}
+ 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
@@ -25,6 +25,24 @@ import (
"testing"
)
+const tmplRootGood = "{{.eg}} {{ template \"tmplPartialGood.tmpl\" . }}"
+const tmplPartialGood = "{{range .data}}{{.eg}}{{end}}"
+const tmplResult = "0 00"
+const tmplRootBad = "{{ example }}} {{{ template \"tmplPartialBad.tmpl\" . }}"
+const tmplPartialBad = "{{{ .example }}"
+
+const hmplRootGood = "<!DOCTYPE html><html><p>{{.eg}} {{ template \"hmplPartialGood.hmpl\" . }}</p></html>"
+const hmplPartialGood = "<b>{{range .data}}{{.eg}}{{end}}</b>"
+const hmplResult = "<!DOCTYPE html><html><p>0 <b>00</b></p></html>"
+const hmplRootBad = "{{ example }} {{{ template \"hmplPartialBad.hmpl\" . }}"
+const hmplPartialBad = "<b>{{{ .example2 }}</b>"
+
+const mstRootGood = "{{eg}} {{> mstPartialGood}}"
+const mstPartialGood = "{{#data}}{{eg}}{{/data}}"
+const mstResult = "0 00"
+const mstRootBad = "{{> badPartial.mst}}{{#doesnt-exist}}{{/exit}}"
+const mstPartialBad = "p{{$}{{ > noexist}}"
+
func TestIsSupportedTemplateLang(t *testing.T) {
exts := []string{
".tmpl", "tmpl", "TMPL", ".TMPL",
@@ -55,23 +73,37 @@ func TestIsSupportedTemplateLang(t *testing.T) {
}
}
-const tmplRootGood = "{{.eg}} {{ template \"tmplPartialGood.tmpl\" . }}"
-const tmplPartialGood = "{{range .data}}{{.eg}}{{end}}"
-const tmplResult = "0 00"
-const tmplRootBad = "{{ example }}} {{{ template \"tmplPartialBad.tmpl\" . }}"
-const tmplPartialBad = "{{{ .example }}"
+func validateTemplate(t *testing.T, template Template, templateType string, rootName string, partialNames ...string) {
+ types := map[string]string{
+ "tmpl": "*template.Template",
+ "gotmpl": "*template.Template",
+ "hmpl": "*template.Template",
+ "gohmpl": "*template.Template",
+ "mst": "*mustache.Template",
+ "mustache": "*mustache.Template",
+ }
-const hmplRootGood = "<!DOCTYPE html><html><p>{{.eg}} {{ template \"hmplPartialGood.hmpl\" . }}</p></html>"
-const hmplPartialGood = "<b>{{range .data}}{{.eg}}{{end}}</b>"
-const hmplResult = "<!DOCTYPE html><html><p>0 <b>00</b></p></html>"
-const hmplRootBad = "{{ example }} {{{ template \"hmplPartialBad.hmpl\" . }}"
-const hmplPartialBad = "<b>{{{ .example2 }}</b>"
+ rt := reflect.TypeOf(template.Template).String()
+ if rt != types[templateType] {
+ t.Fatalf("invalid template type '%s' loaded, should be '%s'", rt, types[templateType])
+ }
-const mstRootGood = "{{eg}} {{> mstPartialGood}}"
-const mstPartialGood = "{{#data}}{{eg}}{{/data}}"
-const mstResult = "0 00"
-const mstRootBad = "{{> badPartial.mst}}{{#doesnt-exist}}{{/exit}}"
-const mstPartialBad = "p{{$}{{ > noexist}}"
+ if types[templateType] == "*template.Template" {
+ var rv []reflect.Value
+ for _, p := range partialNames {
+ rv := reflect.ValueOf(template.Template).MethodByName("Lookup").Call([]reflect.Value{reflect.ValueOf(p)})
+ if rv[0].IsNil() {
+ t.Fatalf("missing defined template '%s'", p)
+ rv = reflect.ValueOf(template.Template).MethodByName("DefinedTemplates").Call([]reflect.Value{})
+ t.Log(rv)
+ }
+ }
+ rv = reflect.ValueOf(template.Template).MethodByName("Name").Call([]reflect.Value{})
+ if rv[0].String() != rootName {
+ t.Fatalf("invalid template name: %s does not match %s", rv[0].String(), rootName)
+ }
+ }
+}
func validateTemplateFile(t *testing.T, template Template, root string, partials ...string) {
types := map[string]string{
@@ -162,6 +194,48 @@ func TestLoadTemplateFile(t *testing.T) {
}
}
+func TestLoadTemplateString(t *testing.T) {
+ var gr, gp, br, bp []string
+
+ gr = append(gr, tmplRootGood)
+ gp = append(gp, tmplPartialGood)
+ br = append(br, tmplRootBad)
+ bp = append(bp, tmplPartialBad)
+
+ gr = append(gr, hmplRootGood)
+ gp = append(gp, hmplPartialGood)
+ br = append(br, hmplRootBad)
+ bp = append(bp, hmplPartialBad)
+
+ gr = append(gr, mstRootGood)
+ gp = append(gp, mstPartialGood)
+ br = append(br, mstRootBad)
+ bp = append(bp, mstPartialBad)
+
+ name := "test"
+ var ttype string
+ for g, root := range gr { // good root, good partials
+ ttype = SupportedTemplateLangs[g]
+ if template, e := LoadTemplateString(ttype, name, root, gp[g]); e != nil {
+ t.Fatalf("'%s' template failed to load: %s", ttype, e)
+ } else {
+ validateTemplate(t, template, ttype, name, name + "-partial0")
+ }
+ }
+ for b, root := range br { // bad root, good partials
+ ttype = SupportedTemplateLangs[b]
+ if _, e := LoadTemplateString(ttype, name, root, gp...); e == nil {
+ t.Fatalf("no error for bad template with good partials\n")
+ }
+ }
+ for b, root := range br { // bad root, bad partials
+ ttype = SupportedTemplateLangs[b]
+ if _, e := LoadTemplateString(ttype, name, root, bp...); e == nil {
+ t.Fatalf("no error for bad template with bad partials\n")
+ }
+ }
+}
+
func validateExecute(t *testing.T, results string, expect string, e error) {
if e != nil {
t.Fatal(e)