github.com/tooploox/oya@v0.0.21-0.20230524103240-1cda1861aad6/pkg/template/template.go (about) 1 package template 2 3 import ( 4 "fmt" 5 "io" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 10 "github.com/gobwas/glob" 11 "github.com/tooploox/oya/pkg/errors" 12 ) 13 14 type ErrRenderFail struct { 15 } 16 17 func (e ErrRenderFail) Error() string { 18 return "render error" 19 } 20 21 // Template represents a template that can be rendered using provided values. 22 type Template interface { 23 Render(out io.Writer, values Scope) error 24 RenderString(values Scope) (string, error) 25 } 26 27 // Load loads template from the path. 28 func Load(path string, delimiters Delimiters) (Template, error) { 29 source, err := ioutil.ReadFile(path) 30 if err != nil { 31 return nil, err 32 } 33 return Parse(string(source), delimiters) 34 } 35 36 // Parse parses template in the source string. 37 func Parse(source string, delimiters Delimiters) (Template, error) { 38 return parsePlush(source, delimiters) 39 } 40 41 // RenderAll renders all templates in the path (directory or a single file) to an output path (directory or file) using the provided value scope. 42 func RenderAll(templatePath string, excludedPaths []string, outputPath string, values Scope, delimiters Delimiters) error { 43 return filepath.Walk(templatePath, func(path string, info os.FileInfo, err error) error { 44 if err != nil { 45 return err 46 } 47 if info.IsDir() { 48 return nil 49 } 50 relPath, err := filepath.Rel(templatePath, path) 51 if err != nil { 52 return err 53 } 54 if relPath == "." { 55 // templatePath is a path to a file. 56 relPath = filepath.Base(templatePath) 57 } 58 if ok, err := pathMatches(excludedPaths, relPath); ok || err != nil { 59 return err // err is nil if ok 60 } 61 62 filePath, err := renderString(filepath.Join(outputPath, relPath), values, delimiters) 63 if err != nil { 64 return errors.Wrap( 65 err, 66 ErrRenderFail{}, 67 errors.Location{ 68 Name: path, 69 VerboseName: fmt.Sprintf("in template %v", path), 70 }, 71 ) 72 } 73 err = renderFile(path, filePath, values, delimiters) 74 if err != nil { 75 return errors.Wrap(err, 76 ErrRenderFail{}, 77 errors.Location{ 78 Name: path, 79 VerboseName: fmt.Sprintf("in template %v", path), 80 }, 81 ) 82 } 83 return nil 84 }) 85 } 86 87 func renderFile(templatePath, outputPath string, values Scope, delimiters Delimiters) error { 88 t, err := Load(templatePath, delimiters) 89 if err != nil { 90 return err 91 } 92 93 err = os.MkdirAll(filepath.Dir(outputPath), 0700) 94 if err != nil { 95 return err 96 } 97 98 out, err := os.Create(outputPath) 99 if err != nil { 100 return err 101 } 102 defer func() { 103 _ = out.Close() 104 }() 105 106 return t.Render(out, values) 107 } 108 109 func renderString(templateSource string, values Scope, delimiters Delimiters) (string, error) { 110 t, err := Parse(templateSource, delimiters) 111 if err != nil { 112 return "", err 113 } 114 str, err := t.RenderString(values) 115 if err != nil { 116 return "", err 117 } 118 return str, nil 119 } 120 121 func pathMatches(patterns []string, path string) (bool, error) { 122 for _, pattern := range patterns { 123 p, err := glob.Compile(pattern) 124 if err != nil { 125 return false, err 126 } 127 if p.Match(path) { 128 return true, nil 129 } 130 } 131 return false, nil 132 }