github.com/anchore/syft@v1.38.2/syft/file/location_set.go (about) 1 package file 2 3 import ( 4 "slices" 5 "sort" 6 7 "github.com/gohugoio/hashstructure" 8 9 "github.com/anchore/syft/internal/log" 10 ) 11 12 // LocationSet provides a unique collection of Locations with metadata and set operations. 13 type LocationSet struct { 14 set map[LocationData]LocationMetadata 15 } 16 17 func NewLocationSet(locations ...Location) (s LocationSet) { 18 for _, l := range locations { 19 s.Add(l) 20 } 21 22 return s 23 } 24 25 func (s *LocationSet) Add(locations ...Location) { 26 if s.set == nil { 27 s.set = make(map[LocationData]LocationMetadata) 28 } 29 for _, l := range locations { 30 if m, ok := s.set[l.LocationData]; ok { 31 err := m.merge(l.LocationMetadata) 32 if err != nil { 33 log.Debugf("partial merge of location metadata: %+v", err) 34 } 35 s.set[l.LocationData] = m 36 } else { 37 s.set[l.LocationData] = l.LocationMetadata 38 } 39 } 40 } 41 42 func (s LocationSet) Remove(locations ...Location) { 43 if s.set == nil { 44 return 45 } 46 for _, l := range locations { 47 delete(s.set, l.LocationData) 48 } 49 } 50 51 func (s LocationSet) Contains(l Location) bool { 52 if s.set == nil { 53 return false 54 } 55 _, ok := s.set[l.LocationData] 56 return ok 57 } 58 59 func (s LocationSet) ToSlice(sorters ...func(a, b Location) int) []Location { 60 locations := s.ToUnorderedSlice() 61 62 var sorted bool 63 for _, sorter := range sorters { 64 if sorter == nil { 65 continue 66 } 67 slices.SortFunc(locations, sorter) 68 sorted = true 69 break 70 } 71 72 if !sorted { 73 // though no sorter was passed, we need to guarantee a stable ordering between calls 74 sort.Sort(Locations(locations)) 75 } 76 77 return locations 78 } 79 80 func (s LocationSet) ToUnorderedSlice() []Location { 81 if s.set == nil { 82 return nil 83 } 84 locations := make([]Location, len(s.set)) 85 idx := 0 86 for dir := range s.set { 87 locations[idx] = Location{ 88 LocationData: dir, 89 LocationMetadata: s.set[dir], 90 } 91 idx++ 92 } 93 return locations 94 } 95 96 func (s *LocationSet) CoordinateSet() CoordinateSet { 97 if s.set == nil { 98 return NewCoordinateSet() 99 } 100 set := NewCoordinateSet() 101 for l := range s.set { 102 set.Add(l.Coordinates) 103 } 104 return set 105 } 106 107 func (s *LocationSet) Empty() bool { 108 if s.set == nil { 109 return true 110 } 111 return len(s.set) == 0 112 } 113 114 func (s LocationSet) Hash() (uint64, error) { 115 // access paths and filesystem IDs are not considered when hashing a location set, only the real paths 116 return hashstructure.Hash(s.CoordinateSet().Paths(), &hashstructure.HashOptions{ 117 ZeroNil: true, 118 SlicesAsSets: true, 119 }) 120 }