github.com/hairyhenderson/gomplate/v4@v4.0.0-pre-2.0.20240520121557-362f058f0c93/gomplate.go (about) 1 // Package gomplate is a template renderer which supports a number of datasources, 2 // and includes hundreds of built-in functions. 3 package gomplate 4 5 import ( 6 "bytes" 7 "context" 8 "fmt" 9 "path/filepath" 10 "strings" 11 "text/template" 12 "time" 13 14 "github.com/hairyhenderson/gomplate/v4/internal/config" 15 "github.com/hairyhenderson/gomplate/v4/internal/datafs" 16 ) 17 18 // RunTemplates - run all gomplate templates specified by the given configuration 19 // 20 // Deprecated: use the Renderer interface instead 21 func RunTemplates(o *Config) error { 22 cfg, err := o.toNewConfig() 23 if err != nil { 24 return err 25 } 26 return Run(context.Background(), cfg) 27 } 28 29 // Run all gomplate templates specified by the given configuration 30 func Run(ctx context.Context, cfg *config.Config) error { 31 Metrics = newMetrics() 32 33 // apply defaults before validation 34 cfg.ApplyDefaults() 35 36 err := cfg.Validate() 37 if err != nil { 38 return fmt.Errorf("failed to validate config: %w\n%+v", err, cfg) 39 } 40 41 funcMap := template.FuncMap{} 42 err = bindPlugins(ctx, cfg, funcMap) 43 if err != nil { 44 return err 45 } 46 47 // if a custom Stdin is set in the config, inject it into the context now 48 ctx = datafs.ContextWithStdin(ctx, cfg.Stdin) 49 50 opts := optionsFromConfig(cfg) 51 opts.Funcs = funcMap 52 tr := NewRenderer(opts) 53 54 start := time.Now() 55 56 namer := chooseNamer(cfg, tr) 57 tmpl, err := gatherTemplates(ctx, cfg, namer) 58 Metrics.GatherDuration = time.Since(start) 59 if err != nil { 60 Metrics.Errors++ 61 return fmt.Errorf("failed to gather templates for rendering: %w", err) 62 } 63 Metrics.TemplatesGathered = len(tmpl) 64 65 err = tr.RenderTemplates(ctx, tmpl) 66 if err != nil { 67 return err 68 } 69 70 return nil 71 } 72 73 func chooseNamer(cfg *config.Config, tr *Renderer) func(context.Context, string) (string, error) { 74 if cfg.OutputMap == "" { 75 return simpleNamer(cfg.OutputDir) 76 } 77 return mappingNamer(cfg.OutputMap, tr) 78 } 79 80 func simpleNamer(outDir string) func(ctx context.Context, inPath string) (string, error) { 81 return func(_ context.Context, inPath string) (string, error) { 82 outPath := filepath.Join(outDir, inPath) 83 return filepath.Clean(outPath), nil 84 } 85 } 86 87 func mappingNamer(outMap string, tr *Renderer) func(context.Context, string) (string, error) { 88 return func(ctx context.Context, inPath string) (string, error) { 89 tcontext, err := createTmplContext(ctx, tr.tctxAliases, tr.data) 90 if err != nil { 91 return "", err 92 } 93 94 // add '.in' to the template context and preserve the original context 95 // in '.ctx' 96 tctx := &tmplctx{} 97 //nolint:gocritic 98 switch c := tcontext.(type) { 99 case *tmplctx: 100 for k, v := range *c { 101 if k != "in" && k != "ctx" { 102 (*tctx)[k] = v 103 } 104 } 105 } 106 (*tctx)["ctx"] = tcontext 107 (*tctx)["in"] = inPath 108 109 out := &bytes.Buffer{} 110 err = tr.renderTemplatesWithData(ctx, 111 []Template{{Name: "<OutputMap>", Text: outMap, Writer: out}}, tctx) 112 if err != nil { 113 return "", fmt.Errorf("failed to render outputMap with ctx %+v and inPath %s: %w", tctx, inPath, err) 114 } 115 116 return filepath.Clean(strings.TrimSpace(out.String())), nil 117 } 118 }