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 }