mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 10:03:42 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			393 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			393 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package cty
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"strings"
 | 
						|
)
 | 
						|
 | 
						|
// marker is an internal wrapper type used to add special "marks" to values.
 | 
						|
//
 | 
						|
// A "mark" is an annotation that can be used to represent additional
 | 
						|
// characteristics of values that propagate through operation methods to
 | 
						|
// result values. However, a marked value cannot be used with integration
 | 
						|
// methods normally associated with its type, in order to ensure that
 | 
						|
// calling applications don't inadvertently drop marks as they round-trip
 | 
						|
// values out of cty and back in again.
 | 
						|
//
 | 
						|
// Marked values are created only explicitly by the calling application, so
 | 
						|
// an application that never marks a value does not need to worry about
 | 
						|
// encountering marked values.
 | 
						|
type marker struct {
 | 
						|
	realV interface{}
 | 
						|
	marks ValueMarks
 | 
						|
}
 | 
						|
 | 
						|
// ValueMarks is a map, representing a set, of "mark" values associated with
 | 
						|
// a Value. See Value.Mark for more information on the usage of mark values.
 | 
						|
type ValueMarks map[interface{}]struct{}
 | 
						|
 | 
						|
// NewValueMarks constructs a new ValueMarks set with the given mark values.
 | 
						|
//
 | 
						|
// If any of the arguments are already ValueMarks values then they'll be merged
 | 
						|
// into the result, rather than used directly as individual marks.
 | 
						|
