github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/syft/format/decoders_collection.go (about) 1 package format 2 3 import ( 4 "fmt" 5 "io" 6 7 "github.com/anchore/syft/internal/log" 8 "github.com/anchore/syft/syft/format/internal/stream" 9 "github.com/anchore/syft/syft/sbom" 10 ) 11 12 var _ sbom.FormatDecoder = (*DecoderCollection)(nil) 13 14 type DecoderCollection struct { 15 decoders []sbom.FormatDecoder 16 } 17 18 func NewDecoderCollection(decoders ...sbom.FormatDecoder) sbom.FormatDecoder { 19 return &DecoderCollection{ 20 decoders: decoders, 21 } 22 } 23 24 // Decode takes a set of bytes and attempts to decode it into an SBOM relative to the decoders in the collection. 25 func (c *DecoderCollection) Decode(r io.Reader) (*sbom.SBOM, sbom.FormatID, string, error) { 26 if r == nil { 27 return nil, "", "", fmt.Errorf("no SBOM bytes provided") 28 } 29 30 reader, err := stream.SeekableReader(r) 31 if err != nil { 32 return nil, "", "", fmt.Errorf("unable to create a seekable reader: %w", err) 33 } 34 35 var bestID sbom.FormatID 36 for _, d := range c.decoders { 37 id, version := d.Identify(reader) 38 if id == "" || version == "" { 39 if id != "" { 40 bestID = id 41 } 42 continue 43 } 44 45 return d.Decode(reader) 46 } 47 48 if bestID != "" { 49 return nil, bestID, "", fmt.Errorf("sbom format found to be %q but the version is not supported", bestID) 50 } 51 52 return nil, "", "", fmt.Errorf("sbom format not recognized") 53 } 54 55 // Identify takes a set of bytes and attempts to identify the format of the SBOM relative to the decoders in the collection. 56 func (c *DecoderCollection) Identify(r io.Reader) (sbom.FormatID, string) { 57 if r == nil { 58 return "", "" 59 } 60 61 reader, err := stream.SeekableReader(r) 62 if err != nil { 63 log.Debugf("unable to create a seekable reader: %v", err) 64 return "", "" 65 } 66 67 for _, d := range c.decoders { 68 id, version := d.Identify(reader) 69 if id != "" && version != "" { 70 return id, version 71 } 72 } 73 return "", "" 74 }