github.com/posener/gitfs@v1.2.2-0.20200410105819-ea4e48d73ab9/fsutil/tmpl.go (about)

     1  package fsutil
     2  
     3  import (
     4  	"bytes"
     5  	htmltmpl "html/template"
     6  	"net/http"
     7  	"path/filepath"
     8  	"strings"
     9  	txttmpl "text/template"
    10  
    11  	"github.com/pkg/errors"
    12  )
    13  
    14  // TmplParse parses templates from the given filesystem according to the
    15  // given paths. If tmpl is not nil, the templates will be added to it.
    16  // paths must contain at least one path. All paths must exist in the
    17  // given filesystem.
    18  func TmplParse(fs http.FileSystem, tmpl *txttmpl.Template, paths ...string) (*txttmpl.Template, error) {
    19  	t := tmplParser{Template: tmpl}
    20  	err := parseFiles(fs, t.parse, paths...)
    21  	return t.Template, err
    22  }
    23  
    24  // TmplParseGlob parses templates from the given filesystem according to
    25  // the provided glob pattern. If tmpl is not nil, the templates will be
    26  // added to it.
    27  func TmplParseGlob(fs http.FileSystem, tmpl *txttmpl.Template, pattern string) (*txttmpl.Template, error) {
    28  	t := tmplParser{Template: tmpl}
    29  	err := parseGlob(fs, t.parse, pattern)
    30  	return t.Template, err
    31  }
    32  
    33  // TmplParseHTML parses HTML templates from the given filesystem according
    34  // to the given paths. If tmpl is not nil, the templates will be added to
    35  // it. paths must contain at least one path. All paths must exist in the
    36  // given filesystem.
    37  func TmplParseHTML(fs http.FileSystem, tmpl *htmltmpl.Template, paths ...string) (*htmltmpl.Template, error) {
    38  	t := tmplParserHTML{Template: tmpl}
    39  	err := parseFiles(fs, t.parse, paths...)
    40  	return t.Template, err
    41  }
    42  
    43  // TmplParseGlobHTML parses HTML templates from the given filesystem
    44  // according to the provided glob pattern. If tmpl is not nil, the
    45  // templates will be added to it.
    46  func TmplParseGlobHTML(fs http.FileSystem, tmpl *htmltmpl.Template, pattern string) (*htmltmpl.Template, error) {
    47  	t := tmplParserHTML{Template: tmpl}
    48  	err := parseGlob(fs, t.parse, pattern)
    49  	return t.Template, err
    50  }
    51  
    52  type tmplParser struct {
    53  	*txttmpl.Template
    54  }
    55  
    56  func (t *tmplParser) parse(name, content string) error {
    57  	var err error
    58  	if t.Template == nil {
    59  		t.Template = txttmpl.New(name)
    60  	} else {
    61  		t.Template = t.New(name)
    62  	}
    63  	t.Template, err = t.Parse(content)
    64  	return err
    65  }
    66  
    67  type tmplParserHTML struct {
    68  	*htmltmpl.Template
    69  }
    70  
    71  func (t *tmplParserHTML) parse(name, content string) error {
    72  	var err error
    73  	if t.Template == nil {
    74  		t.Template = htmltmpl.New(name)
    75  	} else {
    76  		t.Template = t.New(name)
    77  	}
    78  	t.Template, err = t.Parse(content)
    79  	return err
    80  }
    81  
    82  func parseFiles(fs http.FileSystem, parse func(name string, content string) error, paths ...string) error {
    83  	if len(paths) == 0 {
    84  		return errors.New("no paths provided")
    85  	}
    86  	buf := bytes.NewBuffer(nil)
    87  	for _, path := range paths {
    88  		f, err := fs.Open(strings.Trim(path, "/"))
    89  		if err != nil {
    90  			return errors.Wrapf(err, "opening template %s", path)
    91  		}
    92  		name := filepath.Base(path)
    93  		buf.Reset()
    94  		buf.ReadFrom(f)
    95  		err = parse(name, buf.String())
    96  		if err != nil {
    97  			return errors.Wrapf(err, "parsing template %s", path)
    98  		}
    99  	}
   100  	return nil
   101  }
   102  
   103  func parseGlob(fs http.FileSystem, parse func(name string, content string) error, pattern string) error {
   104  	buf := bytes.NewBuffer(nil)
   105  	walker := Walk(fs, "")
   106  	for walker.Step() {
   107  		matched, err := filepath.Match(pattern, walker.Path())
   108  		if err != nil {
   109  			return err
   110  		}
   111  		if !matched {
   112  			continue
   113  		}
   114  
   115  		f, err := fs.Open(walker.Path())
   116  		if err != nil {
   117  			return errors.Wrapf(err, "opening template %s", walker.Path())
   118  		}
   119  		st, err := f.Stat()
   120  		if err != nil {
   121  			return errors.Wrapf(err, "stat %s", walker.Path())
   122  		}
   123  		if st.IsDir() {
   124  			continue
   125  		}
   126  
   127  		buf.Reset()
   128  		buf.ReadFrom(f)
   129  		err = parse(walker.Stat().Name(), buf.String())
   130  		if err != nil {
   131  			return errors.Wrapf(err, "parsing template %s", walker.Path())
   132  		}
   133  	}
   134  	if err := walker.Err(); err != nil {
   135  		return errors.Wrap(err, "failed walking filesystem")
   136  	}
   137  	return nil
   138  }