mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 18:13:42 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			199 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			199 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package cty
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"hash/crc64"
 | 
						|
 | 
						|
	"github.com/zclconf/go-cty/cty/set"
 | 
						|
)
 | 
						|
 | 
						|
// PathSet represents a set of Path objects. This can be used, for example,
 | 
						|
// to talk about a subset of paths within a value that meet some criteria,
 | 
						|
// without directly modifying the values at those paths.
 | 
						|
type PathSet struct {
 | 
						|
	set set.Set
 | 
						|
}
 | 
						|
 | 
						|
// NewPathSet creates and returns a PathSet, with initial contents optionally
 | 
						|
// set by the given arguments.
 | 
						|
func NewPathSet(paths ...Path) PathSet {
 | 
						|
	ret := PathSet{
 | 
						|
		set: set.NewSet(pathSetRules{}),
 | 
						|
	}
 | 
						|
 | 
						|
	for _, path := range paths {
 | 
						|
		ret.Add(path)
 | 
						|
	}
 | 
						|
 | 
						|
	return ret
 | 
						|
}
 | 
						|
 | 
						|
// Add inserts a single given path into the set.
 | 
						|
//
 | 
						|
// Paths are immutable after construction by convention. It is particularly
 | 
						|
// important not to mutate a path after it has been placed into a PathSet.
 | 
						|
// If a Path is mutated while in a set, behavior is undefined.
 | 
						|
func (s PathSet) Add(path Path) {
 | 
						|
	s.set.Add(path)
 | 
						|
}
 | 
						|
 | 
						|
// AddAllSteps is like Add but it also adds all of the steps leading to
 | 
						|
// the given path.
 | 
						|
//
 | 
						|
// For example, if given a path representing "foo.bar", it will add both
 | 
						|
// "foo" and "bar".
 | 
						|
