templating/engine.go (78 lines of code) (raw):

// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. package templating import ( "bytes" "encoding/base64" "errors" "fmt" "log" "reflect" "strings" "text/template" "github.com/Masterminds/sprig" ) // Engine is a wrapper around Go templates. type Engine struct { FuncMap template.FuncMap StrictMode bool } // NewEngine creates a new engine. func NewEngine() *Engine { fm := FuncMap() return &Engine{ FuncMap: fm, } } // Base64Encode Overrides the function b64enc in Sprig to provide nil behavior func Base64Encode(v interface{}) string { if v == nil { return "" } return base64.StdEncoding.EncodeToString([]byte(reflect.ValueOf(v).String())) } // FuncMap returns a FuncMap representing all of the functionality of the engine. func FuncMap() template.FuncMap { // We are overriding the b64enc function with custom implementation modMap := sprig.TxtFuncMap() modMap["b64enc"] = Base64Encode return modMap } // Render renders a template. func (e *Engine) Render(t *Template, values Values) (string, error) { if t == nil { return "", errors.New("template is required") } if values == nil { return "", errors.New("values is required") } rt := renderableTemplate{ name: t.Name, template: string(t.Data), values: values, } return e.render(rt) } func (e *Engine) render(rt renderableTemplate) (rendered string, err error) { return e.RenderGoTemplate(rt.name, rt.template, rt.values) } // RenderGoTemplate renders a go template given template and data. func (e *Engine) RenderGoTemplate(name string, input string, data interface{}) (rendered string, err error) { defer func() { if r := recover(); r != nil { log.Printf("Template rendering recovered. Value: %v\n", r) } }() t := template.New("_") if e.StrictMode { t.Option("missingkey=error") } else { // NB: zero will attempt to add default values for types it knows. // It still emits <no value> for others. This is corrected below. t.Option("missingkey=zero") } t = t.New(name).Funcs(e.FuncMap) if _, err := t.Parse(input); err != nil { return "", fmt.Errorf("failed to parse template: %s. Err: %v", name, err) } var buf bytes.Buffer if err := t.ExecuteTemplate(&buf, name, data); err != nil { return "", fmt.Errorf("failed to execute template: %s. Err: %v", name, err) } // NB: handle `missingkey=zero` by removing the string. rendered = strings.Replace(buf.String(), "<no value>", "", -1) return rendered, nil } type renderableTemplate struct { name string template string values Values }