github.com/blend/go-sdk@v1.20220411.3/template/template.go (about)

     1  /*
     2  
     3  Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved
     4  Use of this source code is governed by a MIT license that can be found in the LICENSE file.
     5  
     6  */
     7  
     8  package template
     9  
    10  import (
    11  	"bytes"
    12  	"io"
    13  	"os"
    14  	texttemplate "text/template"
    15  
    16  	"github.com/blend/go-sdk/env"
    17  	"github.com/blend/go-sdk/ex"
    18  )
    19  
    20  // New creates a new template.
    21  func New() *Template {
    22  	temp := &Template{
    23  		Viewmodel: Viewmodel{
    24  			vars: Vars{},
    25  			env:  env.Env(),
    26  		},
    27  	}
    28  	temp.funcs = texttemplate.FuncMap(ViewFuncs{}.FuncMap())
    29  	return temp
    30  }
    31  
    32  // NewFromFile creates a new template from a file.
    33  func NewFromFile(filepath string) (*Template, error) {
    34  	contents, err := os.ReadFile(filepath)
    35  	if err != nil {
    36  		return nil, ex.New(err)
    37  	}
    38  
    39  	return New().WithName(filepath).WithBody(string(contents)), nil
    40  }
    41  
    42  // Template is a wrapper for html.Template.
    43  type Template struct {
    44  	Viewmodel
    45  	name       string
    46  	body       string
    47  	includes   []string
    48  	funcs      texttemplate.FuncMap
    49  	leftDelim  string
    50  	rightDelim string
    51  }
    52  
    53  // WithName sets the template name.
    54  func (t *Template) WithName(name string) *Template {
    55  	t.name = name
    56  	return t
    57  }
    58  
    59  // Name returns the template name if set, or if not set, just "template" as a constant.
    60  func (t *Template) Name() string {
    61  	if len(t.name) > 0 {
    62  		return t.name
    63  	}
    64  	return "template"
    65  }
    66  
    67  // WithDelims sets the template action delimiters, treating empty string as default delimiter.
    68  func (t *Template) WithDelims(left, right string) *Template {
    69  	t.leftDelim = left
    70  	t.rightDelim = right
    71  	return t
    72  }
    73  
    74  // WithBody sets the template body and returns a reference to the template object.
    75  func (t *Template) WithBody(body string) *Template {
    76  	t.body = body
    77  	return t
    78  }
    79  
    80  // WithInclude includes a (sub) template into the rendering assets.
    81  func (t *Template) WithInclude(body string) *Template {
    82  	t.includes = append(t.includes, body)
    83  	return t
    84  }
    85  
    86  // Body returns the template body.
    87  func (t *Template) Body() string {
    88  	return t.body
    89  }
    90  
    91  // WithVar sets a variable and returns a reference to the template object.
    92  func (t *Template) WithVar(key string, value interface{}) *Template {
    93  	t.SetVar(key, value)
    94  	return t
    95  }
    96  
    97  // SetVar sets a var in the template.
    98  func (t *Template) SetVar(key string, value interface{}) {
    99  	t.vars[key] = value
   100  }
   101  
   102  // WithVars reads a map of variables into the template.
   103  func (t *Template) WithVars(vars Vars) *Template {
   104  	t.vars = MergeVars(t.vars, vars)
   105  	return t
   106  }
   107  
   108  // WithEnvVars sets the environment variables.
   109  func (t *Template) WithEnvVars(envVars env.Vars) *Template {
   110  	t.Viewmodel.env = env.Merge(t.Viewmodel.env, envVars)
   111  	return t
   112  }
   113  
   114  // SetVarsFromFile reads vars from a file and merges them
   115  // with the current variables set.
   116  func (t *Template) SetVarsFromFile(path string) error {
   117  	fileVars, err := NewVarsFromPath(path)
   118  	if err != nil {
   119  		return err
   120  	}
   121  
   122  	t.vars = MergeVars(t.vars, fileVars)
   123  	return nil
   124  }
   125  
   126  // Process processes the template.
   127  func (t *Template) Process(dst io.Writer) error {
   128  	base := texttemplate.New(t.Name()).Funcs(t.ViewFuncs()).Delims(t.leftDelim, t.rightDelim)
   129  
   130  	var err error
   131  	for _, include := range t.includes {
   132  		_, err = base.New(t.Name()).Parse(include)
   133  		if err != nil {
   134  			return err
   135  		}
   136  	}
   137  
   138  	final, err := base.New(t.Name()).Parse(t.body)
   139  	if err != nil {
   140  		return err
   141  	}
   142  	return final.Execute(dst, t.Viewmodel)
   143  }
   144  
   145  // ProcessString is a helper to process the template as a string.
   146  func (t *Template) ProcessString() (string, error) {
   147  	buffer := new(bytes.Buffer)
   148  	err := t.Process(buffer)
   149  	if err != nil {
   150  		return "", err
   151  	}
   152  	return buffer.String(), nil
   153  }
   154  
   155  // MustProcessString is a helper to process a template as a string
   156  // and panic on error.
   157  func (t *Template) MustProcessString() string {
   158  	output, err := t.ProcessString()
   159  	if err != nil {
   160  		panic(err)
   161  	}
   162  	return output
   163  }
   164  
   165  // ViewFuncs returns the view funcs.
   166  func (t *Template) ViewFuncs() texttemplate.FuncMap {
   167  	return t.funcs
   168  }