...

Source file src/text/template/helper.go

Documentation: text/template

		 1  // Copyright 2011 The Go Authors. All rights reserved.
		 2  // Use of this source code is governed by a BSD-style
		 3  // license that can be found in the LICENSE file.
		 4  
		 5  // Helper functions to make constructing templates easier.
		 6  
		 7  package template
		 8  
		 9  import (
		10  	"fmt"
		11  	"io/fs"
		12  	"os"
		13  	"path"
		14  	"path/filepath"
		15  )
		16  
		17  // Functions and methods to parse templates.
		18  
		19  // Must is a helper that wraps a call to a function returning (*Template, error)
		20  // and panics if the error is non-nil. It is intended for use in variable
		21  // initializations such as
		22  //	var t = template.Must(template.New("name").Parse("text"))
		23  func Must(t *Template, err error) *Template {
		24  	if err != nil {
		25  		panic(err)
		26  	}
		27  	return t
		28  }
		29  
		30  // ParseFiles creates a new Template and parses the template definitions from
		31  // the named files. The returned template's name will have the base name and
		32  // parsed contents of the first file. There must be at least one file.
		33  // If an error occurs, parsing stops and the returned *Template is nil.
		34  //
		35  // When parsing multiple files with the same name in different directories,
		36  // the last one mentioned will be the one that results.
		37  // For instance, ParseFiles("a/foo", "b/foo") stores "b/foo" as the template
		38  // named "foo", while "a/foo" is unavailable.
		39  func ParseFiles(filenames ...string) (*Template, error) {
		40  	return parseFiles(nil, readFileOS, filenames...)
		41  }
		42  
		43  // ParseFiles parses the named files and associates the resulting templates with
		44  // t. If an error occurs, parsing stops and the returned template is nil;
		45  // otherwise it is t. There must be at least one file.
		46  // Since the templates created by ParseFiles are named by the base
		47  // names of the argument files, t should usually have the name of one
		48  // of the (base) names of the files. If it does not, depending on t's
		49  // contents before calling ParseFiles, t.Execute may fail. In that
		50  // case use t.ExecuteTemplate to execute a valid template.
		51  //
		52  // When parsing multiple files with the same name in different directories,
		53  // the last one mentioned will be the one that results.
		54  func (t *Template) ParseFiles(filenames ...string) (*Template, error) {
		55  	t.init()
		56  	return parseFiles(t, readFileOS, filenames...)
		57  }
		58  
		59  // parseFiles is the helper for the method and function. If the argument
		60  // template is nil, it is created from the first file.
		61  func parseFiles(t *Template, readFile func(string) (string, []byte, error), filenames ...string) (*Template, error) {
		62  	if len(filenames) == 0 {
		63  		// Not really a problem, but be consistent.
		64  		return nil, fmt.Errorf("template: no files named in call to ParseFiles")
		65  	}
		66  	for _, filename := range filenames {
		67  		name, b, err := readFile(filename)
		68  		if err != nil {
		69  			return nil, err
		70  		}
		71  		s := string(b)
		72  		// First template becomes return value if not already defined,
		73  		// and we use that one for subsequent New calls to associate
		74  		// all the templates together. Also, if this file has the same name
		75  		// as t, this file becomes the contents of t, so
		76  		//	t, err := New(name).Funcs(xxx).ParseFiles(name)
		77  		// works. Otherwise we create a new template associated with t.
		78  		var tmpl *Template
		79  		if t == nil {
		80  			t = New(name)
		81  		}
		82  		if name == t.Name() {
		83  			tmpl = t
		84  		} else {
		85  			tmpl = t.New(name)
		86  		}
		87  		_, err = tmpl.Parse(s)
		88  		if err != nil {
		89  			return nil, err
		90  		}
		91  	}
		92  	return t, nil
		93  }
		94  
		95  // ParseGlob creates a new Template and parses the template definitions from
		96  // the files identified by the pattern. The files are matched according to the
		97  // semantics of filepath.Match, and the pattern must match at least one file.
		98  // The returned template will have the (base) name and (parsed) contents of the
		99  // first file matched by the pattern. ParseGlob is equivalent to calling
	 100  // ParseFiles with the list of files matched by the pattern.
	 101  //
	 102  // When parsing multiple files with the same name in different directories,
	 103  // the last one mentioned will be the one that results.
	 104  func ParseGlob(pattern string) (*Template, error) {
	 105  	return parseGlob(nil, pattern)
	 106  }
	 107  
	 108  // ParseGlob parses the template definitions in the files identified by the
	 109  // pattern and associates the resulting templates with t. The files are matched
	 110  // according to the semantics of filepath.Match, and the pattern must match at
	 111  // least one file. ParseGlob is equivalent to calling t.ParseFiles with the
	 112  // list of files matched by the pattern.
	 113  //
	 114  // When parsing multiple files with the same name in different directories,
	 115  // the last one mentioned will be the one that results.
	 116  func (t *Template) ParseGlob(pattern string) (*Template, error) {
	 117  	t.init()
	 118  	return parseGlob(t, pattern)
	 119  }
	 120  
	 121  // parseGlob is the implementation of the function and method ParseGlob.
	 122  func parseGlob(t *Template, pattern string) (*Template, error) {
	 123  	filenames, err := filepath.Glob(pattern)
	 124  	if err != nil {
	 125  		return nil, err
	 126  	}
	 127  	if len(filenames) == 0 {
	 128  		return nil, fmt.Errorf("template: pattern matches no files: %#q", pattern)
	 129  	}
	 130  	return parseFiles(t, readFileOS, filenames...)
	 131  }
	 132  
	 133  // ParseFS is like ParseFiles or ParseGlob but reads from the file system fsys
	 134  // instead of the host operating system's file system.
	 135  // It accepts a list of glob patterns.
	 136  // (Note that most file names serve as glob patterns matching only themselves.)
	 137  func ParseFS(fsys fs.FS, patterns ...string) (*Template, error) {
	 138  	return parseFS(nil, fsys, patterns)
	 139  }
	 140  
	 141  // ParseFS is like ParseFiles or ParseGlob but reads from the file system fsys
	 142  // instead of the host operating system's file system.
	 143  // It accepts a list of glob patterns.
	 144  // (Note that most file names serve as glob patterns matching only themselves.)
	 145  func (t *Template) ParseFS(fsys fs.FS, patterns ...string) (*Template, error) {
	 146  	t.init()
	 147  	return parseFS(t, fsys, patterns)
	 148  }
	 149  
	 150  func parseFS(t *Template, fsys fs.FS, patterns []string) (*Template, error) {
	 151  	var filenames []string
	 152  	for _, pattern := range patterns {
	 153  		list, err := fs.Glob(fsys, pattern)
	 154  		if err != nil {
	 155  			return nil, err
	 156  		}
	 157  		if len(list) == 0 {
	 158  			return nil, fmt.Errorf("template: pattern matches no files: %#q", pattern)
	 159  		}
	 160  		filenames = append(filenames, list...)
	 161  	}
	 162  	return parseFiles(t, readFileFS(fsys), filenames...)
	 163  }
	 164  
	 165  func readFileOS(file string) (name string, b []byte, err error) {
	 166  	name = filepath.Base(file)
	 167  	b, err = os.ReadFile(file)
	 168  	return
	 169  }
	 170  
	 171  func readFileFS(fsys fs.FS) func(string) (string, []byte, error) {
	 172  	return func(file string) (name string, b []byte, err error) {
	 173  		name = path.Base(file)
	 174  		b, err = fs.ReadFile(fsys, file)
	 175  		return
	 176  	}
	 177  }
	 178  

View as plain text