github.com/tooploox/oya@v0.0.21-0.20230524103240-1cda1861aad6/cmd/internal/render.go (about) 1 package internal 2 3 import ( 4 "fmt" 5 "io" 6 "path/filepath" 7 8 "github.com/tooploox/oya/pkg/project" 9 "github.com/tooploox/oya/pkg/template" 10 ) 11 12 type ErrNoScope struct { 13 Scope string 14 OyafilePath string 15 } 16 17 func (err ErrNoScope) Error() string { 18 return fmt.Sprintf("Scope not found in %v: %q missing or cannot be used as a scope", err.OyafilePath, err.Scope) 19 } 20 21 func Render(oyafilePath, templatePath string, excludedPaths []string, outputPath string, 22 autoScope bool, scopePath string, overrides map[string]interface{}, delimitersStr string, 23 stdout, stderr io.Writer) error { 24 installDir, err := installDir() 25 if err != nil { 26 return err 27 } 28 oyafileFullPath, err := filepath.Abs(oyafilePath) 29 if err != nil { 30 return err 31 } 32 33 proj, err := project.Detect(oyafileFullPath, installDir) 34 if err != nil { 35 return err 36 } 37 38 o, found, err := proj.Oyafile(oyafilePath) 39 if err != nil { 40 return err 41 } 42 43 dt, err := proj.Deps() 44 if err != nil { 45 return err 46 } 47 48 err = o.Build(dt) 49 if err != nil { 50 return err 51 } 52 53 var values template.Scope 54 if found { 55 if autoScope && scopePath == "" { 56 scopePath, _ = lookupOyaScope() 57 } 58 if scopePath != "" { 59 values, err = o.Values.GetScopeAt(scopePath) 60 } else { 61 values = o.Values 62 } 63 if err != nil { 64 // BUG(bilus): Ignoring err. 65 return ErrNoScope{Scope: scopePath, OyafilePath: oyafilePath} 66 } 67 } 68 69 err = overrideValues(values, overrides) 70 if err != nil { 71 return err 72 } 73 74 delimiters, err := template.ParseDelimiters(delimitersStr) 75 if err != nil { 76 return err 77 } 78 79 return template.RenderAll(templatePath, excludedPaths, outputPath, values, delimiters) 80 } 81 82 func overrideValues(values template.Scope, overrides map[string]interface{}) error { 83 for path, val := range overrides { 84 // Force overriding existing paths that have different "shapes". 85 // What AssocAt does is it will create scopes along the path if they don't exist. 86 // If an intermediate path does exist and is a simple value (not a Scope), it will 87 // force it's conversion to a scope, thus losing the original value. 88 // For example: 89 // values: {"foo": "xxx"} 90 // overrides: foo.bar="yyy" 91 // result: {"foo": {"bar": "yyy"}} 92 // With force set to false, the function would fail for the input ^. 93 force := true 94 err := values.AssocAt(path, val, force) 95 if err != nil { 96 return err 97 } 98 } 99 return nil 100 }