func (s PathSet) AddAllSteps(path Path) {
 | 
						|
	for i := 1; i <= len(path); i++ {
 | 
						|
		s.Add(path[:i])
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Has returns true if the given path is in the receiving set.
 | 
						|
func (s PathSet) Has(path Path) bool {
 | 
						|
	return s.set.Has(path)
 | 
						|
}
 | 
						|
 | 
						|
// List makes and returns a slice of all of the paths in the receiving set,
 | 
						|
// in an undefined but consistent order.
 | 
						|
func (s PathSet) List() []Path {
 | 
						|
	if s.Empty() {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	ret := make([]Path, 0, s.set.Length())
 | 
						|
	for it := s.set.Iterator(); it.Next(); {
 | 
						|
		ret = append(ret, it.Value().(Path))
 | 
						|
	}
 | 
						|
	return ret
 | 
						|
}
 | 
						|
 | 
						|
// Remove modifies the receving set to no longer include the given path.
 | 
						|
// If the given path was already absent, this is a no-op.
 | 
						|
func (s PathSet) Remove(path Path) {
 | 
						|
	s.set.Remove(path)
 | 
						|
}
 | 
						|
 | 
						|
// Empty returns true if the length of the receiving set is zero.
 | 
						|
func (s PathSet) Empty() bool {
 | 
						|
	return s.set.Length() == 0
 | 
						|
}
 | 
						|
 | 
						|
// Union returns a new set whose contents are the union of the receiver and
 | 
						|
// the given other set.
 | 
						|
func (s PathSet) Union(other PathSet) PathSet {
 | 
						|
	return PathSet{
 | 
						|
		set: s.set.Union(other.set),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Intersection returns a new set whose contents are the intersection of the
 | 
						|
// receiver and the given other set.
 | 
						|
func (s PathSet) Intersection(other PathSet) PathSet {
 | 
						|
	return PathSet{
 | 
						|
		set: s.set.Intersection(other.set),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Subtract returns a new set whose contents are those from the receiver with
 | 
						|
// any elements of the other given set subtracted.
 | 
						|
func (s PathSet) Subtract(other PathSet) PathSet {
 | 
						|
	return PathSet{
 | 
						|
		set: s.set.Subtract(other.set),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// SymmetricDifference returns a new set whose contents are the symmetric
 | 
						|
// difference of the receiver and the given other set.
 | 
						|
func (s PathSet) SymmetricDifference(other PathSet) PathSet {
 | 
						|
	return PathSet{
 | 
						|
		set: s.set.SymmetricDifference(other.set),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Equal returns true if and only if both the receiver and the given other
 | 
						|
// set contain exactly the same paths.
 | 
						|
func (s PathSet) Equal(other PathSet) bool {
 | 
						|
	if s.set.Length() != other.set.Length() {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
	// Now we know the lengths are the same we only need to test in one
 | 
						|
	// direction whether everything in one is in the other.
 | 
						|
	for it := s.set.Iterator(); it.Next(); {
 | 
						|
		if !other.set.Has(it.Value()) {
 | 
						|
			return false
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
var crc64Table = crc64.MakeTable(crc64.ISO)
 | 
						|
 | 
						|
var indexStepPlaceholder = []byte("#")
 | 
						|
 | 
						|
// pathSetRules is an implementation of set.Rules from the set package,
 | 
						|
// used internally within PathSet.
 | 
						|
type pathSetRules struct {
 | 
						|
}
 | 
						|
 | 
						|
func (r pathSetRules) Hash(v interface{}) int {
 | 
						|
	path := v.(Path)
 | 
						|
	hash := crc64.New(crc64Table)
 | 
						|
 | 
						|
	for _, rawStep := range path {
 | 
						|
		switch step := rawStep.(type) {
 | 
						|
		case GetAttrStep:
 | 
						|
			// (this creates some garbage converting the string name to a
 | 
						|
			// []byte, but that's okay since cty is not designed to be
 | 
						|
			// used in tight loops under memory pressure.)
 | 
						|
			hash.Write([]byte(step.Name))
 | 
						|
		default:
 | 
						|
			// For any other step type we just append a predefined value,
 | 
						|
			// which means that e.g. all indexes into a given collection will
 | 
						|
			// hash to the same value but we assume that collections are
 | 
						|
			// small and thus this won't hurt too much.
 | 
						|
			hash.Write(indexStepPlaceholder)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// We discard half of the hash on 32-bit platforms; collisions just make
 | 
						|
	// our lookups take marginally longer, so not a big deal.
 | 
						|
	return int(hash.Sum64())
 | 
						|
}
 | 
						|
 | 
						|
func (r pathSetRules) Equivalent(a, b interface{}) bool {
 | 
						|
	aPath := a.(Path)
 | 
						|
	bPath := b.(Path)
 | 
						|
 | 
						|
	if len(aPath) != len(bPath) {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
 | 
						|
	for i := range aPath {
 | 
						|
		switch aStep := aPath[i].(type) {
 | 
						|
		case GetAttrStep:
 | 
						|
			bStep, ok := bPath[i].(GetAttrStep)
 | 
						|
			if !ok {
 | 
						|
				return false
 | 
						|
			}
 | 
						|
 | 
						|
			if aStep.Name != bStep.Name {
 | 
						|
				return false
 | 
						|
			}
 | 
						|
		case IndexStep:
 | 
						|
			bStep, ok := bPath[i].(IndexStep)
 | 
						|
			if !ok {
 | 
						|
				return false
 | 
						|
			}
 | 
						|
 | 
						|
			eq := aStep.Key.Equals(bStep.Key)
 | 
						|
			if !eq.IsKnown() || eq.False() {
 | 
						|
				return false
 | 
						|
			}
 | 
						|
		default:
 | 
						|
			// Should never happen, since we document PathStep as a closed type.
 | 
						|
			panic(fmt.Errorf("unsupported step type %T", aStep))
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return true
 | 
						|
}
 |