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  }