diff --git a/go.mod b/go.mod index d110234b..bed11b10 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/spdx/tools-golang -go 1.13 +go 1.18 require ( github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 diff --git a/spdx/v3/v3_0/README.md b/spdx/v3/v3_0/README.md new file mode 100644 index 00000000..3b728b04 --- /dev/null +++ b/spdx/v3/v3_0/README.md @@ -0,0 +1,53 @@ +# SPDX 3 Model Patterns + +This is a non-exhaustive list of some patterns we are able to use in Golang to work with an SPDX 3 data model. + +I've included some variations I think we should consider. An explanation is as follows, along with some pros/cons: + +- [embedded first](embedded_first) - lean into modifying go structs directly with exported fields, using struct embedding. +This requires using interfaces, but the interfaces are simple single-functions to get an editable embedded struct. + - pro: interfaces are simple + - pro: modifying structs is simple and idiomatic + - pro: minimal surface area: very few additional functions necessary + - con: construction is confusing -- which nesting has field X? + - con: naming is hard, need `Package` and `IPackage` + + +- [interfaces only](interfaces_only) - only export interfaces and use interfaces to interact with structs. +NOTE: this also includes a variant that setters return `error`, which could be applied to other options. +This option only exports the interface types and simple no-arg constructor functions. + - pro: idiomatic getter naming (without `Get`) + - pro: single type exported, data structs are not + - pro: minimal surface area: no extra helper functions/structs that we may decide to change later + - con: absolutely no JSON/etc. output + - con: getter/setter still not especially idiomatic + - con: duplicated code because no embedding + - con: construction is tedious and could be error-prone if referencing wrong variable name + + +- [interfaces only with constructors](interfaces_only/constructors) + - (generally same pros/cons as above), but: + - pro: much simpler construction pattern for users + - pro: single validation spot during construction (with returning `error` pattern) + - con: more structs and functions exported which cannot be changed without breaking backwards compatibility + + +- [inverted](inverted) - since we know the entire data model and it does not need to be extended, + it is possible to implement an uber-struct without the need to use interfaces at all. +I thought I'd mention this for some semblance of completeness, but this is probably a very bad idea. + - pro: single set of types exported + - pro: fairly easy to interact with existing documents + - con: easy to construct incorrectly + - con: potential unneecssary memory usage + + +- [embedded with interfaces and constructors](embedded_with_interfaces_and_constructors) - takes the approach of both +embedding and interfaces, along with constructor functions. This makes creation reasonably simple and working with +existing documents reasonably simple. + - pro: probably the most idiomatic Go of these options + - pro: simple construction + - pro: reasonably simple to work with existing documents + - pro: eliminates some code duplication seen with `interfaces_only` + - con: getter/setter still not especially idiomatic + - con: larger surface area: at least 4 exported parts for each type + - con: absolutely no JSON/etc. output diff --git a/spdx/v3/v3_0/embedded_first/model.go b/spdx/v3/v3_0/embedded_first/model.go new file mode 100644 index 00000000..00f885d3 --- /dev/null +++ b/spdx/v3/v3_0/embedded_first/model.go @@ -0,0 +1,161 @@ +package v3_0 + +import "time" + +type ElementID string + +// ----------------------- SpdxDocument ----------------------- + +type SpdxDocument struct { + ElementCollection + + NamespaceMap map[string]string +} + +type ISpdxDocument interface { + IElementCollection +} + +// ----------------------- ElementCollection ----------------------- + +type ElementCollection struct { + Element + + RootElements []IElement + Elements []IElement +} + +type IElementCollection interface { + IElement + + AsElementCollection() *ElementCollection +} + +// ----------------------- Element ----------------------- + +type Element struct { + SpdxID ElementID + Name string +} + +func (e *Element) AsElement() *Element { + return e +} + +type IElement interface { + AsElement() *Element +} + +var _ interface { + IElement +} = (*Element)(nil) + +// ----------------------- Artifact ----------------------- + +type Artifact struct { + Element + + BuiltTime time.Time +} + +func (a *Artifact) AsArtifact() *Artifact { + return a +} + +type IArtifact interface { + IElement + + AsArtifact() *Artifact +} + +var _ interface { + IArtifact +} = (*Artifact)(nil) + +// ----------------------- SoftwareArtifact ----------------------- + +type SoftwareArtifact struct { + Artifact + + CopyrightText string +} + +type ISoftwareArtifact interface { + IArtifact + + AsSoftwareArtifact() *SoftwareArtifact +} + +func (a *SoftwareArtifact) AsSoftwareArtifact() *SoftwareArtifact { + return a +} + +var _ interface { + ISoftwareArtifact +} = (*SoftwareArtifact)(nil) + +// ----------------------- Package ----------------------- + +type IPackage interface { + ISoftwareArtifact + + AsPackage() *Package +} + +type Package struct { + SoftwareArtifact + + PackageVersion string +} + +func (p *Package) AsPackage() *Package { + return p +} + +var _ interface { + IPackage +} = (*Package)(nil) + +// ----------------------- File ----------------------- + +type IFile interface { + ISoftwareArtifact + + AsFile() *File +} + +type File struct { + SoftwareArtifact + + ContentType string +} + +func (f *File) AsFile() *File { + return f +} + +var _ IPackage = (*Package)(nil) + +// ----------------------- Relationship ----------------------- + +type RelationshipType string + +type Relationship struct { + Element + + RelationshipType RelationshipType + From IElement + To []IElement +} + +type IRelationship interface { + IElement + + AsRelationship() *Relationship +} + +func (r *Relationship) AsRelationship() *Relationship { + return r +} + +var _ IRelationship = (*Relationship)(nil) diff --git a/spdx/v3/v3_0/embedded_first/model_test.go b/spdx/v3/v3_0/embedded_first/model_test.go new file mode 100644 index 00000000..82fd8e83 --- /dev/null +++ b/spdx/v3/v3_0/embedded_first/model_test.go @@ -0,0 +1,70 @@ +package v3_0 + +import ( + "fmt" + "testing" +) + +func Test_makeAnSpdxDocument(t *testing.T) { + // creating new documents: 2 packages found from 1 file with 2 relationships + + // embedded nesting is not ideal when instantiating: + pkg1 := &Package{ + SoftwareArtifact: SoftwareArtifact{ + Artifact: Artifact{ + Element: Element{ + SpdxID: "pkg-1", + Name: "pkg-1", + }, + }, + }, + PackageVersion: "1.0.0", + } + + // can just reference properties directly, once an object exists: + pkg2 := &Package{} + pkg2.SpdxID = "package-2" + pkg2.Name = "package-2" + pkg2.PackageVersion = "2.0.0" + + file1 := &File{} + file1.SpdxID = "file-1" + file1.Name = "file-1" + file1.ContentType = "text/plain" + + file1containsPkg1 := &Relationship{ + RelationshipType: "CONTAINS", + From: file1, + To: []IElement{pkg1}, + } + + pkg1dependsOnFile1 := &Relationship{ + RelationshipType: "DEPENDS_ON", + From: pkg1, + To: []IElement{pkg2}, + } + + doc := SpdxDocument{ + ElementCollection: ElementCollection{ + Element: Element{ + SpdxID: "spdx-document", + }, + Elements: []IElement{ + pkg1, + pkg2, + pkg1dependsOnFile1, + file1containsPkg1, + }, + }, + } + fmt.Printf("%#v\n", doc) + + // working with existing documents + + for _, e := range doc.Elements { + if e, ok := e.(IPackage); ok { + e.AsPackage().Name = "updated-name" + } + } + fmt.Printf("%#v\n", doc) +} diff --git a/spdx/v3/v3_0/embedded_second/model.go b/spdx/v3/v3_0/embedded_second/model.go new file mode 100644 index 00000000..b6582ea7 --- /dev/null +++ b/spdx/v3/v3_0/embedded_second/model.go @@ -0,0 +1,161 @@ +package v3_0 + +import "time" + +type ElementID string + +// ----------------------- SpdxDocumentData ----------------------- + +type SpdxDocumentData struct { + ElementCollectionData + + NamespaceMap map[string]string +} + +type SpdxDocument interface { + ElementCollection +} + +// ----------------------- ElementCollectionData ----------------------- + +type ElementCollectionData struct { + ElementData + + RootElements []Element + Elements []Element +} + +type ElementCollection interface { + Element + + AsElementCollection() *ElementCollectionData +} + +// ----------------------- ElementData ----------------------- + +type ElementData struct { + SpdxID ElementID + Name string +} + +func (e *ElementData) AsElement() *ElementData { + return e +} + +type Element interface { + AsElement() *ElementData +} + +var _ interface { + Element +} = (*ElementData)(nil) + +// ----------------------- ArtifactData ----------------------- + +type ArtifactData struct { + ElementData + + BuiltTime time.Time +} + +func (a *ArtifactData) AsArtifact() *ArtifactData { + return a +} + +type Artifact interface { + Element + + AsArtifact() *ArtifactData +} + +var _ interface { + Artifact +} = (*ArtifactData)(nil) + +// ----------------------- SoftwareArtifactData ----------------------- + +type SoftwareArtifactData struct { + ArtifactData + + CopyrightText string +} + +type ISoftwareArtifact interface { + Artifact + + AsSoftwareArtifact() *SoftwareArtifactData +} + +func (a *SoftwareArtifactData) AsSoftwareArtifact() *SoftwareArtifactData { + return a +} + +var _ interface { + ISoftwareArtifact +} = (*SoftwareArtifactData)(nil) + +// ----------------------- PackageData ----------------------- + +type Package interface { + ISoftwareArtifact + + AsPackage() *PackageData +} + +type PackageData struct { + SoftwareArtifactData + + PackageVersion string +} + +func (p *PackageData) AsPackage() *PackageData { + return p +} + +var _ interface { + Package +} = (*PackageData)(nil) + +// ----------------------- FileData ----------------------- + +type File interface { + ISoftwareArtifact + + AsFile() *FileData +} + +type FileData struct { + SoftwareArtifactData + + ContentType string +} + +func (f *FileData) AsFile() *FileData { + return f +} + +var _ Package = (*PackageData)(nil) + +// ----------------------- RelationshipData ----------------------- + +type RelationshipType string + +type RelationshipData struct { + ElementData + + RelationshipType RelationshipType + From Element + To []Element +} + +type Relationship interface { + Element + + AsRelationship() *RelationshipData +} + +func (r *RelationshipData) AsRelationship() *RelationshipData { + return r +} + +var _ Relationship = (*RelationshipData)(nil) diff --git a/spdx/v3/v3_0/embedded_second/model_test.go b/spdx/v3/v3_0/embedded_second/model_test.go new file mode 100644 index 00000000..abd1c2ab --- /dev/null +++ b/spdx/v3/v3_0/embedded_second/model_test.go @@ -0,0 +1,72 @@ +package v3_0_test + +import ( + "fmt" + "testing" + + spdx "github.com/spdx/tools-golang/spdx/v3/v3_0/embedded_second" +) + +func Test_makeAnSpdxDocument(t *testing.T) { + // creating new documents: 2 packages found from 1 file with 2 relationships + + // embedded nesting is not ideal when instantiating: + pkg1 := &spdx.PackageData{ + SoftwareArtifactData: spdx.SoftwareArtifactData{ + ArtifactData: spdx.ArtifactData{ + ElementData: spdx.ElementData{ + SpdxID: "pkg-1", + Name: "pkg-1", + }, + }, + }, + PackageVersion: "1.0.0", + } + + // can just reference properties directly, once an object exists: + pkg2 := &spdx.PackageData{} + pkg2.SpdxID = "package-2" + pkg2.Name = "package-2" + pkg2.PackageVersion = "2.0.0" + + file1 := &spdx.FileData{} + file1.SpdxID = "file-1" + file1.Name = "file-1" + file1.ContentType = "text/plain" + + file1containsPkg1 := &spdx.RelationshipData{ + RelationshipType: "CONTAINS", + From: file1, + To: []spdx.Element{pkg1}, + } + + pkg1dependsOnFile1 := &spdx.RelationshipData{ + RelationshipType: "DEPENDS_ON", + From: pkg1, + To: []spdx.Element{pkg2}, + } + + doc := spdx.SpdxDocumentData{ + ElementCollectionData: spdx.ElementCollectionData{ + ElementData: spdx.ElementData{ + SpdxID: "spdx-document", + }, + Elements: []spdx.Element{ + pkg1, + pkg2, + pkg1dependsOnFile1, + file1containsPkg1, + }, + }, + } + fmt.Printf("%#v\n", doc) + + // working with existing documents + + for _, e := range doc.Elements { + if e, ok := e.(spdx.Package); ok { + e.AsPackage().Name = "updated-name" + } + } + fmt.Printf("%#v\n", doc) +} diff --git a/spdx/v3/v3_0/embedded_with_interfaces/constructors/constructors.go b/spdx/v3/v3_0/embedded_with_interfaces/constructors/constructors.go new file mode 100644 index 00000000..e21d6845 --- /dev/null +++ b/spdx/v3/v3_0/embedded_with_interfaces/constructors/constructors.go @@ -0,0 +1,106 @@ +package v3_0 + +import ( + "errors" + "time" + + v3_0_e "github.com/spdx/tools-golang/spdx/v3/v3_0/embedded_with_interfaces" +) + +// Could add convenience creation props like: + +func NewPackage(props PackageProps) (v3_0_e.Package, error) { + out := &v3_0_e.PackageImpl{} + return out, errors.Join( + out.SetSpdxID(props.SpdxID), + out.SetName(props.Name), + out.SetBuiltTime(props.BuiltTime), + out.SetCopyrightText(props.CopyrightText), + out.SetPackageVersion(props.PackageVersion), + ) +} + +type PackageProps struct { + // Element properties + SpdxID v3_0_e.ElementID + Name string + + // Artifact properties + BuiltTime time.Time + + // SoftwareArtifact properties + CopyrightText string + + // Package properties + PackageVersion string +} + +func NewFile(props FileProps) (v3_0_e.File, error) { + out := &v3_0_e.FileImpl{} + return out, errors.Join( + out.SetSpdxID(props.SpdxID), + out.SetName(props.Name), + out.SetBuiltTime(props.BuiltTime), + out.SetCopyrightText(props.CopyrightText), + out.SetContentType(props.ContentType), + ) +} + +type FileProps struct { + // Element properties + SpdxID v3_0_e.ElementID + Name string + + // Artifact properties + BuiltTime time.Time + + // SoftwareArtifact properties + CopyrightText string + + // File properties + ContentType string +} + +func NewRelationship(props RelationshipProps) (v3_0_e.Relationship, error) { + out := &v3_0_e.RelationshipImpl{} + return out, errors.Join( + out.SetSpdxID(props.SpdxID), + out.SetName(props.Name), + out.SetRelationshipType(props.RelationshipType), + out.SetFrom(props.From), + out.SetTo(props.To), + ) +} + +type RelationshipProps struct { + // Element properties + SpdxID v3_0_e.ElementID + Name string + + // Relationship properties + RelationshipType v3_0_e.RelationshipType + From v3_0_e.Element + To []v3_0_e.Element +} + +func NewSpdxDocument(props SpdxDocumentProps) (v3_0_e.SpdxDocument, error) { + out := &v3_0_e.SpdxDocumentImpl{} + return out, errors.Join( + out.SetSpdxID(props.SpdxID), + out.SetName(props.Name), + out.SetProfileConformance(props.ProfileConformance), + out.SetRootElement(props.RootElement), + out.SetElements(props.Element), + ) +} + +type SpdxDocumentProps struct { + // Element properties + SpdxID v3_0_e.ElementID + Name string + + // ElementCollection properties + ProfileConformance v3_0_e.ProfileIdentifierType + RootElement []v3_0_e.Element + Element []v3_0_e.Element +} diff --git a/spdx/v3/v3_0/embedded_with_interfaces/constructors/constructors_test.go b/spdx/v3/v3_0/embedded_with_interfaces/constructors/constructors_test.go new file mode 100644 index 00000000..e218e8bb --- /dev/null +++ b/spdx/v3/v3_0/embedded_with_interfaces/constructors/constructors_test.go @@ -0,0 +1,62 @@ +package v3_0 + +import ( + "fmt" + "testing" + + spdx "github.com/spdx/tools-golang/spdx/v3/v3_0/embedded_with_interfaces" +) + +func Test_makeAnSpdxDocument(t *testing.T) { + // creating new documents: 2 packages found from 1 file with 2 relationships + + pkg1, _ := NewPackage(PackageProps{ + SpdxID: "package-1", + Name: "package-1", + PackageVersion: "1.0.0", + }) + + pkg2, _ := NewPackage(PackageProps{ + SpdxID: "package-2", + Name: "package-2", + PackageVersion: "2.0.0", + }) + + file1, _ := NewFile(FileProps{ + SpdxID: "file-1", + Name: "file-1", + ContentType: "text/plain", + }) + + file1containsPkg1, _ := NewRelationship(RelationshipProps{ + RelationshipType: "CONTAINS", + From: file1, + To: []spdx.Element{pkg1}, + }) + + pkg1dependsOnFile1, _ := NewRelationship(RelationshipProps{ + RelationshipType: "DEPENDS_ON", + From: pkg1, + To: []spdx.Element{pkg2}, + }) + + doc, _ := NewSpdxDocument(SpdxDocumentProps{ + SpdxID: "spdx-document", + Element: []spdx.Element{ + pkg1, + pkg2, + pkg1dependsOnFile1, + file1containsPkg1, + }, + }) + fmt.Printf("%#v\n", doc) + + // working with existing documents + + for _, e := range doc.Elements() { + if e, ok := e.(spdx.Package); ok { + _ = e.SetName("updated-name") + } + } + fmt.Printf("%#v\n", doc) +} diff --git a/spdx/v3/v3_0/embedded_with_interfaces/model.go b/spdx/v3/v3_0/embedded_with_interfaces/model.go new file mode 100644 index 00000000..6682d1c6 --- /dev/null +++ b/spdx/v3/v3_0/embedded_with_interfaces/model.go @@ -0,0 +1,277 @@ +package v3_0_e + +import "time" + +type ElementID string + +type SpdxDocument interface { + ElementCollection +} + +type SpdxDocumentImpl struct { + ElementCollectionImpl +} + +var _ SpdxDocument = (*SpdxDocumentImpl)(nil) + +// ----------------------- Element ----------------------- + +type Element interface { + SpdxID() ElementID + SetSpdxID(spdxID ElementID) error + + Name() string + SetName(name string) error +} + +type ElementImpl struct { + spdxID ElementID + name string +} + +var _ Element = (*ElementImpl)(nil) + +func (p *ElementImpl) SpdxID() ElementID { + return p.spdxID +} + +func (p *ElementImpl) SetSpdxID(id ElementID) error { + p.spdxID = id + return nil +} + +func (p *ElementImpl) Name() string { + return p.name +} + +func (p *ElementImpl) SetName(name string) error { + p.name = name + return nil +} + +// ----------------------- Artifact ----------------------- + +type Artifact interface { + Element + + BuiltTime() time.Time + SetBuiltTime(builtTime time.Time) error +} + +type ArtifactImpl struct { + ElementImpl + + builtTime time.Time +} + +var _ Artifact = (*ArtifactImpl)(nil) +var _ Element = (*ArtifactImpl)(nil) + +func (p *ArtifactImpl) BuiltTime() time.Time { + return p.builtTime +} + +func (p *ArtifactImpl) SetBuiltTime(builtTime time.Time) error { + p.builtTime = builtTime + return nil +} + +// ----------------------- SoftwareArtifact ----------------------- + +type SoftwareArtifact interface { + Artifact + + CopyrightText() string + SetCopyrightText(copyrightText string) error +} + +type SoftwareArtifactImpl struct { + ArtifactImpl + + copyrightText string +} + +var _ SoftwareArtifact = (*SoftwareArtifactImpl)(nil) +var _ Artifact = (*SoftwareArtifactImpl)(nil) +var _ Element = (*SoftwareArtifactImpl)(nil) + +func (p *SoftwareArtifactImpl) CopyrightText() string { + return p.copyrightText +} + +func (p *SoftwareArtifactImpl) SetCopyrightText(copyrightText string) error { + p.copyrightText = copyrightText + return nil +} + +// ----------------------- Package ----------------------- + +type Package interface { + SoftwareArtifact + + PackageVersion() string + SetPackageVersion(packageVersion string) error +} + +var _ Element = (Package)(nil) + +type PackageImpl struct { + SoftwareArtifactImpl + + packageVersion string +} + +var _ Package = (*PackageImpl)(nil) +var _ SoftwareArtifact = (*PackageImpl)(nil) +var _ Artifact = (*PackageImpl)(nil) +var _ Element = (*PackageImpl)(nil) + +func (p *PackageImpl) PackageVersion() string { + return p.packageVersion +} + +func (p *PackageImpl) SetPackageVersion(packageVersion string) error { + p.packageVersion = packageVersion + return nil +} + +// ----------------------- File ----------------------- + +type File interface { + SoftwareArtifact + + ContentType() string + SetContentType(contentType string) error +} + +type FileImpl struct { + SoftwareArtifactImpl + + // File properties + contentType string +} + +var _ File = (*FileImpl)(nil) +var _ SoftwareArtifact = (*FileImpl)(nil) +var _ Artifact = (*FileImpl)(nil) +var _ Element = (*FileImpl)(nil) + +func (p *FileImpl) ContentType() string { + return p.contentType +} + +func (p *FileImpl) SetContentType(contentType string) error { + p.contentType = contentType + return nil +} + +// ----------------------- ElementCollection ----------------------- + +type ProfileIdentifierType string + +type ElementCollection interface { + Element + + ProfileConformance() ProfileIdentifierType + SetProfileConformance(profileConformance ProfileIdentifierType) error + + RootElement() []Element + SetRootElement(element []Element) error + + Elements() []Element + SetElements(elements []Element) error +} + +type ElementCollectionImpl struct { + ElementImpl + + // ElementCollection properties + profileConformance ProfileIdentifierType + rootElement []Element + elements []Element +} + +var _ ElementCollection = (*ElementCollectionImpl)(nil) +var _ Element = (*ElementCollectionImpl)(nil) + +func (p *ElementCollectionImpl) ProfileConformance() ProfileIdentifierType { + return p.profileConformance +} + +func (p *ElementCollectionImpl) SetProfileConformance(profileConformance ProfileIdentifierType) error { + p.profileConformance = profileConformance + return nil +} + +func (e *ElementCollectionImpl) RootElement() []Element { + return e.rootElement +} + +func (e *ElementCollectionImpl) SetRootElement(rootElement []Element) error { + e.rootElement = rootElement + return nil +} + +func (e *ElementCollectionImpl) Elements() []Element { + return e.elements +} + +func (e *ElementCollectionImpl) SetElements(elements []Element) error { + e.elements = elements + return nil +} + +// ----------------------- Relationship ----------------------- + +type RelationshipType string + +type Relationship interface { + Element + + RelationshipType() RelationshipType + SetRelationshipType(relationshipType RelationshipType) error + + From() Element + SetFrom(element Element) error + + To() []Element + SetTo(element []Element) error +} + +type RelationshipImpl struct { + ElementImpl + + relationshipType RelationshipType + from Element + to []Element +} + +var _ Relationship = (*RelationshipImpl)(nil) +var _ Element = (*RelationshipImpl)(nil) + +func (e *RelationshipImpl) RelationshipType() RelationshipType { + return e.relationshipType +} + +func (e *RelationshipImpl) SetRelationshipType(relationshipType RelationshipType) error { + e.relationshipType = relationshipType + return nil +} + +func (e *RelationshipImpl) From() Element { + return e.from +} + +func (e *RelationshipImpl) SetFrom(from Element) error { + e.from = from + return nil +} + +func (e *RelationshipImpl) To() []Element { + return e.to +} + +func (e *RelationshipImpl) SetTo(to []Element) error { + e.to = to + return nil +} diff --git a/spdx/v3/v3_0/embedded_with_interfaces/model_test.go b/spdx/v3/v3_0/embedded_with_interfaces/model_test.go new file mode 100644 index 00000000..1c4d0aa9 --- /dev/null +++ b/spdx/v3/v3_0/embedded_with_interfaces/model_test.go @@ -0,0 +1,57 @@ +package v3_0_e_test + +import ( + "fmt" + "testing" + + spdx "github.com/spdx/tools-golang/spdx/v3/v3_0/embedded_with_interfaces" +) + +func Test_makeAnSpdxDocument(t *testing.T) { + // creating new documents: 2 packages found from 1 file with 2 relationships + + // must call setters + pkg1 := &spdx.PackageImpl{} + _ = pkg1.SetSpdxID("package-1") + _ = pkg1.SetName("package-1") + _ = pkg1.SetPackageVersion("1.0.0") + + pkg2 := &spdx.PackageImpl{} + _ = pkg2.SetSpdxID("package-2") + _ = pkg2.SetName("package-2") + _ = pkg2.SetPackageVersion("2.0.0") + + file1 := &spdx.FileImpl{} + _ = file1.SetSpdxID("file-1") + _ = file1.SetName("file-1") + _ = file1.SetContentType("text/plain") + + file1containsPkg1 := &spdx.RelationshipImpl{} + _ = file1containsPkg1.SetRelationshipType("CONTAINS") + _ = file1containsPkg1.SetFrom(file1) + _ = file1containsPkg1.SetTo([]spdx.Element{pkg1}) + + pkg1dependsOnFile1 := &spdx.RelationshipImpl{} + _ = pkg1dependsOnFile1.SetRelationshipType("DEPENDS_ON") + _ = pkg1dependsOnFile1.SetFrom(pkg1) + _ = pkg1dependsOnFile1.SetTo([]spdx.Element{pkg2}) + + doc := &spdx.SpdxDocumentImpl{} + _ = doc.SetSpdxID("spdx-document") + _ = doc.SetElements([]spdx.Element{ + pkg1, + pkg2, + pkg1dependsOnFile1, + file1containsPkg1, + }) + fmt.Printf("%#v\n", doc) + + // working with existing documents + + for _, e := range doc.Elements() { + if pkg, ok := e.(spdx.Package); ok { + _ = pkg.SetName("updated-name") + } + } + fmt.Printf("%#v\n", doc) +} diff --git a/spdx/v3/v3_0/embedded_with_interfaces_and_constructors/constructors.go b/spdx/v3/v3_0/embedded_with_interfaces_and_constructors/constructors.go new file mode 100644 index 00000000..3f3578e8 --- /dev/null +++ b/spdx/v3/v3_0/embedded_with_interfaces_and_constructors/constructors.go @@ -0,0 +1,99 @@ +package v3_0 + +import ( + "time" +) + +// Could add convenience creation props like: + +func NewPackage(props PackageProps) Package { + out := &PackageImpl{} + out.SetSpdxID(props.SpdxID) + out.SetName(props.Name) + out.SetBuiltTime(props.BuiltTime) + out.SetCopyrightText(props.CopyrightText) + out.SetPackageVersion(props.PackageVersion) + return out +} + +type PackageProps struct { + // Element properties + SpdxID ElementID + Name string + + // Artifact properties + BuiltTime time.Time + + // SoftwareArtifact properties + CopyrightText string + + // Package properties + PackageVersion string +} + +func NewFile(props FileProps) File { + out := &FileImpl{} + out.SetSpdxID(props.SpdxID) + out.SetName(props.Name) + out.SetBuiltTime(props.BuiltTime) + out.SetCopyrightText(props.CopyrightText) + out.SetContentType(props.ContentType) + return out +} + +type FileProps struct { + // Element properties + SpdxID ElementID + Name string + + // Artifact properties + BuiltTime time.Time + + // SoftwareArtifact properties + CopyrightText string + + // File properties + ContentType string +} + +func NewRelationship(props RelationshipProps) Relationship { + out := &RelationshipImpl{} + out.SetSpdxID(props.SpdxID) + out.SetName(props.Name) + out.SetRelationshipType(props.RelationshipType) + out.SetFrom(props.From) + out.SetTo(props.To) + return out +} + +type RelationshipProps struct { + // Element properties + SpdxID ElementID + Name string + + // Relationship properties + RelationshipType RelationshipType + From Element + To []Element +} + +func NewSpdxDocument(props SpdxDocumentProps) SpdxDocument { + out := &SpdxDocumentImpl{} + out.SetSpdxID(props.SpdxID) + out.SetName(props.Name) + out.SetProfileConformance(props.ProfileConformance) + out.SetRootElement(props.RootElement) + out.SetElements(props.Element) + return out +} + +type SpdxDocumentProps struct { + // Element properties + SpdxID ElementID + Name string + + // ElementCollection properties + ProfileConformance ProfileIdentifierType + RootElement []Element + Element []Element +} diff --git a/spdx/v3/v3_0/embedded_with_interfaces_and_constructors/model.go b/spdx/v3/v3_0/embedded_with_interfaces_and_constructors/model.go new file mode 100644 index 00000000..8e095f00 --- /dev/null +++ b/spdx/v3/v3_0/embedded_with_interfaces_and_constructors/model.go @@ -0,0 +1,254 @@ +package v3_0 + +import "time" + +type ElementID string + +type SpdxDocument interface { + ElementCollection +} + +type SpdxDocumentImpl struct { + ElementCollectionImpl +} + +var _ SpdxDocument = (*SpdxDocumentImpl)(nil) + +// ----------------------- Element ----------------------- + +type Element interface { + SpdxID() ElementID + SetSpdxID(spdxID ElementID) + + Name() string + SetName(name string) +} + +type ElementImpl struct { + spdxID ElementID + name string +} + +var _ Element = (*ElementImpl)(nil) + +func (p *ElementImpl) SpdxID() ElementID { + return p.spdxID +} + +func (p *ElementImpl) SetSpdxID(id ElementID) { + p.spdxID = id +} + +func (p *ElementImpl) Name() string { + return p.name +} + +func (p *ElementImpl) SetName(name string) { + p.name = name +} + +// ----------------------- Artifact ----------------------- + +type Artifact interface { + Element + + BuiltTime() time.Time + SetBuiltTime(builtTime time.Time) +} + +type ArtifactImpl struct { + ElementImpl + + builtTime time.Time +} + +var _ Artifact = (*ArtifactImpl)(nil) + +func (p *ArtifactImpl) BuiltTime() time.Time { + return p.builtTime +} + +func (p *ArtifactImpl) SetBuiltTime(builtTime time.Time) { + p.builtTime = builtTime +} + +// ----------------------- SoftwareArtifact ----------------------- + +type SoftwareArtifact interface { + Artifact + + CopyrightText() string + SetCopyrightText(copyrightText string) +} + +type SoftwareArtifactImpl struct { + ArtifactImpl + + copyrightText string +} + +var _ SoftwareArtifact = (*SoftwareArtifactImpl)(nil) + +func (p *SoftwareArtifactImpl) CopyrightText() string { + return p.copyrightText +} + +func (p *SoftwareArtifactImpl) SetCopyrightText(copyrightText string) { + p.copyrightText = copyrightText +} + +// ----------------------- Package ----------------------- + +type Package interface { + SoftwareArtifact + + PackageVersion() string + SetPackageVersion(packageVersion string) +} + +var _ Element = (Package)(nil) + +type PackageImpl struct { + SoftwareArtifactImpl + + packageVersion string +} + +var _ Package = (*PackageImpl)(nil) + +func (p *PackageImpl) PackageVersion() string { + return p.packageVersion +} + +func (p *PackageImpl) SetPackageVersion(packageVersion string) { + p.packageVersion = packageVersion +} + +// ----------------------- File ----------------------- + +type File interface { + SoftwareArtifact + + ContentType() string + SetContentType(contentType string) +} + +type FileImpl struct { + SoftwareArtifactImpl + + // File properties + contentType string +} + +var _ File = (*FileImpl)(nil) + +func (p *FileImpl) ContentType() string { + return p.contentType +} + +func (p *FileImpl) SetContentType(contentType string) { + p.contentType = contentType +} + +// ----------------------- ElementCollection ----------------------- + +type ProfileIdentifierType string + +type ElementCollection interface { + Element + + ProfileConformance() ProfileIdentifierType + SetProfileConformance(profileConformance ProfileIdentifierType) + + RootElement() []Element + SetRootElement(element []Element) + + Elements() []Element + SetElements(elements []Element) +} + +type ElementCollectionImpl struct { + ElementImpl + + // ElementCollection properties + profileConformance ProfileIdentifierType + rootElement []Element + elements []Element +} + +var _ ElementCollection = (*ElementCollectionImpl)(nil) + +func (p *ElementCollectionImpl) ProfileConformance() ProfileIdentifierType { + return p.profileConformance +} + +func (p *ElementCollectionImpl) SetProfileConformance(profileConformance ProfileIdentifierType) { + p.profileConformance = profileConformance +} + +func (e *ElementCollectionImpl) RootElement() []Element { + return e.rootElement +} + +func (e *ElementCollectionImpl) SetRootElement(rootElement []Element) { + e.rootElement = rootElement +} + +func (e *ElementCollectionImpl) Elements() []Element { + return e.elements +} + +func (e *ElementCollectionImpl) SetElements(elements []Element) { + e.elements = elements +} + +// ----------------------- Relationship ----------------------- + +type RelationshipType string + +type Relationship interface { + Element + + RelationshipType() RelationshipType + SetRelationshipType(relationshipType RelationshipType) + + From() Element + SetFrom(element Element) + + To() []Element + SetTo(element []Element) +} + +type RelationshipImpl struct { + *ElementImpl + + relationshipType RelationshipType + from Element + to []Element +} + +var _ Relationship = (*RelationshipImpl)(nil) + +func (e *RelationshipImpl) RelationshipType() RelationshipType { + return e.relationshipType +} + +func (e *RelationshipImpl) SetRelationshipType(relationshipType RelationshipType) { + e.relationshipType = relationshipType +} + +func (e *RelationshipImpl) From() Element { + return e.from +} + +func (e *RelationshipImpl) SetFrom(from Element) { + e.from = from +} + +func (e *RelationshipImpl) To() []Element { + return e.to +} + +func (e *RelationshipImpl) SetTo(to []Element) { + e.to = to +} diff --git a/spdx/v3/v3_0/embedded_with_interfaces_and_constructors/usage_test.go b/spdx/v3/v3_0/embedded_with_interfaces_and_constructors/usage_test.go new file mode 100644 index 00000000..4fd2f2d8 --- /dev/null +++ b/spdx/v3/v3_0/embedded_with_interfaces_and_constructors/usage_test.go @@ -0,0 +1,62 @@ +package v3_0_test + +import ( + "fmt" + "testing" + + spdx "github.com/spdx/tools-golang/spdx/v3/v3_0/embedded_with_interfaces_and_constructors" +) + +func Test_makeAnSpdxDocument(t *testing.T) { + // creating new documents: 2 packages found from 1 file with 2 relationships + + pkg1 := spdx.NewPackage(spdx.PackageProps{ + SpdxID: "package-1", + Name: "package-1", + PackageVersion: "1.0.0", + }) + + pkg2 := spdx.NewPackage(spdx.PackageProps{ + SpdxID: "package-2", + Name: "package-2", + PackageVersion: "2.0.0", + }) + + file1 := spdx.NewFile(spdx.FileProps{ + SpdxID: "file-1", + Name: "file-1", + ContentType: "text/plain", + }) + + file1containsPkg1 := spdx.NewRelationship(spdx.RelationshipProps{ + RelationshipType: "CONTAINS", + From: file1, + To: []spdx.Element{pkg1}, + }) + + pkg1dependsOnFile1 := spdx.NewRelationship(spdx.RelationshipProps{ + RelationshipType: "DEPENDS_ON", + From: pkg1, + To: []spdx.Element{pkg2}, + }) + + doc := spdx.NewSpdxDocument(spdx.SpdxDocumentProps{ + SpdxID: "spdx-document", + Element: []spdx.Element{ + pkg1, + pkg2, + pkg1dependsOnFile1, + file1containsPkg1, + }, + }) + fmt.Printf("%#v\n", doc) + + // working with existing documents + + for _, e := range doc.Elements() { + if e, ok := e.(spdx.Package); ok { + e.SetName("updated-name") + } + } + fmt.Printf("%#v\n", doc) +} diff --git a/spdx/v3/v3_0/interfaces_only/constructors/constructors.go b/spdx/v3/v3_0/interfaces_only/constructors/constructors.go new file mode 100644 index 00000000..8821edd2 --- /dev/null +++ b/spdx/v3/v3_0/interfaces_only/constructors/constructors.go @@ -0,0 +1,106 @@ +package v3_0 + +import ( + "errors" + "time" + + v3_0 "github.com/spdx/tools-golang/spdx/v3/v3_0/interfaces_only" +) + +// Could add convenience creation props like: + +func NewPackage(props PackageProps) (v3_0.Package, error) { + out := v3_0.NewPackage() + return out, errors.Join( + out.SetSpdxID(props.SpdxID), + out.SetName(props.Name), + out.SetBuiltTime(props.BuiltTime), + out.SetCopyrightText(props.CopyrightText), + out.SetPackageVersion(props.PackageVersion), + ) +} + +type PackageProps struct { + // Element properties + SpdxID v3_0.ElementID + Name string + + // Artifact properties + BuiltTime time.Time + + // SoftwareArtifact properties + CopyrightText string + + // Package properties + PackageVersion string +} + +func NewFile(props FileProps) (v3_0.File, error) { + out := v3_0.NewFile() + return out, errors.Join( + out.SetSpdxID(props.SpdxID), + out.SetName(props.Name), + out.SetBuiltTime(props.BuiltTime), + out.SetCopyrightText(props.CopyrightText), + out.SetContentType(props.ContentType), + ) +} + +type FileProps struct { + // Element properties + SpdxID v3_0.ElementID + Name string + + // Artifact properties + BuiltTime time.Time + + // SoftwareArtifact properties + CopyrightText string + + // File properties + ContentType string +} + +func NewRelationship(props RelationshipProps) (v3_0.Relationship, error) { + out := v3_0.NewRelationship() + return out, errors.Join( + out.SetSpdxID(props.SpdxID), + out.SetName(props.Name), + out.SetRelationshipType(props.RelationshipType), + out.SetFrom(props.From), + out.SetTo(props.To), + ) +} + +type RelationshipProps struct { + // Element properties + SpdxID v3_0.ElementID + Name string + + // Relationship properties + RelationshipType v3_0.RelationshipType + From v3_0.Element + To []v3_0.Element +} + +func NewSpdxDocument(props SpdxDocumentProps) (v3_0.SpdxDocument, error) { + out := v3_0.NewSpdxDocument() + return out, errors.Join( + out.SetSpdxID(props.SpdxID), + out.SetName(props.Name), + out.SetProfileConformance(props.ProfileConformance), + out.SetRootElement(props.RootElement), + out.SetElements(props.Element), + ) +} + +type SpdxDocumentProps struct { + // Element properties + SpdxID v3_0.ElementID + Name string + + // ElementCollection properties + ProfileConformance v3_0.ProfileIdentifierType + RootElement []v3_0.Element + Element []v3_0.Element +} diff --git a/spdx/v3/v3_0/interfaces_only/constructors/constructors_test.go b/spdx/v3/v3_0/interfaces_only/constructors/constructors_test.go new file mode 100644 index 00000000..65dea33a --- /dev/null +++ b/spdx/v3/v3_0/interfaces_only/constructors/constructors_test.go @@ -0,0 +1,63 @@ +package v3_0 + +import ( + "fmt" + "testing" + + v3_0 "github.com/spdx/tools-golang/spdx/v3/v3_0/interfaces_only" +) + +func Test_makeAnSpdxDocument(t *testing.T) { + // creating new documents: 2 packages found from 1 file with 2 relationships + + // must call setters + pkg1, _ := NewPackage(PackageProps{ + SpdxID: "package-1", + Name: "package-1", + PackageVersion: "1.0.0", + }) + + pkg2, _ := NewPackage(PackageProps{ + SpdxID: "package-2", + Name: "package-2", + PackageVersion: "2.0.0", + }) + + file1, _ := NewFile(FileProps{ + SpdxID: "file-1", + Name: "file-1", + ContentType: "text/plain", + }) + + file1containsPkg1, _ := NewRelationship(RelationshipProps{ + RelationshipType: "CONTAINS", + From: file1, + To: []v3_0.Element{pkg1}, + }) + + pkg1dependsOnFile1, _ := NewRelationship(RelationshipProps{ + RelationshipType: "DEPENDS_ON", + From: pkg1, + To: []v3_0.Element{pkg2}, + }) + + doc, _ := NewSpdxDocument(SpdxDocumentProps{ + SpdxID: "spdx-document", + Element: []v3_0.Element{ + pkg1, + pkg2, + pkg1dependsOnFile1, + file1containsPkg1, + }, + }) + fmt.Printf("%#v\n", doc) + + // working with existing documents + + for _, e := range doc.Elements() { + if e, ok := e.(v3_0.Package); ok { + _ = e.SetName("updated-name") + } + } + fmt.Printf("%#v\n", doc) +} diff --git a/spdx/v3/v3_0/interfaces_only/model.go b/spdx/v3/v3_0/interfaces_only/model.go new file mode 100644 index 00000000..925e108b --- /dev/null +++ b/spdx/v3/v3_0/interfaces_only/model.go @@ -0,0 +1,342 @@ +package v3_0 + +import "time" + +type ElementID string + +type SpdxDocument interface { + ElementCollection +} + +type spdxDocument struct { + // Element properties + spdxID ElementID + name string + + // ElementCollection properties + profileConformance ProfileIdentifierType + rootElement []Element + elements []Element +} + +func (p *spdxDocument) SpdxID() ElementID { + return p.spdxID +} + +func (p *spdxDocument) SetSpdxID(id ElementID) error { + p.spdxID = id + return nil +} + +func (p *spdxDocument) Name() string { + return p.name +} + +func (p *spdxDocument) SetName(name string) error { + p.name = name + return nil +} + +func (p *spdxDocument) ProfileConformance() ProfileIdentifierType { + return p.profileConformance +} + +func (p *spdxDocument) SetProfileConformance(profileConformance ProfileIdentifierType) error { + p.profileConformance = profileConformance + return nil +} + +func (e *spdxDocument) RootElement() []Element { + return e.rootElement +} + +func (e *spdxDocument) SetRootElement(rootElement []Element) error { + e.rootElement = rootElement + return nil +} + +func (e *spdxDocument) Elements() []Element { + return e.elements +} + +func (e *spdxDocument) SetElements(elements []Element) error { + e.elements = elements + return nil +} + +func NewSpdxDocument() SpdxDocument { + return &spdxDocument{} +} + +// ----------------------- Element ----------------------- + +type Element interface { + SpdxID() ElementID + SetSpdxID(spdxID ElementID) error + + Name() string + SetName(name string) error +} + +// ----------------------- Artifact ----------------------- + +type Artifact interface { + Element + + BuiltTime() time.Time + SetBuiltTime(builtTime time.Time) error +} + +// ----------------------- SoftwareArtifact ----------------------- + +type SoftwareArtifact interface { + Artifact + + CopyrightText() string + SetCopyrightText(copyrightText string) error +} + +// ----------------------- Package ----------------------- + +type Package interface { + SoftwareArtifact + + PackageVersion() string + SetPackageVersion(packageVersion string) error +} + +// "package" is a reserved word... +type packageImpl struct { + // Element properties + spdxID ElementID + name string + + // Artifact properties + builtTime time.Time + + // SoftwareArtifact properties + copyrightText string + + // Package properties + packageVersion string +} + +func (p *packageImpl) SpdxID() ElementID { + return p.spdxID +} + +func (p *packageImpl) SetSpdxID(id ElementID) error { + p.spdxID = id + return nil +} + +func (p *packageImpl) Name() string { + return p.name +} + +func (p *packageImpl) SetName(name string) error { + p.name = name + return nil +} + +func (p *packageImpl) BuiltTime() time.Time { + return p.builtTime +} + +func (p *packageImpl) SetBuiltTime(builtTime time.Time) error { + p.builtTime = builtTime + return nil +} + +func (p *packageImpl) CopyrightText() string { + return p.copyrightText +} + +func (p *packageImpl) SetCopyrightText(copyrightText string) error { + p.copyrightText = copyrightText + return nil +} + +func (p *packageImpl) PackageVersion() string { + return p.packageVersion +} + +func (p *packageImpl) SetPackageVersion(packageVersion string) error { + p.packageVersion = packageVersion + return nil +} + +func NewPackage() Package { + return &packageImpl{} +} + +var _ Package = (*packageImpl)(nil) + +// ----------------------- File ----------------------- + +type File interface { + SoftwareArtifact + + ContentType() string + SetContentType(contentType string) error +} + +type file struct { + // Element properties + spdxID ElementID + name string + + // Artifact properties + builtTime time.Time + + // SoftwareArtifact properties + copyrightText string + + // File properties + contentType string +} + +func (p *file) SpdxID() ElementID { + return p.spdxID +} + +func (p *file) SetSpdxID(id ElementID) error { + p.spdxID = id + return nil +} + +func (p *file) Name() string { + return p.name +} + +func (p *file) SetName(name string) error { + p.name = name + return nil +} + +func (p *file) BuiltTime() time.Time { + return p.builtTime +} + +func (p *file) SetBuiltTime(builtTime time.Time) error { + p.builtTime = builtTime + return nil +} + +func (p *file) CopyrightText() string { + return p.copyrightText +} + +func (p *file) SetCopyrightText(copyrightText string) error { + p.copyrightText = copyrightText + return nil +} + +func (p *file) ContentType() string { + return p.contentType +} + +func (p *file) SetContentType(contentType string) error { + p.contentType = contentType + return nil +} + +func NewFile() File { + return &file{} +} + +var _ File = (*file)(nil) + +// ----------------------- ElementCollection ----------------------- + +type ProfileIdentifierType string + +type ElementCollection interface { + Element + + ProfileConformance() ProfileIdentifierType + SetProfileConformance(profileConformance ProfileIdentifierType) error + + RootElement() []Element + SetRootElement(element []Element) error + + Elements() []Element + SetElements(elements []Element) error +} + +// ----------------------- Relationship ----------------------- + +type RelationshipType string + +type Relationship interface { + Element + + RelationshipType() RelationshipType + SetRelationshipType(relationshipType RelationshipType) error + + From() Element + SetFrom(element Element) error + + To() []Element + SetTo(element []Element) error +} + +func NewRelationship() Relationship { + return &relationship{} +} + +type relationship struct { + // Element properties + spdxID ElementID + name string + + // Relationship properties + relationshipType RelationshipType + from Element + to []Element +} + +func (p *relationship) SpdxID() ElementID { + return p.spdxID +} + +func (p *relationship) SetSpdxID(id ElementID) error { + p.spdxID = id + return nil +} + +func (p *relationship) Name() string { + return p.name +} + +func (p *relationship) SetName(name string) error { + p.name = name + return nil +} + +func (e *relationship) RelationshipType() RelationshipType { + return e.relationshipType +} + +func (e *relationship) SetRelationshipType(relationshipType RelationshipType) error { + e.relationshipType = relationshipType + return nil +} + +func (e *relationship) From() Element { + return e.from +} + +func (e *relationship) SetFrom(from Element) error { + e.from = from + return nil +} + +func (e *relationship) To() []Element { + return e.to +} + +func (e *relationship) SetTo(to []Element) error { + e.to = to + return nil +} diff --git a/spdx/v3/v3_0/interfaces_only/model_test.go b/spdx/v3/v3_0/interfaces_only/model_test.go new file mode 100644 index 00000000..752b75ca --- /dev/null +++ b/spdx/v3/v3_0/interfaces_only/model_test.go @@ -0,0 +1,55 @@ +package v3_0 + +import ( + "fmt" + "testing" +) + +func Test_makeAnSpdxDocument(t *testing.T) { + // creating new documents: 2 packages found from 1 file with 2 relationships + + // must call setters + pkg1 := NewPackage() + _ = pkg1.SetSpdxID("package-1") + _ = pkg1.SetName("package-1") + _ = pkg1.SetPackageVersion("1.0.0") + + pkg2 := NewPackage() + _ = pkg2.SetSpdxID("package-2") + _ = pkg2.SetName("package-2") + _ = pkg2.SetPackageVersion("2.0.0") + + file1 := NewFile() + _ = file1.SetSpdxID("file-1") + _ = file1.SetName("file-1") + _ = file1.SetContentType("text/plain") + + file1containsPkg1 := NewRelationship() + _ = file1containsPkg1.SetRelationshipType("CONTAINS") + _ = file1containsPkg1.SetFrom(file1) + _ = file1containsPkg1.SetTo([]Element{pkg1}) + + pkg1dependsOnFile1 := NewRelationship() + _ = pkg1dependsOnFile1.SetRelationshipType("DEPENDS_ON") + _ = pkg1dependsOnFile1.SetFrom(pkg1) + _ = pkg1dependsOnFile1.SetTo([]Element{pkg2}) + + doc := NewSpdxDocument() + _ = doc.SetSpdxID("spdx-document") + _ = doc.SetElements([]Element{ + pkg1, + pkg2, + pkg1dependsOnFile1, + file1containsPkg1, + }) + fmt.Printf("%#v\n", doc) + + // working with existing documents + + for _, e := range doc.Elements() { + if e, ok := e.(Package); ok { + _ = e.SetName("updated-name") + } + } + fmt.Printf("%#v\n", doc) +} diff --git a/spdx/v3/v3_0/inverted/model.go b/spdx/v3/v3_0/inverted/model.go new file mode 100644 index 00000000..a1cca98e --- /dev/null +++ b/spdx/v3/v3_0/inverted/model.go @@ -0,0 +1,137 @@ +package v3_0 + +import "time" + +type ElementID string + +type SpdxDocument struct { +} + +func NewSpdxDocument(p SpdxDocumentProps) *Element { + return &Element{ + SpdxID: p.SpdxID, + Name: p.Name, + SpdxDocument: &SpdxDocument{}, + ElementCollection: &ElementCollection{ + RootElement: p.RootElement, + Elements: p.Elements, + }, + } +} + +type SpdxDocumentProps struct { + SpdxID ElementID + Name string + RootElement []*Element + Elements []*Element +} + +// ----------------------- Element ----------------------- + +type Element struct { + *ElementCollection + *SpdxDocument + *Artifact + *SoftwareArtifact + *Package + *File + *Relationship + + SpdxID ElementID + Name string +} + +// ----------------------- Artifact ----------------------- + +type Artifact struct { + BuiltTime time.Time +} + +// ----------------------- SoftwareArtifact ----------------------- + +type SoftwareArtifact struct { + CopyrightText string +} + +// ----------------------- Package ----------------------- + +type Package struct { + PackageVersion string +} + +func NewPackage(p PackageProps) *Element { + return &Element{ + SpdxID: p.SpdxID, + Name: p.Name, + Artifact: &Artifact{}, + SoftwareArtifact: &SoftwareArtifact{}, + Package: &Package{ + PackageVersion: p.PackageVersion, + }, + } +} + +type PackageProps struct { + SpdxID ElementID + Name string + PackageVersion string +} + +// ----------------------- File ----------------------- + +type File struct { + ContentType string +} + +func NewFile(p FileProps) *Element { + return &Element{ + SpdxID: p.SpdxID, + Name: p.Name, + Artifact: &Artifact{}, + SoftwareArtifact: &SoftwareArtifact{}, + File: &File{ + ContentType: p.ContentType, + }, + } +} + +type FileProps struct { + SpdxID ElementID + Name string + ContentType string +} + +// ----------------------- Relationship ----------------------- + +type Relationship struct { + RelationshipType string + From *Element + To []*Element +} + +func NewRelationship(p RelationshipProps) *Element { + return &Element{ + SpdxID: p.SpdxID, + Name: p.Name, + Relationship: &Relationship{ + RelationshipType: p.RelationshipType, + From: p.From, + To: p.To, + }, + } +} + +type RelationshipProps struct { + SpdxID ElementID + Name string + RelationshipType string + From *Element + To []*Element +} + +// ----------------------- ElementCollection ----------------------- + +type ElementCollection struct { + RootElement []*Element + Elements []*Element +} diff --git a/spdx/v3/v3_0/inverted/model_test.go b/spdx/v3/v3_0/inverted/model_test.go new file mode 100644 index 00000000..7f3753a4 --- /dev/null +++ b/spdx/v3/v3_0/inverted/model_test.go @@ -0,0 +1,64 @@ +package v3_0 + +import ( + "fmt" + "testing" +) + +func Test_model(t *testing.T) { + // creating new documents: 2 packages found from 1 file with 2 relationships + + pkg1 := &Element{ + SpdxID: "package-1", + Name: "package-1", + Artifact: &Artifact{}, // how would a user know to do this? + SoftwareArtifact: &SoftwareArtifact{}, + Package: &Package{ + PackageVersion: "1.0.0", + }, + } + + pkg2 := NewPackage(PackageProps{ + SpdxID: "package-2", + Name: "package-2", + PackageVersion: "2.0.0", + }) + + file1 := NewFile(FileProps{ + SpdxID: "file-2", + Name: "file-2", + ContentType: "text/plain", + }) + + file1containsPkg1 := NewRelationship(RelationshipProps{ + RelationshipType: "CONTAINS", + From: file1, + To: []*Element{pkg1}, + }) + + pkg1dependsOnFile1 := NewRelationship(RelationshipProps{ + RelationshipType: "DEPENDS_ON", + From: pkg1, + To: []*Element{pkg2}, + }) + + doc := NewSpdxDocument(SpdxDocumentProps{ + SpdxID: "spdx-document", + Elements: []*Element{ + pkg1, + pkg2, + pkg1dependsOnFile1, + file1containsPkg1, + }, + }) + fmt.Printf("%#v\n", doc) + + // working with existing documents + + for _, e := range doc.Elements { + if p := e.Package; p != nil { + e.Name = "updated-name" + } + } + fmt.Printf("%#v\n", doc) +}