github.com/noqcks/syft@v0.0.0-20230920222752-a9e2c4e288e5/syft/pkg/license_set.go (about)

     1  package pkg
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  
     7  	"github.com/mitchellh/hashstructure/v2"
     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 have a value
    54  		// note, this check should be moved to the license constructor in the future
    55  		if l.Value != "" {
    56  			if id, merged, err := s.addToExisting(l); err == nil && !merged {
    57  				// doesn't exist, add it
    58  				s.set[id] = l
    59  			} else if err != nil {
    60  				log.Trace("license set failed to add license %#v: %+v", l, err)
    61  			}
    62  		}
    63  	}
    64  }
    65  
    66  func (s LicenseSet) ToSlice() []License {
    67  	if s.set == nil {
    68  		return nil
    69  	}
    70  	var licenses []License
    71  	for _, v := range s.set {
    72  		licenses = append(licenses, v)
    73  	}
    74  	sort.Sort(Licenses(licenses))
    75  	return licenses
    76  }
    77  
    78  func (s LicenseSet) Hash() (uint64, error) {
    79  	// access paths and filesystem IDs are not considered when hashing a license set, only the real paths
    80  	return hashstructure.Hash(s.ToSlice(), hashstructure.FormatV2, &hashstructure.HashOptions{
    81  		ZeroNil:      true,
    82  		SlicesAsSets: true,
    83  	})
    84  }
    85  
    86  func (s LicenseSet) Empty() bool {
    87  	return len(s.set) < 1
    88  }