diff --git a/syft/format/decoder.go b/syft/format/decoder.go new file mode 100644 index 000000000..149e36400 --- /dev/null +++ b/syft/format/decoder.go @@ -0,0 +1,12 @@ +package format + +import ( + "io" + + "github.com/anchore/syft/syft/distro" + + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" +) + +type Decoder func(reader io.Reader) (*pkg.Catalog, *source.Metadata, *distro.Distro, error) diff --git a/syft/format/encoder.go b/syft/format/encoder.go new file mode 100644 index 000000000..46e1d3b75 --- /dev/null +++ b/syft/format/encoder.go @@ -0,0 +1,22 @@ +package format + +import ( + "io" + + "github.com/anchore/syft/syft/distro" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" +) + +// TODO: this should probably be a helper function to return the bytes +//func using() { +// var buff bytes.Buffer +// var encoder Encoder // = ... +// +// _:=encoder(&buff, nil, nil, nil) +// +// bytes.NewReader(buff.Bytes()) +// +//} + +type Encoder func(io.Writer, *pkg.Catalog, *source.Metadata, *distro.Distro) error diff --git a/syft/format/format.go b/syft/format/format.go new file mode 100644 index 000000000..6bb12bf11 --- /dev/null +++ b/syft/format/format.go @@ -0,0 +1,65 @@ +package format + +import ( + "bytes" + "errors" + "io" + + "github.com/anchore/syft/syft/distro" + + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" +) + +var ( + ErrEncodingNotSupported = errors.New("encoding not supported") + ErrDecodingNotSupported = errors.New("decoding not supported") +) + +type Format struct { + Option Option + encoder Encoder + decoder Decoder + validator Validator +} + +func NewFormat(option Option, encoder Encoder, decoder Decoder, validator Validator) Format { + return Format{ + Option: option, + encoder: encoder, + decoder: decoder, + validator: validator, + } +} + +func (f Format) Encode(output io.Writer, catalog *pkg.Catalog, d *distro.Distro, metadata *source.Metadata) error { + if f.encoder == nil { + return ErrEncodingNotSupported + } + return f.encoder(output, catalog, metadata, d) +} + +func (f Format) Decode(reader io.Reader) (*pkg.Catalog, *source.Metadata, *distro.Distro, error) { + if f.decoder == nil { + return nil, nil, nil, ErrDecodingNotSupported + } + return f.decoder(reader) +} + +func (f Format) Detect(b []byte) bool { + if f.validator == nil { + return false + } + + if err := f.validator(bytes.NewReader(b)); err != nil { + return false + } + return true +} + +func (f Format) Presenter(catalog *pkg.Catalog, metadata *source.Metadata, d *distro.Distro) *Presenter { + if f.encoder == nil { + return nil + } + return NewPresenter(f.encoder, catalog, metadata, d) +} diff --git a/syft/format/option.go b/syft/format/option.go new file mode 100644 index 000000000..805656c11 --- /dev/null +++ b/syft/format/option.go @@ -0,0 +1,43 @@ +package format + +import "strings" + +const ( + UnknownOption Option = "UnknownFormatOption" + JSONOption Option = "json" + TextOption Option = "text" + TableOption Option = "table" + CycloneDxOption Option = "cyclonedx" + SPDXTagValueOption Option = "spdx-tag-value" + SPDXJSONOption Option = "spdx-json" +) + +var AllOptions = []Option{ + JSONOption, + TextOption, + TableOption, + CycloneDxOption, + SPDXTagValueOption, + SPDXJSONOption, +} + +type Option string + +func ParseOption(userStr string) Option { + switch strings.ToLower(userStr) { + case string(JSONOption), "syft-json": + return JSONOption + case string(TextOption): + return TextOption + case string(TableOption): + return TableOption + case string(CycloneDxOption), "cyclone", "cyclone-dx": + return CycloneDxOption + case string(SPDXTagValueOption), "spdx", "spdx-tagvalue", "spdxtagvalue", "spdx-tv", "spdxtv": + return SPDXTagValueOption + case string(SPDXJSONOption), "spdxjson": + return SPDXJSONOption + default: + return UnknownOption + } +} diff --git a/syft/format/presenter.go b/syft/format/presenter.go new file mode 100644 index 000000000..3915f3cb7 --- /dev/null +++ b/syft/format/presenter.go @@ -0,0 +1,30 @@ +package format + +import ( + "io" + + "github.com/anchore/syft/syft/distro" + + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" +) + +type Presenter struct { + catalog *pkg.Catalog + srcMetadata *source.Metadata + distro *distro.Distro + encoder Encoder +} + +func NewPresenter(encoder Encoder, catalog *pkg.Catalog, srcMetadata *source.Metadata, d *distro.Distro) *Presenter { + return &Presenter{ + catalog: catalog, + srcMetadata: srcMetadata, + distro: d, + encoder: encoder, + } +} + +func (pres *Presenter) Present(output io.Writer) error { + return pres.encoder(output, pres.catalog, pres.srcMetadata, pres.distro) +} diff --git a/syft/format/validator.go b/syft/format/validator.go new file mode 100644 index 000000000..59269e271 --- /dev/null +++ b/syft/format/validator.go @@ -0,0 +1,5 @@ +package format + +import "io" + +type Validator func(reader io.Reader) error