func NewValueMarks(marks ...interface{}) ValueMarks {
 | 
						|
	if len(marks) == 0 {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	ret := make(ValueMarks, len(marks))
 | 
						|
	for _, v := range marks {
 | 
						|
		if vm, ok := v.(ValueMarks); ok {
 | 
						|
			// Constructing a new ValueMarks with an existing ValueMarks
 | 
						|
			// implements a merge operation. (This can cause our result to
 | 
						|
			// have a larger size than we expected, but that's okay.)
 | 
						|
			for v := range vm {
 | 
						|
				ret[v] = struct{}{}
 | 
						|
			}
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		ret[v] = struct{}{}
 | 
						|
	}
 | 
						|
	if len(ret) == 0 {
 | 
						|
		// If we were merging ValueMarks values together and they were all
 | 
						|
		// empty then we'll avoid returning a zero-length map and return a
 | 
						|
		// nil instead, as is conventional.
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	return ret
 | 
						|
}
 | 
						|
 | 
						|
// Equal returns true if the receiver and the given ValueMarks both contain
 | 
						|
// the same marks.
 | 
						|
func (m ValueMarks) Equal(o ValueMarks) bool {
 | 
						|
	if len(m) != len(o) {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
	for v := range m {
 | 
						|
		if _, ok := o[v]; !ok {
 | 
						|
			return false
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
func (m ValueMarks) GoString() string {
 | 
						|
	var s strings.Builder
 | 
						|
	s.WriteString("cty.NewValueMarks(")
 | 
						|
	i := 0
 | 
						|
	for mv := range m {
 | 
						|
		if i != 0 {
 | 
						|
			s.WriteString(", ")
 | 
						|
		}
 | 
						|
		s.WriteString(fmt.Sprintf("%#v", mv))
 | 
						|
		i++
 | 
						|
	}
 | 
						|
	s.WriteString(")")
 | 
						|
	return s.String()
 | 
						|
}
 | 
						|
 | 
						|
// PathValueMarks is a structure that enables tracking marks
 | 
						|
// and the paths where they are located in one type
 | 
						|
type PathValueMarks struct {
 | 
						|
	Path  Path
 | 
						|
	Marks ValueMarks
 | 
						|
}
 | 
						|
 | 
						|
func (p PathValueMarks) Equal(o PathValueMarks) bool {
 | 
						|
	if !p.Path.Equals(o.Path) {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
	if !p.Marks.Equal(o.Marks) {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
// IsMarked returns true if and only if the receiving value carries at least
 | 
						|
// one mark. A marked value cannot be used directly with integration methods
 | 
						|
// without explicitly unmarking it (and retrieving the markings) first.
 | 
						|
func (val Value) IsMarked() bool {
 | 
						|
	_, ok := val.v.(marker)
 | 
						|
	return ok
 | 
						|
}
 | 
						|
 | 
						|
// HasMark returns true if and only if the receiving value has the given mark.
 | 
						|
func (val Value) HasMark(mark interface{}) bool {
 | 
						|
	if mr, ok := val.v.(marker); ok {
 | 
						|
		_, ok := mr.marks[mark]
 | 
						|
		return ok
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
// ContainsMarked returns true if the receiving value or any value within it
 | 
						|
// is marked.
 | 
						|
//
 | 
						|
// This operation is relatively expensive. If you only need a shallow result,
 | 
						|
// use IsMarked instead.
 | 
						|
func (val Value) ContainsMarked() bool {
 | 
						|
	ret := false
 | 
						|
	Walk(val, func(_ Path, v Value) (bool, error) {
 | 
						|
		if v.IsMarked() {
 | 
						|
			ret = true
 | 
						|
			return false, nil
 | 
						|
		}
 | 
						|
		return true, nil
 | 
						|
	})
 | 
						|
	return ret
 | 
						|
}
 | 
						|
 | 
						|
func (val Value) assertUnmarked() {
 | 
						|
	if val.IsMarked() {
 | 
						|
		panic("value is marked, so must be unmarked first")
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Marks returns a map (representing a set) of all of the mark values
 | 
						|
// associated with the receiving value, without changing the marks. Returns nil
 | 
						|
// if the value is not marked at all.
 | 
						|
func (val Value) Marks() ValueMarks {
 | 
						|
	if mr, ok := val.v.(marker); ok {
 | 
						|
		// copy so that the caller can't mutate our internals
 | 
						|
		ret := make(ValueMarks, len(mr.marks))
 | 
						|
		for k, v := range mr.marks {
 | 
						|
			ret[k] = v
 | 
						|
		}
 | 
						|
		return ret
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// HasSameMarks returns true if an only if the receiver and the given other
 | 
						|
// value have identical marks.
 | 
						|
func (val Value) HasSameMarks(other Value) bool {
 | 
						|
	vm, vmOK := val.v.(marker)
 | 
						|
	om, omOK := other.v.(marker)
 | 
						|
	if vmOK != omOK {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
	if vmOK {
 | 
						|
		return vm.marks.Equal(om.marks)
 | 
						|
	}
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
// Mark returns a new value that as the same type and underlying value as
 | 
						|
// the receiver but that also carries the given value as a "mark".
 | 
						|
//
 | 
						|
// Marks are used to carry additional application-specific characteristics
 | 
						|
// associated with values. A marked value can be used with operation methods,
 | 
						|
// in which case the marks are propagated to the operation results. A marked
 | 
						|
// value _cannot_ be used with integration methods, so callers of those
 | 
						|
// must derive an unmarked value using Unmark (and thus explicitly handle
 | 
						|
// the markings) before calling the integration methods.
 | 
						|
//
 | 
						|
// The mark value can be any value that would be valid to use as a map key.
 | 
						|
// The mark value should be of a named type in order to use the type itself
 | 
						|
// as a namespace for markings. That type can be unexported if desired, in
 | 
						|
// order to ensure that the mark can only be handled through the defining
 | 
						|
// package's own functions.
 | 
						|
//
 | 
						|
// An application that never calls this method does not need to worry about
 | 
						|
// handling marked values.
 | 
						|
func (val Value) Mark(mark interface{}) Value {
 | 
						|
	if _, ok := mark.(ValueMarks); ok {
 | 
						|
		panic("cannot call Value.Mark with a ValueMarks value (use WithMarks instead)")
 | 
						|
	}
 | 
						|
	var newMarker marker
 | 
						|
	newMarker.realV = val.v
 | 
						|
	if mr, ok := val.v.(marker); ok {
 | 
						|
		// It's already a marker, so we'll retain existing marks.
 | 
						|
		newMarker.marks = make(ValueMarks, len(mr.marks)+1)
 | 
						|
		for k, v := range mr.marks {
 | 
						|
			newMarker.marks[k] = v
 | 
						|
		}
 | 
						|
		// unwrap the inner marked value, so we don't get multiple layers
 | 
						|
		// of marking.
 | 
						|
		newMarker.realV = mr.realV
 | 
						|
	} else {
 | 
						|
		// It's not a marker yet, so we're creating the first mark.
 | 
						|
		newMarker.marks = make(ValueMarks, 1)
 | 
						|
	}
 | 
						|
	newMarker.marks[mark] = struct{}{}
 | 
						|
	return Value{
 | 
						|
		ty: val.ty,
 | 
						|
		v:  newMarker,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type applyPathValueMarksTransformer struct {
 | 
						|
	pvm []PathValueMarks
 | 
						|
}
 | 
						|
 | 
						|
func (t *applyPathValueMarksTransformer) Enter(p Path, v Value) (Value, error) {
 | 
						|
	return v, nil
 | 
						|
}
 | 
						|
 | 
						|
func (t *applyPathValueMarksTransformer) Exit(p Path, v Value) (Value, error) {
 | 
						|
	for _, path := range t.pvm {
 | 
						|
		if p.Equals(path.Path) {
 | 
						|
			return v.WithMarks(path.Marks), nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return v, nil
 | 
						|
}
 | 
						|
 | 
						|
// MarkWithPaths accepts a slice of PathValueMarks to apply
 | 
						|
// markers to particular paths and returns the marked
 | 
						|
// Value.
 | 
						|
func (val Value) MarkWithPaths(pvm []PathValueMarks) Value {
 | 
						|
	ret, _ := TransformWithTransformer(val, &applyPathValueMarksTransformer{pvm})
 | 
						|
	return ret
 | 
						|
}
 | 
						|
 | 
						|
// Unmark separates the marks of the receiving value from the value itself,
 | 
						|
// removing a new unmarked value and a map (representing a set) of the marks.
 | 
						|
//
 | 
						|
// If the receiver isn't marked, Unmark returns it verbatim along with a nil
 | 
						|
// map of marks.
 | 
						|
func (val Value) Unmark() (Value, ValueMarks) {
 | 
						|
	if !val.IsMarked() {
 | 
						|
		return val, nil
 | 
						|
	}
 | 
						|
	mr := val.v.(marker)
 | 
						|
	marks := val.Marks() // copy so that the caller can't mutate our internals
 | 
						|
	return Value{
 | 
						|
		ty: val.ty,
 | 
						|
		v:  mr.realV,
 | 
						|
	}, marks
 | 
						|
}
 | 
						|
 | 
						|
type unmarkTransformer struct {
 | 
						|
	pvm []PathValueMarks
 | 
						|
}
 | 
						|
 | 
						|
func (t *unmarkTransformer) Enter(p Path, v Value) (Value, error) {
 | 
						|
	unmarkedVal, marks := v.Unmark()
 | 
						|
	if len(marks) > 0 {
 | 
						|
		path := make(Path, len(p), len(p)+1)
 | 
						|
		copy(path, p)
 | 
						|
		t.pvm = append(t.pvm, PathValueMarks{path, marks})
 | 
						|
	}
 | 
						|
	return unmarkedVal, nil
 | 
						|
}
 | 
						|
 | 
						|
func (t *unmarkTransformer) Exit(p Path, v Value) (Value, error) {
 | 
						|
	return v, nil
 | 
						|
}
 | 
						|
 | 
						|
// UnmarkDeep is similar to Unmark, but it works with an entire nested structure
 | 
						|
// rather than just the given value directly.
 | 
						|
//
 | 
						|
// The result is guaranteed to contain no nested values that are marked, and
 | 
						|
// the returned marks set includes the superset of all of the marks encountered
 | 
						|
// during the operation.
 | 
						|
func (val Value) UnmarkDeep() (Value, ValueMarks) {
 | 
						|
	t := unmarkTransformer{}
 | 
						|
	ret, _ := TransformWithTransformer(val, &t)
 | 
						|
 | 
						|
	marks := make(ValueMarks)
 | 
						|
	for _, pvm := range t.pvm {
 | 
						|
		for m, s := range pvm.Marks {
 | 
						|
			marks[m] = s
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return ret, marks
 | 
						|
}
 | 
						|
 | 
						|
// UnmarkDeepWithPaths is like UnmarkDeep, except it returns a slice
 | 
						|
// of PathValueMarks rather than a superset of all marks. This allows
 | 
						|
// a caller to know which marks are associated with which paths
 | 
						|
// in the Value.
 | 
						|
func (val Value) UnmarkDeepWithPaths() (Value, []PathValueMarks) {
 | 
						|
	t := unmarkTransformer{}
 | 
						|
	ret, _ := TransformWithTransformer(val, &t)
 | 
						|
	return ret, t.pvm
 | 
						|
}
 | 
						|
 | 
						|
func (val Value) unmarkForce() Value {
 | 
						|
	unw, _ := val.Unmark()
 | 
						|
	return unw
 | 
						|
}
 | 
						|
 | 
						|
// WithMarks returns a new value that has the same type and underlying value
 | 
						|
// as the receiver and also has the marks from the given maps (representing
 | 
						|
// sets).
 | 
						|
func (val Value) WithMarks(marks ...ValueMarks) Value {
 | 
						|
	if len(marks) == 0 {
 | 
						|
		return val
 | 
						|
	}
 | 
						|
	ownMarks := val.Marks()
 | 
						|
	markCount := len(ownMarks)
 | 
						|
	for _, s := range marks {
 | 
						|
		markCount += len(s)
 | 
						|
	}
 | 
						|
	if markCount == 0 {
 | 
						|
		return val
 | 
						|
	}
 | 
						|
	newMarks := make(ValueMarks, markCount)
 | 
						|
	for m := range ownMarks {
 | 
						|
		newMarks[m] = struct{}{}
 | 
						|
	}
 | 
						|
	for _, s := range marks {
 | 
						|
		for m := range s {
 | 
						|
			newMarks[m] = struct{}{}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	v := val.v
 | 
						|
	if mr, ok := v.(marker); ok {
 | 
						|
		v = mr.realV
 | 
						|
	}
 | 
						|
	return Value{
 | 
						|
		ty: val.ty,
 | 
						|
		v: marker{
 | 
						|
			realV: v,
 | 
						|
			marks: newMarks,
 | 
						|
		},
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// WithSameMarks returns a new value that has the same type and underlying
 | 
						|
// value as the receiver and also has the marks from the given source values.
 | 
						|
//
 | 
						|
// Use this if you are implementing your own higher-level operations against
 | 
						|
// cty using the integration methods, to re-introduce the marks from the
 | 
						|
// source values of the operation.
 | 
						|
func (val Value) WithSameMarks(srcs ...Value) Value {
 | 
						|
	if len(srcs) == 0 {
 | 
						|
		return val
 | 
						|
	}
 | 
						|
	ownMarks := val.Marks()
 | 
						|
	markCount := len(ownMarks)
 | 
						|
	for _, sv := range srcs {
 | 
						|
		if mr, ok := sv.v.(marker); ok {
 | 
						|
			markCount += len(mr.marks)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if markCount == 0 {
 | 
						|
		return val
 | 
						|
	}
 | 
						|
	newMarks := make(ValueMarks, markCount)
 | 
						|
	for m := range ownMarks {
 | 
						|
		newMarks[m] = struct{}{}
 | 
						|
	}
 | 
						|
	for _, sv := range srcs {
 | 
						|
		if mr, ok := sv.v.(marker); ok {
 | 
						|
			for m := range mr.marks {
 | 
						|
				newMarks[m] = struct{}{}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	v := val.v
 | 
						|
	if mr, ok := v.(marker); ok {
 | 
						|
		v = mr.realV
 | 
						|
	}
 | 
						|
	return Value{
 | 
						|
		ty: val.ty,
 | 
						|
		v: marker{
 | 
						|
			realV: v,
 | 
						|
			marks: newMarks,
 | 
						|
		},
 | 
						|
	}
 | 
						|
}
 |