github.com/anchore/syft@v1.38.2/syft/file/locations.go (about)

     1  package file
     2  
     3  import (
     4  	"strings"
     5  
     6  	"github.com/anchore/syft/internal/evidence"
     7  )
     8  
     9  var locationSorterWithoutLayers = LocationSorter(nil)
    10  
    11  // Locations is a sortable slice of Location values.
    12  type Locations []Location
    13  
    14  func (l Locations) Len() int {
    15  	return len(l)
    16  }
    17  
    18  func (l Locations) Less(i, j int) bool {
    19  	return locationSorterWithoutLayers(l[i], l[j]) < 0
    20  }
    21  
    22  func (l Locations) Swap(i, j int) {
    23  	l[i], l[j] = l[j], l[i]
    24  }
    25  
    26  // LocationSorter creates a comparison function (slices.SortFunc) for Location objects based on layer order
    27  func LocationSorter(layers []string) func(a, b Location) int { //nolint:gocognit
    28  	var layerOrderByDigest map[string]int
    29  	if len(layers) > 0 {
    30  		layerOrderByDigest = make(map[string]int)
    31  		for i, digest := range layers {
    32  			layerOrderByDigest[digest] = i
    33  		}
    34  	}
    35  
    36  	return func(a, b Location) int {
    37  		// compare by evidence annotations first...
    38  		aEvidence := a.Annotations[evidence.AnnotationKey]
    39  		bEvidence := b.Annotations[evidence.AnnotationKey]
    40  
    41  		if aEvidence != bEvidence {
    42  			if aEvidence == evidence.PrimaryAnnotation {
    43  				return -1
    44  			}
    45  			if bEvidence == evidence.PrimaryAnnotation {
    46  				return 1
    47  			}
    48  
    49  			if aEvidence > bEvidence {
    50  				return -1
    51  			}
    52  			if bEvidence > aEvidence {
    53  				return 1
    54  			}
    55  		}
    56  
    57  		// ...then by layer order
    58  		if layerOrderByDigest != nil {
    59  			// we're given layer order details
    60  			aLayerIdx, aExists := layerOrderByDigest[a.FileSystemID]
    61  			bLayerIdx, bExists := layerOrderByDigest[b.FileSystemID]
    62  
    63  			if aLayerIdx != bLayerIdx {
    64  				if !aExists && !bExists {
    65  					return strings.Compare(a.FileSystemID, b.FileSystemID)
    66  				}
    67  				if !aExists {
    68  					return 1
    69  				}
    70  				if !bExists {
    71  					return -1
    72  				}
    73  
    74  				return aLayerIdx - bLayerIdx
    75  			}
    76  		} else if a.FileSystemID != b.FileSystemID {
    77  			// no layer info given, legacy behavior is to sort lexicographically
    78  			return strings.Compare(a.FileSystemID, b.FileSystemID)
    79  		}
    80  
    81  		// ...then by paths
    82  		if a.AccessPath != b.AccessPath {
    83  			return strings.Compare(a.AccessPath, b.AccessPath)
    84  		}
    85  
    86  		return strings.Compare(a.RealPath, b.RealPath)
    87  	}
    88  }
    89  
    90  // CoordinatesSorter creates a comparison function (slices.SortFunc) for Coordinate objects based on layer order
    91  func CoordinatesSorter(layers []string) func(a, b Coordinates) int {
    92  	var layerOrderByDigest map[string]int
    93  	if len(layers) > 0 {
    94  		layerOrderByDigest = make(map[string]int)
    95  		for i, digest := range layers {
    96  			layerOrderByDigest[digest] = i
    97  		}
    98  	}
    99  
   100  	return func(a, b Coordinates) int {
   101  		// ...then by layer order
   102  		if layerOrderByDigest != nil {
   103  			aLayerIdx, aExists := layerOrderByDigest[a.FileSystemID]
   104  			bLayerIdx, bExists := layerOrderByDigest[b.FileSystemID]
   105  
   106  			if aLayerIdx != bLayerIdx {
   107  				if !aExists && !bExists {
   108  					return strings.Compare(a.FileSystemID, b.FileSystemID)
   109  				}
   110  				if !aExists {
   111  					return 1
   112  				}
   113  				if !bExists {
   114  					return -1
   115  				}
   116  
   117  				return aLayerIdx - bLayerIdx
   118  			}
   119  		}
   120  
   121  		return strings.Compare(a.RealPath, b.RealPath)
   122  	}
   123  }