github.com/lineaje-labs/syft@v0.98.1-0.20231227153149-9e393f60ff1b/syft/format/template/encoder.go (about) 1 package template 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "os" 8 "reflect" 9 "text/template" 10 11 "github.com/Masterminds/sprig/v3" 12 "github.com/mitchellh/go-homedir" 13 14 "github.com/anchore/syft/syft/format/syftjson" 15 "github.com/anchore/syft/syft/sbom" 16 ) 17 18 const ID sbom.FormatID = "template" 19 20 type EncoderConfig struct { 21 TemplatePath string 22 syftjson.EncoderConfig 23 } 24 25 type encoder struct { 26 cfg EncoderConfig 27 funcMap template.FuncMap 28 } 29 30 func NewFormatEncoder(cfg EncoderConfig) (sbom.FormatEncoder, error) { 31 // TODO: revisit this... should no template file be an error or simply render an empty result? or render the json output? 32 // Note: do not check for the existence of the template file here, as the default encoder cannot provide one. 33 f := sprig.HermeticTxtFuncMap() 34 f["getLastIndex"] = func(collection interface{}) int { 35 if v := reflect.ValueOf(collection); v.Kind() == reflect.Slice { 36 return v.Len() - 1 37 } 38 39 return 0 40 } 41 // Checks if a field is defined 42 f["hasField"] = func(obj interface{}, field string) bool { 43 t := reflect.TypeOf(obj) 44 _, ok := t.FieldByName(field) 45 return ok 46 } 47 48 return encoder{ 49 cfg: cfg, 50 // These are custom functions available to template authors. 51 funcMap: f, 52 }, nil 53 } 54 55 func DefaultEncoderConfig() EncoderConfig { 56 return EncoderConfig{ 57 EncoderConfig: syftjson.DefaultEncoderConfig(), 58 } 59 } 60 61 func (e encoder) ID() sbom.FormatID { 62 return ID 63 } 64 65 func (e encoder) Aliases() []string { 66 return []string{} 67 } 68 69 func (e encoder) Version() string { 70 return sbom.AnyVersion 71 } 72 73 func (e encoder) Encode(writer io.Writer, s sbom.SBOM) error { 74 if e.cfg.TemplatePath == "" { 75 return errors.New("no template file provided") 76 } 77 78 templatePath, err := homedir.Expand(e.cfg.TemplatePath) 79 if err != nil { 80 return fmt.Errorf("unable to expand path %s", e.cfg.TemplatePath) 81 } 82 83 templateContents, err := os.ReadFile(templatePath) 84 if err != nil { 85 return fmt.Errorf("unable to get template content: %w", err) 86 } 87 88 tmpl, err := template.New(templatePath).Funcs(e.funcMap).Parse(string(templateContents)) 89 if err != nil { 90 return fmt.Errorf("unable to parse template: %w", err) 91 } 92 93 doc := syftjson.ToFormatModel(s, e.cfg.EncoderConfig) 94 return tmpl.Execute(writer, doc) 95 }