github.com/anchore/syft@v1.38.2/syft/pkg/license_set.go (about) 1 package pkg 2 3 import ( 4 "fmt" 5 "sort" 6 7 "github.com/gohugoio/hashstructure" 8 9 "github.com/anchore/syft/internal/log" 10 "github.com/anchore/syft/syft/artifact" 11 ) 12 13 type LicenseSet struct { 14 set map[artifact.ID]License 15 } 16 17 func NewLicenseSet(licenses ...License) (s LicenseSet) { 18 for _, l := range licenses { 19 s.Add(l) 20 } 21 22 return s 23 } 24 25 func (s *LicenseSet) addToExisting(license License) (id artifact.ID, merged bool, err error) { 26 id, err = artifact.IDByHash(license) 27 if err != nil { 28 return id, false, fmt.Errorf("could not get the hash for a license: %w", err) 29 } 30 31 v, ok := s.set[id] 32 if !ok { 33 // doesn't exist safe to add 34 return id, false, nil 35 } 36 37 // we got the same id; we want to merge the URLs and Location data 38 // URLs/Location are not considered when taking the Hash 39 m, err := v.Merge(license) 40 if err != nil { 41 return id, false, fmt.Errorf("could not merge license into map: %w", err) 42 } 43 s.set[id] = *m 44 45 return id, true, nil 46 } 47 48 func (s *LicenseSet) Add(licenses ...License) { 49 if s.set == nil { 50 s.set = make(map[artifact.ID]License) 51 } 52 for _, l := range licenses { 53 // we only want to add licenses that are not empty 54 if l.Empty() { 55 continue 56 } 57 // note, this check should be moved to the license constructor in the future 58 if id, merged, err := s.addToExisting(l); err == nil && !merged { 59 // doesn't exist, add it 60 s.set[id] = l 61 } else if err != nil { 62 log.WithFields("error", err, "license", l).Trace("failed to add license to license set") 63 } 64 } 65 } 66 67 func (s LicenseSet) ToSlice(sorters ...func(a, b License) int) []License { 68 licenses := s.ToUnorderedSlice() 69 70 var sorted bool 71 for _, sorter := range sorters { 72 if sorter == nil { 73 continue 74 } 75 sort.Slice(licenses, func(i, j int) bool { 76 return sorter(licenses[i], licenses[j]) < 0 77 }) 78 sorted = true 79 break 80 } 81 82 if !sorted { 83 sort.Sort(Licenses(licenses)) 84 } 85 86 return licenses 87 } 88 89 func (s LicenseSet) ToUnorderedSlice() []License { 90 if s.set == nil { 91 return nil 92 } 93 licenses := make([]License, len(s.set)) 94 idx := 0 95 for _, v := range s.set { 96 licenses[idx] = v 97 idx++ 98 } 99 return licenses 100 } 101 102 func (s LicenseSet) Hash() (uint64, error) { 103 // access paths and filesystem IDs are not considered when hashing a license set, only the real paths 104 return hashstructure.Hash(s.ToSlice(), &hashstructure.HashOptions{ 105 ZeroNil: true, 106 SlicesAsSets: true, 107 }) 108 } 109 110 func (s LicenseSet) Empty() bool { 111 return len(s.set) < 1 112 }