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  }