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