github.com/lineaje-labs/syft@v0.98.1-0.20231227153149-9e393f60ff1b/cmd/syft/cli/options/format.go (about) 1 package options 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/go-multierror" 7 8 "github.com/anchore/clio" 9 "github.com/anchore/syft/syft/format/cyclonedxjson" 10 "github.com/anchore/syft/syft/format/cyclonedxxml" 11 "github.com/anchore/syft/syft/format/github" 12 "github.com/anchore/syft/syft/format/spdxjson" 13 "github.com/anchore/syft/syft/format/spdxtagvalue" 14 "github.com/anchore/syft/syft/format/syftjson" 15 "github.com/anchore/syft/syft/format/table" 16 "github.com/anchore/syft/syft/format/template" 17 "github.com/anchore/syft/syft/format/text" 18 "github.com/anchore/syft/syft/sbom" 19 ) 20 21 var _ clio.PostLoader = (*Format)(nil) 22 23 // Format contains all user configuration for output formatting. 24 type Format struct { 25 Pretty *bool `yaml:"pretty" json:"pretty" mapstructure:"pretty"` 26 Template FormatTemplate `yaml:"template" json:"template" mapstructure:"template"` 27 SyftJSON FormatSyftJSON `yaml:"json" json:"json" mapstructure:"json"` 28 SPDXJSON FormatSPDXJSON `yaml:"spdx-json" json:"spdx-json" mapstructure:"spdx-json"` 29 CyclonedxJSON FormatCyclonedxJSON `yaml:"cyclonedx-json" json:"cyclonedx-json" mapstructure:"cyclonedx-json"` 30 CyclonedxXML FormatCyclonedxXML `yaml:"cyclonedx-xml" json:"cyclonedx-xml" mapstructure:"cyclonedx-xml"` 31 } 32 33 func (o *Format) PostLoad() error { 34 o.SyftJSON.Pretty = multiLevelOption[bool](false, o.Pretty, o.SyftJSON.Pretty) 35 o.SPDXJSON.Pretty = multiLevelOption[bool](false, o.Pretty, o.SPDXJSON.Pretty) 36 o.CyclonedxJSON.Pretty = multiLevelOption[bool](false, o.Pretty, o.CyclonedxJSON.Pretty) 37 o.CyclonedxXML.Pretty = multiLevelOption[bool](false, o.Pretty, o.CyclonedxXML.Pretty) 38 39 return nil 40 } 41 42 func DefaultFormat() Format { 43 return Format{ 44 Template: DefaultFormatTemplate(), 45 SyftJSON: DefaultFormatJSON(), 46 SPDXJSON: DefaultFormatSPDXJSON(), 47 CyclonedxJSON: DefaultFormatCyclonedxJSON(), 48 CyclonedxXML: DefaultFormatCyclonedxXML(), 49 } 50 } 51 52 func (o *Format) Encoders() ([]sbom.FormatEncoder, error) { 53 // setup all encoders based on the configuration 54 var list encoderList 55 56 // in the future there will be application configuration options that can be used to set the default output format 57 list.addWithErr(template.ID)(o.Template.formatEncoders()) 58 list.addWithErr(syftjson.ID)(o.SyftJSON.formatEncoders()) 59 list.add(table.ID)(table.NewFormatEncoder()) 60 list.add(text.ID)(text.NewFormatEncoder()) 61 list.add(github.ID)(github.NewFormatEncoder()) 62 list.addWithErr(cyclonedxxml.ID)(o.CyclonedxXML.formatEncoders()) 63 list.addWithErr(cyclonedxjson.ID)(o.CyclonedxJSON.formatEncoders()) 64 list.addWithErr(spdxjson.ID)(o.SPDXJSON.formatEncoders()) 65 list.addWithErr(spdxtagvalue.ID)(spdxTagValueEncoders()) 66 67 return list.encoders, list.err 68 } 69 70 // TODO: when application configuration is made for this format then this should be ported to the options object 71 // that is created for that configuration (as done with the template output option) 72 func spdxTagValueEncoders() ([]sbom.FormatEncoder, error) { 73 var ( 74 encs []sbom.FormatEncoder 75 errs error 76 ) 77 for _, v := range spdxtagvalue.SupportedVersions() { 78 enc, err := spdxtagvalue.NewFormatEncoderWithConfig(spdxtagvalue.EncoderConfig{Version: v}) 79 if err != nil { 80 errs = multierror.Append(errs, err) 81 } else { 82 encs = append(encs, enc) 83 } 84 } 85 return encs, errs 86 } 87 88 type encoderList struct { 89 encoders []sbom.FormatEncoder 90 err error 91 } 92 93 func (l *encoderList) addWithErr(name sbom.FormatID) func([]sbom.FormatEncoder, error) { 94 return func(encs []sbom.FormatEncoder, err error) { 95 if err != nil { 96 l.err = multierror.Append(l.err, fmt.Errorf("unable to configure %q format encoder: %w", name, err)) 97 return 98 } 99 for _, enc := range encs { 100 if enc == nil { 101 l.err = multierror.Append(l.err, fmt.Errorf("unable to configure %q format encoder: nil encoder returned", name)) 102 continue 103 } 104 l.encoders = append(l.encoders, enc) 105 } 106 } 107 } 108 109 func (l *encoderList) add(name sbom.FormatID) func(...sbom.FormatEncoder) { 110 return func(encs ...sbom.FormatEncoder) { 111 for _, enc := range encs { 112 if enc == nil { 113 l.err = multierror.Append(l.err, fmt.Errorf("unable to configure %q format encoder: nil encoder returned", name)) 114 continue 115 } 116 l.encoders = append(l.encoders, enc) 117 } 118 } 119 } 120 121 func multiLevelOption[T any](defaultValue T, option ...*T) *T { 122 result := defaultValue 123 for _, opt := range option { 124 if opt != nil { 125 result = *opt 126 } 127 } 128 return &result 129 }