mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-07-09 21:17:09 +08:00
Extend hcl2 support with more functions
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
This commit is contained in:
95
vendor/github.com/zclconf/go-cty/cty/convert/conversion_collection.go
generated
vendored
95
vendor/github.com/zclconf/go-cty/cty/convert/conversion_collection.go
generated
vendored
@ -13,6 +13,14 @@ import (
|
||||
// if we're converting from a set into a list of the same element type.)
|
||||
func conversionCollectionToList(ety cty.Type, conv conversion) conversion {
|
||||
return func(val cty.Value, path cty.Path) (cty.Value, error) {
|
||||
if !val.Length().IsKnown() {
|
||||
// If the input collection has an unknown length (which is true
|
||||
// for a set containing unknown values) then our result must be
|
||||
// an unknown list, because we can't predict how many elements
|
||||
// the resulting list should have.
|
||||
return cty.UnknownVal(cty.List(val.Type().ElementType())), nil
|
||||
}
|
||||
|
||||
elems := make([]cty.Value, 0, val.LengthInt())
|
||||
i := int64(0)
|
||||
elemPath := append(path.Copy(), nil)
|
||||
@ -156,34 +164,45 @@ func conversionCollectionToMap(ety cty.Type, conv conversion) conversion {
|
||||
// given tuple type and return a set of the given element type.
|
||||
//
|
||||
// Will panic if the given tupleType isn't actually a tuple type.
|
||||
func conversionTupleToSet(tupleType cty.Type, listEty cty.Type, unsafe bool) conversion {
|
||||
func conversionTupleToSet(tupleType cty.Type, setEty cty.Type, unsafe bool) conversion {
|
||||
tupleEtys := tupleType.TupleElementTypes()
|
||||
|
||||
if len(tupleEtys) == 0 {
|
||||
// Empty tuple short-circuit
|
||||
return func(val cty.Value, path cty.Path) (cty.Value, error) {
|
||||
return cty.SetValEmpty(listEty), nil
|
||||
return cty.SetValEmpty(setEty), nil
|
||||
}
|
||||
}
|
||||
|
||||
if listEty == cty.DynamicPseudoType {
|
||||
if setEty == cty.DynamicPseudoType {
|
||||
// This is a special case where the caller wants us to find
|
||||
// a suitable single type that all elements can convert to, if
|
||||
// possible.
|
||||
listEty, _ = unify(tupleEtys, unsafe)
|
||||
if listEty == cty.NilType {
|
||||
setEty, _ = unify(tupleEtys, unsafe)
|
||||
if setEty == cty.NilType {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If the set element type after unification is still the dynamic
|
||||
// type, the only way this can result in a valid set is if all values
|
||||
// are of dynamic type
|
||||
if setEty == cty.DynamicPseudoType {
|
||||
for _, tupleEty := range tupleEtys {
|
||||
if !tupleEty.Equals(cty.DynamicPseudoType) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elemConvs := make([]conversion, len(tupleEtys))
|
||||
for i, tupleEty := range tupleEtys {
|
||||
if tupleEty.Equals(listEty) {
|
||||
if tupleEty.Equals(setEty) {
|
||||
// no conversion required
|
||||
continue
|
||||
}
|
||||
|
||||
elemConvs[i] = getConversion(tupleEty, listEty, unsafe)
|
||||
elemConvs[i] = getConversion(tupleEty, setEty, unsafe)
|
||||
if elemConvs[i] == nil {
|
||||
// If any of our element conversions are impossible, then the our
|
||||
// whole conversion is impossible.
|
||||
@ -244,6 +263,17 @@ func conversionTupleToList(tupleType cty.Type, listEty cty.Type, unsafe bool) co
|
||||
if listEty == cty.NilType {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If the list element type after unification is still the dynamic
|
||||
// type, the only way this can result in a valid list is if all values
|
||||
// are of dynamic type
|
||||
if listEty == cty.DynamicPseudoType {
|
||||
for _, tupleEty := range tupleEtys {
|
||||
if !tupleEty.Equals(cty.DynamicPseudoType) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elemConvs := make([]conversion, len(tupleEtys))
|
||||
@ -265,6 +295,7 @@ func conversionTupleToList(tupleType cty.Type, listEty cty.Type, unsafe bool) co
|
||||
// element conversions in elemConvs
|
||||
return func(val cty.Value, path cty.Path) (cty.Value, error) {
|
||||
elems := make([]cty.Value, 0, len(elemConvs))
|
||||
elemTys := make([]cty.Type, 0, len(elems))
|
||||
elemPath := append(path.Copy(), nil)
|
||||
i := int64(0)
|
||||
it := val.ElementIterator()
|
||||
@ -284,10 +315,15 @@ func conversionTupleToList(tupleType cty.Type, listEty cty.Type, unsafe bool) co
|
||||
}
|
||||
}
|
||||
elems = append(elems, val)
|
||||
elemTys = append(elemTys, val.Type())
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
elems, err := conversionUnifyListElements(elems, elemPath, unsafe)
|
||||
if err != nil {
|
||||
return cty.NilVal, err
|
||||
}
|
||||
return cty.ListVal(elems), nil
|
||||
}
|
||||
}
|
||||
@ -430,6 +466,16 @@ func conversionMapToObject(mapType cty.Type, objType cty.Type, unsafe bool) conv
|
||||
elems[name.AsString()] = val
|
||||
}
|
||||
|
||||
for name, aty := range objectAtys {
|
||||
if _, exists := elems[name]; !exists {
|
||||
if optional := objType.AttributeOptional(name); optional {
|
||||
elems[name] = cty.NullVal(aty)
|
||||
} else {
|
||||
return cty.NilVal, path.NewErrorf("map has no element for required attribute %q", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cty.ObjectVal(elems), nil
|
||||
}
|
||||
}
|
||||
@ -441,6 +487,7 @@ func conversionUnifyCollectionElements(elems map[string]cty.Value, path cty.Path
|
||||
}
|
||||
unifiedType, _ := unify(elemTypes, unsafe)
|
||||
if unifiedType == cty.NilType {
|
||||
return nil, path.NewErrorf("collection elements cannot be unified")
|
||||
}
|
||||
|
||||
unifiedElems := make(map[string]cty.Value)
|
||||
@ -486,3 +533,37 @@ func conversionCheckMapElementTypes(elems map[string]cty.Value, path cty.Path) e
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func conversionUnifyListElements(elems []cty.Value, path cty.Path, unsafe bool) ([]cty.Value, error) {
|
||||
elemTypes := make([]cty.Type, len(elems))
|
||||
for i, elem := range elems {
|
||||
elemTypes[i] = elem.Type()
|
||||
}
|
||||
unifiedType, _ := unify(elemTypes, unsafe)
|
||||
if unifiedType == cty.NilType {
|
||||
return nil, path.NewErrorf("collection elements cannot be unified")
|
||||
}
|
||||
|
||||
ret := make([]cty.Value, len(elems))
|
||||
elemPath := append(path.Copy(), nil)
|
||||
|
||||
for i, elem := range elems {
|
||||
if elem.Type().Equals(unifiedType) {
|
||||
ret[i] = elem
|
||||
continue
|
||||
}
|
||||
conv := getConversion(elem.Type(), unifiedType, unsafe)
|
||||
if conv == nil {
|
||||
}
|
||||
elemPath[len(elemPath)-1] = cty.IndexStep{
|
||||
Key: cty.NumberIntVal(int64(i)),
|
||||
}
|
||||
val, err := conv(elem, elemPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret[i] = val
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
19
vendor/github.com/zclconf/go-cty/cty/convert/conversion_object.go
generated
vendored
19
vendor/github.com/zclconf/go-cty/cty/convert/conversion_object.go
generated
vendored
@ -11,17 +11,29 @@ import (
|
||||
// type, meaning that each attribute of the output type has a corresponding
|
||||
// attribute in the input type where a recursive conversion is available.
|
||||
//
|
||||
// If the "out" type has any optional attributes, those attributes may be
|
||||
// absent in the "in" type, in which case null values will be used in their
|
||||
// place in the result.
|
||||
//
|
||||
// Shallow object conversions work the same for both safe and unsafe modes,
|
||||
// but the safety flag is passed on to recursive conversions and may thus
|
||||
// limit the above definition of "subset".
|
||||
func conversionObjectToObject(in, out cty.Type, unsafe bool) conversion {
|
||||
inAtys := in.AttributeTypes()
|
||||
outAtys := out.AttributeTypes()
|
||||
outOptionals := out.OptionalAttributes()
|
||||
attrConvs := make(map[string]conversion)
|
||||
|
||||
for name, outAty := range outAtys {
|
||||
inAty, exists := inAtys[name]
|
||||
if !exists {
|
||||
if _, optional := outOptionals[name]; optional {
|
||||
// If it's optional then we'll skip inserting an
|
||||
// attribute conversion and then deal with inserting
|
||||
// the default value in our overall conversion logic
|
||||
// later.
|
||||
continue
|
||||
}
|
||||
// No conversion is available, then.
|
||||
return nil
|
||||
}
|
||||
@ -71,6 +83,13 @@ func conversionObjectToObject(in, out cty.Type, unsafe bool) conversion {
|
||||
attrVals[name] = val
|
||||
}
|
||||
|
||||
for name := range outOptionals {
|
||||
if _, exists := attrVals[name]; !exists {
|
||||
wantTy := outAtys[name]
|
||||
attrVals[name] = cty.NullVal(wantTy)
|
||||
}
|
||||
}
|
||||
|
||||
return cty.ObjectVal(attrVals), nil
|
||||
}
|
||||
}
|
||||
|
8
vendor/github.com/zclconf/go-cty/cty/convert/mismatch_msg.go
generated
vendored
8
vendor/github.com/zclconf/go-cty/cty/convert/mismatch_msg.go
generated
vendored
@ -78,10 +78,16 @@ func mismatchMessageObjects(got, want cty.Type) string {
|
||||
for name, wantAty := range wantAtys {
|
||||
gotAty, exists := gotAtys[name]
|
||||
if !exists {
|
||||
missingAttrs = append(missingAttrs, name)
|
||||
if !want.AttributeOptional(name) {
|
||||
missingAttrs = append(missingAttrs, name)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if gotAty.Equals(wantAty) {
|
||||
continue // exact match, so no problem
|
||||
}
|
||||
|
||||
// We'll now try to convert these attributes in isolation and
|
||||
// see if we have a nested conversion error to report.
|
||||
// We'll try an unsafe conversion first, and then fall back on
|
||||
|
44
vendor/github.com/zclconf/go-cty/cty/function/function.go
generated
vendored
44
vendor/github.com/zclconf/go-cty/cty/function/function.go
generated
vendored
@ -244,19 +244,21 @@ func (f Function) Call(args []cty.Value) (val cty.Value, err error) {
|
||||
return cty.UnknownVal(expectedType), nil
|
||||
}
|
||||
|
||||
if val.IsMarked() && !spec.AllowMarked {
|
||||
unwrappedVal, marks := val.Unmark()
|
||||
// In order to avoid additional overhead on applications that
|
||||
// are not using marked values, we copy the given args only
|
||||
// if we encounter a marked value we need to unmark. However,
|
||||
// as a consequence we end up doing redundant copying if multiple
|
||||
// marked values need to be unwrapped. That seems okay because
|
||||
// argument lists are generally small.
|
||||
newArgs := make([]cty.Value, len(args))
|
||||
copy(newArgs, args)
|
||||
newArgs[i] = unwrappedVal
|
||||
resultMarks = append(resultMarks, marks)
|
||||
args = newArgs
|
||||
if !spec.AllowMarked {
|
||||
unwrappedVal, marks := val.UnmarkDeep()
|
||||
if len(marks) > 0 {
|
||||
// In order to avoid additional overhead on applications that
|
||||
// are not using marked values, we copy the given args only
|
||||
// if we encounter a marked value we need to unmark. However,
|
||||
// as a consequence we end up doing redundant copying if multiple
|
||||
// marked values need to be unwrapped. That seems okay because
|
||||
// argument lists are generally small.
|
||||
newArgs := make([]cty.Value, len(args))
|
||||
copy(newArgs, args)
|
||||
newArgs[i] = unwrappedVal
|
||||
resultMarks = append(resultMarks, marks)
|
||||
args = newArgs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -266,13 +268,15 @@ func (f Function) Call(args []cty.Value) (val cty.Value, err error) {
|
||||
if !val.IsKnown() && !spec.AllowUnknown {
|
||||
return cty.UnknownVal(expectedType), nil
|
||||
}
|
||||
if val.IsMarked() && !spec.AllowMarked {
|
||||
unwrappedVal, marks := val.Unmark()
|
||||
newArgs := make([]cty.Value, len(args))
|
||||
copy(newArgs, args)
|
||||
newArgs[len(posArgs)+i] = unwrappedVal
|
||||
resultMarks = append(resultMarks, marks)
|
||||
args = newArgs
|
||||
if !spec.AllowMarked {
|
||||
unwrappedVal, marks := val.UnmarkDeep()
|
||||
if len(marks) > 0 {
|
||||
newArgs := make([]cty.Value, len(args))
|
||||
copy(newArgs, args)
|
||||
newArgs[len(posArgs)+i] = unwrappedVal
|
||||
resultMarks = append(resultMarks, marks)
|
||||
args = newArgs
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
38
vendor/github.com/zclconf/go-cty/cty/function/stdlib/collection.go
generated
vendored
38
vendor/github.com/zclconf/go-cty/cty/function/stdlib/collection.go
generated
vendored
@ -138,6 +138,13 @@ var ElementFunc = function.New(&function.Spec{
|
||||
},
|
||||
Type: func(args []cty.Value) (cty.Type, error) {
|
||||
list := args[0]
|
||||
index := args[1]
|
||||
if index.IsKnown() {
|
||||
if index.LessThan(cty.NumberIntVal(0)).True() {
|
||||
return cty.DynamicPseudoType, fmt.Errorf("cannot use element function with a negative index")
|
||||
}
|
||||
}
|
||||
|
||||
listTy := list.Type()
|
||||
switch {
|
||||
case listTy.IsListType():
|
||||
@ -173,6 +180,10 @@ var ElementFunc = function.New(&function.Spec{
|
||||
return cty.DynamicVal, fmt.Errorf("invalid index: %s", err)
|
||||
}
|
||||
|
||||
if args[1].LessThan(cty.NumberIntVal(0)).True() {
|
||||
return cty.DynamicVal, fmt.Errorf("cannot use element function with a negative index")
|
||||
}
|
||||
|
||||
if !args[0].IsKnown() {
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
@ -240,6 +251,10 @@ var CoalesceListFunc = function.New(&function.Spec{
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
|
||||
if arg.IsNull() {
|
||||
continue
|
||||
}
|
||||
|
||||
if arg.LengthInt() > 0 {
|
||||
return arg, nil
|
||||
}
|
||||
@ -492,6 +507,11 @@ var FlattenFunc = function.New(&function.Spec{
|
||||
// We can flatten lists with unknown values, as long as they are not
|
||||
// lists themselves.
|
||||
func flattener(flattenList cty.Value) ([]cty.Value, bool) {
|
||||
if !flattenList.Length().IsKnown() {
|
||||
// If we don't know the length of what we're flattening then we can't
|
||||
// predict the length of our result yet either.
|
||||
return nil, false
|
||||
}
|
||||
out := make([]cty.Value, 0)
|
||||
for it := flattenList.ElementIterator(); it.Next(); {
|
||||
_, val := it.Element()
|
||||
@ -742,16 +762,10 @@ var MergeFunc = function.New(&function.Spec{
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
outputMap := make(map[string]cty.Value)
|
||||
|
||||
// if all inputs are null, return a null value rather than an object
|
||||
// with null attributes
|
||||
allNull := true
|
||||
for _, arg := range args {
|
||||
if arg.IsNull() {
|
||||
continue
|
||||
} else {
|
||||
allNull = false
|
||||
}
|
||||
|
||||
for it := arg.ElementIterator(); it.Next(); {
|
||||
k, v := it.Element()
|
||||
outputMap[k.AsString()] = v
|
||||
@ -759,9 +773,10 @@ var MergeFunc = function.New(&function.Spec{
|
||||
}
|
||||
|
||||
switch {
|
||||
case allNull:
|
||||
return cty.NullVal(retType), nil
|
||||
case retType.IsMapType():
|
||||
if len(outputMap) == 0 {
|
||||
return cty.MapValEmpty(retType.ElementType()), nil
|
||||
}
|
||||
return cty.MapVal(outputMap), nil
|
||||
case retType.IsObjectType(), retType.Equals(cty.DynamicPseudoType):
|
||||
return cty.ObjectVal(outputMap), nil
|
||||
@ -869,6 +884,10 @@ var SetProductFunc = function.New(&function.Spec{
|
||||
|
||||
total := 1
|
||||
for _, arg := range args {
|
||||
if !arg.Length().IsKnown() {
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
|
||||
// Because of our type checking function, we are guaranteed that
|
||||
// all of the arguments are known, non-null values of types that
|
||||
// support LengthInt.
|
||||
@ -1016,7 +1035,8 @@ func sliceIndexes(args []cty.Value) (int, int, bool, error) {
|
||||
var startIndex, endIndex, length int
|
||||
var startKnown, endKnown, lengthKnown bool
|
||||
|
||||
if args[0].Type().IsTupleType() || args[0].IsKnown() { // if it's a tuple then we always know the length by the type, but lists must be known
|
||||
// If it's a tuple then we always know the length by the type, but collections might be unknown or have unknown length
|
||||
if args[0].Type().IsTupleType() || args[0].Length().IsKnown() {
|
||||
length = args[0].LengthInt()
|
||||
lengthKnown = true
|
||||
}
|
||||
|
11
vendor/github.com/zclconf/go-cty/cty/function/stdlib/datetime.go
generated
vendored
11
vendor/github.com/zclconf/go-cty/cty/function/stdlib/datetime.go
generated
vendored
@ -362,9 +362,14 @@ func splitDateFormat(data []byte, atEOF bool) (advance int, token []byte, err er
|
||||
for i := 1; i < len(data); i++ {
|
||||
if data[i] == esc {
|
||||
if (i + 1) == len(data) {
|
||||
// We need at least one more byte to decide if this is an
|
||||
// escape or a terminator.
|
||||
return 0, nil, nil
|
||||
if atEOF {
|
||||
// We have a closing quote and are at the end of our input
|
||||
return len(data), data, nil
|
||||
} else {
|
||||
// We need at least one more byte to decide if this is an
|
||||
// escape or a terminator.
|
||||
return 0, nil, nil
|
||||
}
|
||||
}
|
||||
if data[i+1] == esc {
|
||||
i++ // doubled-up quotes are an escape sequence
|
||||
|
2
vendor/github.com/zclconf/go-cty/cty/function/stdlib/format.go
generated
vendored
2
vendor/github.com/zclconf/go-cty/cty/function/stdlib/format.go
generated
vendored
@ -85,7 +85,7 @@ var FormatListFunc = function.New(&function.Spec{
|
||||
argTy := arg.Type()
|
||||
switch {
|
||||
case (argTy.IsListType() || argTy.IsSetType() || argTy.IsTupleType()) && !arg.IsNull():
|
||||
if !argTy.IsTupleType() && !arg.IsKnown() {
|
||||
if !argTy.IsTupleType() && !(arg.IsKnown() && arg.Length().IsKnown()) {
|
||||
// We can't iterate this one at all yet then, so we can't
|
||||
// yet produce a result.
|
||||
unknowns[i] = true
|
||||
|
5
vendor/github.com/zclconf/go-cty/cty/function/stdlib/json.go
generated
vendored
5
vendor/github.com/zclconf/go-cty/cty/function/stdlib/json.go
generated
vendored
@ -12,6 +12,7 @@ var JSONEncodeFunc = function.New(&function.Spec{
|
||||
Name: "val",
|
||||
Type: cty.DynamicPseudoType,
|
||||
AllowDynamicType: true,
|
||||
AllowNull: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
@ -24,6 +25,10 @@ var JSONEncodeFunc = function.New(&function.Spec{
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
|
||||
if val.IsNull() {
|
||||
return cty.StringVal("null"), nil
|
||||
}
|
||||
|
||||
buf, err := json.Marshal(val, val.Type())
|
||||
if err != nil {
|
||||
return cty.NilVal, err
|
||||
|
41
vendor/github.com/zclconf/go-cty/cty/function/stdlib/set.go
generated
vendored
41
vendor/github.com/zclconf/go-cty/cty/function/stdlib/set.go
generated
vendored
@ -44,7 +44,7 @@ var SetUnionFunc = function.New(&function.Spec{
|
||||
Type: setOperationReturnType,
|
||||
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
|
||||
return s1.Union(s2)
|
||||
}),
|
||||
}, true),
|
||||
})
|
||||
|
||||
var SetIntersectionFunc = function.New(&function.Spec{
|
||||
@ -63,7 +63,7 @@ var SetIntersectionFunc = function.New(&function.Spec{
|
||||
Type: setOperationReturnType,
|
||||
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
|
||||
return s1.Intersection(s2)
|
||||
}),
|
||||
}, false),
|
||||
})
|
||||
|
||||
var SetSubtractFunc = function.New(&function.Spec{
|
||||
@ -82,7 +82,7 @@ var SetSubtractFunc = function.New(&function.Spec{
|
||||
Type: setOperationReturnType,
|
||||
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
|
||||
return s1.Subtract(s2)
|
||||
}),
|
||||
}, false),
|
||||
})
|
||||
|
||||
var SetSymmetricDifferenceFunc = function.New(&function.Spec{
|
||||
@ -100,8 +100,8 @@ var SetSymmetricDifferenceFunc = function.New(&function.Spec{
|
||||
},
|
||||
Type: setOperationReturnType,
|
||||
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
|
||||
return s1.Subtract(s2)
|
||||
}),
|
||||
return s1.SymmetricDifference(s2)
|
||||
}, false),
|
||||
})
|
||||
|
||||
// SetHasElement determines whether the given set contains the given value as an
|
||||
@ -163,8 +163,23 @@ func SetSymmetricDifference(sets ...cty.Value) (cty.Value, error) {
|
||||
func setOperationReturnType(args []cty.Value) (ret cty.Type, err error) {
|
||||
var etys []cty.Type
|
||||
for _, arg := range args {
|
||||
etys = append(etys, arg.Type().ElementType())
|
||||
ty := arg.Type().ElementType()
|
||||
|
||||
// Do not unify types for empty dynamic pseudo typed collections. These
|
||||
// will always convert to any other concrete type.
|
||||
if arg.IsKnown() && arg.LengthInt() == 0 && ty.Equals(cty.DynamicPseudoType) {
|
||||
continue
|
||||
}
|
||||
|
||||
etys = append(etys, ty)
|
||||
}
|
||||
|
||||
// If all element types were skipped (due to being empty dynamic collections),
|
||||
// the return type should also be a set of dynamic pseudo type.
|
||||
if len(etys) == 0 {
|
||||
return cty.Set(cty.DynamicPseudoType), nil
|
||||
}
|
||||
|
||||
newEty, _ := convert.UnifyUnsafe(etys)
|
||||
if newEty == cty.NilType {
|
||||
return cty.NilType, fmt.Errorf("given sets must all have compatible element types")
|
||||
@ -172,13 +187,21 @@ func setOperationReturnType(args []cty.Value) (ret cty.Type, err error) {
|
||||
return cty.Set(newEty), nil
|
||||
}
|
||||
|
||||
func setOperationImpl(f func(s1, s2 cty.ValueSet) cty.ValueSet) function.ImplFunc {
|
||||
func setOperationImpl(f func(s1, s2 cty.ValueSet) cty.ValueSet, allowUnknowns bool) function.ImplFunc {
|
||||
return func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
first := args[0]
|
||||
first, err = convert.Convert(first, retType)
|
||||
if err != nil {
|
||||
return cty.NilVal, function.NewArgError(0, err)
|
||||
}
|
||||
if !allowUnknowns && !first.IsWhollyKnown() {
|
||||
// This set function can produce a correct result only when all
|
||||
// elements are known, because eventually knowing the unknown
|
||||
// values may cause the result to have fewer known elements, or
|
||||
// might cause a result with no unknown elements at all to become
|
||||
// one with a different length.
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
|
||||
set := first.AsValueSet()
|
||||
for i, arg := range args[1:] {
|
||||
@ -186,6 +209,10 @@ func setOperationImpl(f func(s1, s2 cty.ValueSet) cty.ValueSet) function.ImplFun
|
||||
if err != nil {
|
||||
return cty.NilVal, function.NewArgError(i+1, err)
|
||||
}
|
||||
if !allowUnknowns && !arg.IsWhollyKnown() {
|
||||
// (For the same reason as we did this check for "first" above.)
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
|
||||
argSet := arg.AsValueSet()
|
||||
set = f(set, argSet)
|
||||
|
25
vendor/github.com/zclconf/go-cty/cty/json.go
generated
vendored
25
vendor/github.com/zclconf/go-cty/cty/json.go
generated
vendored
@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// MarshalJSON is an implementation of json.Marshaler that allows Type
|
||||
@ -52,6 +53,19 @@ func (t Type) MarshalJSON() ([]byte, error) {
|
||||
}
|
||||
buf.WriteString(`["object",`)
|
||||
buf.Write(atysJSON)
|
||||
if optionals := t.OptionalAttributes(); len(optionals) > 0 {
|
||||
buf.WriteByte(',')
|
||||
optionalNames := make([]string, 0, len(optionals))
|
||||
for k := range optionals {
|
||||
optionalNames = append(optionalNames, k)
|
||||
}
|
||||
sort.Strings(optionalNames)
|
||||
optionalsJSON, err := json.Marshal(optionalNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf.Write(optionalsJSON)
|
||||
}
|
||||
buf.WriteRune(']')
|
||||
return buf.Bytes(), nil
|
||||
case typeTuple:
|
||||
@ -148,7 +162,16 @@ func (t *Type) UnmarshalJSON(buf []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*t = Object(atys)
|
||||
if dec.More() {
|
||||
var optionals []string
|
||||
err = dec.Decode(&optionals)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*t = ObjectWithOptionalAttrs(atys, optionals)
|
||||
} else {
|
||||
*t = Object(atys)
|
||||
}
|
||||
case "tuple":
|
||||
var etys []Type
|
||||
err = dec.Decode(&etys)
|
||||
|
2
vendor/github.com/zclconf/go-cty/cty/json/marshal.go
generated
vendored
2
vendor/github.com/zclconf/go-cty/cty/json/marshal.go
generated
vendored
@ -10,7 +10,7 @@ import (
|
||||
|
||||
func marshal(val cty.Value, t cty.Type, path cty.Path, b *bytes.Buffer) error {
|
||||
if val.IsMarked() {
|
||||
return path.NewErrorf("value has marks, so it cannot be seralized")
|
||||
return path.NewErrorf("value has marks, so it cannot be serialized as JSON")
|
||||
}
|
||||
|
||||
// If we're going to decode as DynamicPseudoType then we need to save
|
||||
|
82
vendor/github.com/zclconf/go-cty/cty/marks.go
generated
vendored
82
vendor/github.com/zclconf/go-cty/cty/marks.go
generated
vendored
@ -67,6 +67,23 @@ func (m ValueMarks) GoString() string {
|
||||
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.
|
||||
@ -174,6 +191,31 @@ func (val Value) Mark(mark interface{}) Value {
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
//
|
||||
@ -191,6 +233,24 @@ func (val Value) Unmark() (Value, ValueMarks) {
|
||||
}, 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.
|
||||
//
|
||||
@ -198,17 +258,29 @@ func (val Value) Unmark() (Value, ValueMarks) {
|
||||
// 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)
|
||||
ret, _ := Transform(val, func(_ Path, v Value) (Value, error) {
|
||||
unmarkedV, valueMarks := v.Unmark()
|
||||
for m, s := range valueMarks {
|
||||
for _, pvm := range t.pvm {
|
||||
for m, s := range pvm.Marks {
|
||||
marks[m] = s
|
||||
}
|
||||
return unmarkedV, nil
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
|
89
vendor/github.com/zclconf/go-cty/cty/object_type.go
generated
vendored
89
vendor/github.com/zclconf/go-cty/cty/object_type.go
generated
vendored
@ -2,11 +2,13 @@ package cty
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type typeObject struct {
|
||||
typeImplSigil
|
||||
AttrTypes map[string]Type
|
||||
AttrTypes map[string]Type
|
||||
AttrOptional map[string]struct{}
|
||||
}
|
||||
|
||||
// Object creates an object type with the given attribute types.
|
||||
@ -14,14 +16,52 @@ type typeObject struct {
|
||||
// After a map is passed to this function the caller must no longer access it,
|
||||
// since ownership is transferred to this library.
|
||||
func Object(attrTypes map[string]Type) Type {
|
||||
return ObjectWithOptionalAttrs(attrTypes, nil)
|
||||
}
|
||||
|
||||
// ObjectWithOptionalAttrs creates an object type where some of its attributes
|
||||
// are optional.
|
||||
//
|
||||
// This function is EXPERIMENTAL. The behavior of the function or of any other
|
||||
// functions working either directly or indirectly with a type created by
|
||||
// this function is not currently considered as a compatibility constraint, and
|
||||
// is subject to change even in minor-version releases of this module. Other
|
||||
// modules that work with cty types and values may or may not support object
|
||||
// types with optional attributes; if they do not, their behavior when
|
||||
// receiving one may be non-ideal.
|
||||
//
|
||||
// Optional attributes are significant only when an object type is being used
|
||||
// as a target type for conversion in the "convert" package. A value of an
|
||||
// object type always has a value for each of the attributes in the attribute
|
||||
// types table, with optional values replaced with null during conversion.
|
||||
//
|
||||
// All keys in the optional slice must also exist in the attrTypes map. If not,
|
||||
// this function will panic.
|
||||
//
|
||||
// After a map or array is passed to this function the caller must no longer
|
||||
// access it, since ownership is transferred to this library.
|
||||
func ObjectWithOptionalAttrs(attrTypes map[string]Type, optional []string) Type {
|
||||
attrTypesNorm := make(map[string]Type, len(attrTypes))
|
||||
for k, v := range attrTypes {
|
||||
attrTypesNorm[NormalizeString(k)] = v
|
||||
}
|
||||
|
||||
var optionalSet map[string]struct{}
|
||||
if len(optional) > 0 {
|
||||
optionalSet = make(map[string]struct{}, len(optional))
|
||||
for _, k := range optional {
|
||||
k = NormalizeString(k)
|
||||
if _, exists := attrTypesNorm[k]; !exists {
|
||||
panic(fmt.Sprintf("optional contains undeclared attribute %q", k))
|
||||
}
|
||||
optionalSet[k] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return Type{
|
||||
typeObject{
|
||||
AttrTypes: attrTypesNorm,
|
||||
AttrTypes: attrTypesNorm,
|
||||
AttrOptional: optionalSet,
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -44,6 +84,11 @@ func (t typeObject) Equals(other Type) bool {
|
||||
if !oty.Equals(ty) {
|
||||
return false
|
||||
}
|
||||
_, opt := t.AttrOptional[attr]
|
||||
_, oopt := ot.AttrOptional[attr]
|
||||
if opt != oopt {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
@ -66,6 +111,14 @@ func (t typeObject) GoString() string {
|
||||
if len(t.AttrTypes) == 0 {
|
||||
return "cty.EmptyObject"
|
||||
}
|
||||
if len(t.AttrOptional) > 0 {
|
||||
opt := make([]string, len(t.AttrOptional))
|
||||
for k := range t.AttrOptional {
|
||||
opt = append(opt, k)
|
||||
}
|
||||
sort.Strings(opt)
|
||||
return fmt.Sprintf("cty.ObjectWithOptionalAttrs(%#v, %#v)", t.AttrTypes, opt)
|
||||
}
|
||||
return fmt.Sprintf("cty.Object(%#v)", t.AttrTypes)
|
||||
}
|
||||
|
||||
@ -133,3 +186,35 @@ func (t Type) AttributeTypes() map[string]Type {
|
||||
}
|
||||
panic("AttributeTypes on non-object Type")
|
||||
}
|
||||
|
||||
// OptionalAttributes returns a map representing the set of attributes
|
||||
// that are optional. Will panic if the receiver is not an object type
|
||||
// (use IsObjectType to confirm).
|
||||
//
|
||||
// The returned map is part of the internal state of the type, and is provided
|
||||
// for read access only. It is forbidden for any caller to modify the returned
|
||||
// map.
|
||||
func (t Type) OptionalAttributes() map[string]struct{} {
|
||||
if ot, ok := t.typeImpl.(typeObject); ok {
|
||||
return ot.AttrOptional
|
||||
}
|
||||
panic("OptionalAttributes on non-object Type")
|
||||
}
|
||||
|
||||
// AttributeOptional returns true if the attribute of the given name is
|
||||
// optional.
|
||||
//
|
||||
// Will panic if the receiver is not an object type (use IsObjectType to
|
||||
// confirm) or if the object type has no such attribute (use HasAttribute to
|
||||
// confirm).
|
||||
func (t Type) AttributeOptional(name string) bool {
|
||||
name = NormalizeString(name)
|
||||
if ot, ok := t.typeImpl.(typeObject); ok {
|
||||
if _, hasAttr := ot.AttrTypes[name]; !hasAttr {
|
||||
panic("no such attribute")
|
||||
}
|
||||
_, exists := ot.AttrOptional[name]
|
||||
return exists
|
||||
}
|
||||
panic("AttributeDefaultValue on non-object Type")
|
||||
}
|
||||
|
6
vendor/github.com/zclconf/go-cty/cty/path_set.go
generated
vendored
6
vendor/github.com/zclconf/go-cty/cty/path_set.go
generated
vendored
@ -196,3 +196,9 @@ func (r pathSetRules) Equivalent(a, b interface{}) bool {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// SameRules is true if both Rules instances are pathSetRules structs.
|
||||
func (r pathSetRules) SameRules(other set.Rules) bool {
|
||||
_, ok := other.(pathSetRules)
|
||||
return ok
|
||||
}
|
||||
|
4
vendor/github.com/zclconf/go-cty/cty/set/rules.go
generated
vendored
4
vendor/github.com/zclconf/go-cty/cty/set/rules.go
generated
vendored
@ -22,6 +22,10 @@ type Rules interface {
|
||||
// though it is *not* required that two values with the same hash value
|
||||
// be equivalent.
|
||||
Equivalent(interface{}, interface{}) bool
|
||||
|
||||
// SameRules returns true if the instance is equivalent to another Rules
|
||||
// instance.
|
||||
SameRules(Rules) bool
|
||||
}
|
||||
|
||||
// OrderedRules is an extension of Rules that can apply a partial order to
|
||||
|
4
vendor/github.com/zclconf/go-cty/cty/set/set.go
generated
vendored
4
vendor/github.com/zclconf/go-cty/cty/set/set.go
generated
vendored
@ -41,7 +41,7 @@ func NewSetFromSlice(rules Rules, vals []interface{}) Set {
|
||||
}
|
||||
|
||||
func sameRules(s1 Set, s2 Set) bool {
|
||||
return s1.rules == s2.rules
|
||||
return s1.rules.SameRules(s2.rules)
|
||||
}
|
||||
|
||||
func mustHaveSameRules(s1 Set, s2 Set) {
|
||||
@ -53,7 +53,7 @@ func mustHaveSameRules(s1 Set, s2 Set) {
|
||||
// HasRules returns true if and only if the receiving set has the given rules
|
||||
// instance as its rules.
|
||||
func (s Set) HasRules(rules Rules) bool {
|
||||
return s.rules == rules
|
||||
return s.rules.SameRules(rules)
|
||||
}
|
||||
|
||||
// Rules returns the receiving set's rules instance.
|
||||
|
11
vendor/github.com/zclconf/go-cty/cty/set_internals.go
generated
vendored
11
vendor/github.com/zclconf/go-cty/cty/set_internals.go
generated
vendored
@ -65,6 +65,17 @@ func (r setRules) Equivalent(v1 interface{}, v2 interface{}) bool {
|
||||
return eqv.v == true
|
||||
}
|
||||
|
||||
// SameRules is only true if the other Rules instance is also a setRules struct,
|
||||
// and the types are considered equal.
|
||||
func (r setRules) SameRules(other set.Rules) bool {
|
||||
rules, ok := other.(setRules)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return r.Type.Equals(rules.Type)
|
||||
}
|
||||
|
||||
// Less is an implementation of set.OrderedRules so that we can iterate over
|
||||
// set elements in a consistent order, where such an order is possible.
|
||||
func (r setRules) Less(v1, v2 interface{}) bool {
|
||||
|
5
vendor/github.com/zclconf/go-cty/cty/type.go
generated
vendored
5
vendor/github.com/zclconf/go-cty/cty/type.go
generated
vendored
@ -36,6 +36,9 @@ func (t typeImplSigil) isTypeImpl() typeImplSigil {
|
||||
// Equals returns true if the other given Type exactly equals the receiver
|
||||
// type.
|
||||
func (t Type) Equals(other Type) bool {
|
||||
if t == NilType || other == NilType {
|
||||
return t == other
|
||||
}
|
||||
return t.typeImpl.Equals(other)
|
||||
}
|
||||
|
||||
@ -87,7 +90,7 @@ func (t Type) HasDynamicTypes() bool {
|
||||
case t.IsPrimitiveType():
|
||||
return false
|
||||
case t.IsCollectionType():
|
||||
return false
|
||||
return t.ElementType().HasDynamicTypes()
|
||||
case t.IsObjectType():
|
||||
attrTypes := t.AttributeTypes()
|
||||
for _, at := range attrTypes {
|
||||
|
3
vendor/github.com/zclconf/go-cty/cty/unknown.go
generated
vendored
3
vendor/github.com/zclconf/go-cty/cty/unknown.go
generated
vendored
@ -5,7 +5,8 @@ package cty
|
||||
type unknownType struct {
|
||||
}
|
||||
|
||||
// Unknown is a special value that can be
|
||||
// unknown is a special value that can be used as the internal value of a
|
||||
// Value to create a placeholder for a value that isn't yet known.
|
||||
var unknown interface{} = &unknownType{}
|
||||
|
||||
// UnknownVal returns an Value that represents an unknown value of the given
|
||||
|
34
vendor/github.com/zclconf/go-cty/cty/value.go
generated
vendored
34
vendor/github.com/zclconf/go-cty/cty/value.go
generated
vendored
@ -106,3 +106,37 @@ func (val Value) IsWhollyKnown() bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// HasWhollyKnownType checks if the value is dynamic, or contains any nested
|
||||
// DynamicVal. This implies that both the value is not known, and the final
|
||||
// type may change.
|
||||
func (val Value) HasWhollyKnownType() bool {
|
||||
// a null dynamic type is known
|
||||
if val.IsNull() {
|
||||
return true
|
||||
}
|
||||
|
||||
// an unknown DynamicPseudoType is a DynamicVal, but we don't want to
|
||||
// check that value for equality here, since this method is used within the
|
||||
// equality check.
|
||||
if !val.IsKnown() && val.ty == DynamicPseudoType {
|
||||
return false
|
||||
}
|
||||
|
||||
if val.CanIterateElements() {
|
||||
// if the value is not known, then we can look directly at the internal
|
||||
// types
|
||||
if !val.IsKnown() {
|
||||
return !val.ty.HasDynamicTypes()
|
||||
}
|
||||
|
||||
for it := val.ElementIterator(); it.Next(); {
|
||||
_, ev := it.Element()
|
||||
if !ev.HasWhollyKnownType() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
5
vendor/github.com/zclconf/go-cty/cty/value_init.go
generated
vendored
5
vendor/github.com/zclconf/go-cty/cty/value_init.go
generated
vendored
@ -247,11 +247,6 @@ func SetVal(vals []Value) Value {
|
||||
val = unmarkedVal
|
||||
markSets = append(markSets, marks)
|
||||
}
|
||||
if val.ContainsMarked() {
|
||||
// FIXME: Allow this, but unmark the values and apply the
|
||||
// marking to the set itself instead.
|
||||
panic("set cannot contain marked values")
|
||||
}
|
||||
if elementType == DynamicPseudoType {
|
||||
elementType = val.ty
|
||||
} else if val.ty != DynamicPseudoType && !elementType.Equals(val.ty) {
|
||||
|
152
vendor/github.com/zclconf/go-cty/cty/value_ops.go
generated
vendored
152
vendor/github.com/zclconf/go-cty/cty/value_ops.go
generated
vendored
@ -3,7 +3,6 @@ package cty
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
|
||||
"github.com/zclconf/go-cty/cty/set"
|
||||
)
|
||||
@ -133,9 +132,9 @@ func (val Value) Equals(other Value) Value {
|
||||
case val.IsKnown() && !other.IsKnown():
|
||||
switch {
|
||||
case val.IsNull(), other.ty.HasDynamicTypes():
|
||||
// If known is Null, we need to wait for the unkown value since
|
||||
// If known is Null, we need to wait for the unknown value since
|
||||
// nulls of any type are equal.
|
||||
// An unkown with a dynamic type compares as unknown, which we need
|
||||
// An unknown with a dynamic type compares as unknown, which we need
|
||||
// to check before the type comparison below.
|
||||
return UnknownVal(Bool)
|
||||
case !val.ty.Equals(other.ty):
|
||||
@ -148,9 +147,9 @@ func (val Value) Equals(other Value) Value {
|
||||
case other.IsKnown() && !val.IsKnown():
|
||||
switch {
|
||||
case other.IsNull(), val.ty.HasDynamicTypes():
|
||||
// If known is Null, we need to wait for the unkown value since
|
||||
// If known is Null, we need to wait for the unknown value since
|
||||
// nulls of any type are equal.
|
||||
// An unkown with a dynamic type compares as unknown, which we need
|
||||
// An unknown with a dynamic type compares as unknown, which we need
|
||||
// to check before the type comparison below.
|
||||
return UnknownVal(Bool)
|
||||
case !other.ty.Equals(val.ty):
|
||||
@ -171,7 +170,15 @@ func (val Value) Equals(other Value) Value {
|
||||
return BoolVal(false)
|
||||
}
|
||||
|
||||
if val.ty.HasDynamicTypes() || other.ty.HasDynamicTypes() {
|
||||
// Check if there are any nested dynamic values making this comparison
|
||||
// unknown.
|
||||
if !val.HasWhollyKnownType() || !other.HasWhollyKnownType() {
|
||||
// Even if we have dynamic values, we can still determine inequality if
|
||||
// there is no way the types could later conform.
|
||||
if val.ty.TestConformance(other.ty) != nil && other.ty.TestConformance(val.ty) != nil {
|
||||
return BoolVal(false)
|
||||
}
|
||||
|
||||
return UnknownVal(Bool)
|
||||
}
|
||||
|
||||
@ -262,24 +269,26 @@ func (val Value) Equals(other Value) Value {
|
||||
s2 := other.v.(set.Set)
|
||||
equal := true
|
||||
|
||||
// Note that by our definition of sets it's never possible for two
|
||||
// sets that contain unknown values (directly or indicrectly) to
|
||||
// ever be equal, even if they are otherwise identical.
|
||||
|
||||
// FIXME: iterating both lists and checking each item is not the
|
||||
// ideal implementation here, but it works with the primitives we
|
||||
// have in the set implementation. Perhaps the set implementation
|
||||
// can provide its own equality test later.
|
||||
s1.EachValue(func(v interface{}) {
|
||||
if !s2.Has(v) {
|
||||
// Two sets are equal if all of their values are known and all values
|
||||
// in one are also in the other.
|
||||
for it := s1.Iterator(); it.Next(); {
|
||||
rv := it.Value()
|
||||
if rv == unknown { // "unknown" is the internal representation of unknown-ness
|
||||
return UnknownVal(Bool)
|
||||
}
|
||||
if !s2.Has(rv) {
|
||||
equal = false
|
||||
}
|
||||
})
|
||||
s2.EachValue(func(v interface{}) {
|
||||
if !s1.Has(v) {
|
||||
}
|
||||
for it := s2.Iterator(); it.Next(); {
|
||||
rv := it.Value()
|
||||
if rv == unknown { // "unknown" is the internal representation of unknown-ness
|
||||
return UnknownVal(Bool)
|
||||
}
|
||||
if !s1.Has(rv) {
|
||||
equal = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
result = equal
|
||||
case ty.IsMapType():
|
||||
@ -454,17 +463,32 @@ func (val Value) RawEquals(other Value) bool {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
case ty.IsSetType():
|
||||
s1 := val.v.(set.Set)
|
||||
s2 := other.v.(set.Set)
|
||||
|
||||
// Since we're intentionally ignoring our rule that two unknowns
|
||||
// are never equal, we can cheat here.
|
||||
// (This isn't 100% right since e.g. it will fail if the set contains
|
||||
// numbers that are infinite, which DeepEqual can't compare properly.
|
||||
// We're accepting that limitation for simplicity here, since this
|
||||
// function is here primarily for testing.)
|
||||
return reflect.DeepEqual(s1, s2)
|
||||
case ty.IsSetType():
|
||||
// Convert the set values into a slice so that we can compare each
|
||||
// value. This is safe because the underlying sets are ordered (see
|
||||
// setRules in set_internals.go), and so the results are guaranteed to
|
||||
// be in a consistent order for two equal sets
|
||||
setList1 := val.AsValueSlice()
|
||||
setList2 := other.AsValueSlice()
|
||||
|
||||
// If both physical sets have the same length and they have all of their
|
||||
// _known_ values in common, we know that both sets also have the same
|
||||
// number of unknown values.
|
||||
if len(setList1) != len(setList2) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range setList1 {
|
||||
eq := setList1[i].RawEquals(setList2[i])
|
||||
if !eq {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// If we got here without returning false already then our sets are
|
||||
// equal enough for RawEquals purposes.
|
||||
return true
|
||||
|
||||
case ty.IsMapType():
|
||||
ety := ty.typeImpl.(typeMap).ElementTypeT
|
||||
@ -572,8 +596,25 @@ func (val Value) Multiply(other Value) Value {
|
||||
return *shortCircuit
|
||||
}
|
||||
|
||||
ret := new(big.Float)
|
||||
// find the larger precision of the arguments
|
||||
resPrec := val.v.(*big.Float).Prec()
|
||||
otherPrec := other.v.(*big.Float).Prec()
|
||||
if otherPrec > resPrec {
|
||||
resPrec = otherPrec
|
||||
}
|
||||
|
||||
// make sure we have enough precision for the product
|
||||
ret := new(big.Float).SetPrec(512)
|
||||
ret.Mul(val.v.(*big.Float), other.v.(*big.Float))
|
||||
|
||||
// now reduce the precision back to the greater argument, or the minimum
|
||||
// required by the product.
|
||||
minPrec := ret.MinPrec()
|
||||
if minPrec > resPrec {
|
||||
resPrec = minPrec
|
||||
}
|
||||
ret.SetPrec(resPrec)
|
||||
|
||||
return NumberVal(ret)
|
||||
}
|
||||
|
||||
@ -645,11 +686,14 @@ func (val Value) Modulo(other Value) Value {
|
||||
// FIXME: This is a bit clumsy. Should come back later and see if there's a
|
||||
// more straightforward way to do this.
|
||||
rat := val.Divide(other)
|
||||
ratFloorInt := &big.Int{}
|
||||
rat.v.(*big.Float).Int(ratFloorInt)
|
||||
work := (&big.Float{}).SetInt(ratFloorInt)
|
||||
ratFloorInt, _ := rat.v.(*big.Float).Int(nil)
|
||||
|
||||
// start with a copy of the original larger value so that we do not lose
|
||||
// precision.
|
||||
v := val.v.(*big.Float)
|
||||
work := new(big.Float).Copy(v).SetInt(ratFloorInt)
|
||||
work.Mul(other.v.(*big.Float), work)
|
||||
work.Sub(val.v.(*big.Float), work)
|
||||
work.Sub(v, work)
|
||||
|
||||
return NumberVal(work)
|
||||
}
|
||||
@ -947,8 +991,7 @@ func (val Value) HasElement(elem Value) Value {
|
||||
// If the receiver is null then this function will panic.
|
||||
//
|
||||
// Note that Length is not supported for strings. To determine the length
|
||||
// of a string, call AsString and take the length of the native Go string
|
||||
// that is returned.
|
||||
// of a string, use the Length function in funcs/stdlib.
|
||||
func (val Value) Length() Value {
|
||||
if val.IsMarked() {
|
||||
val, valMarks := val.Unmark()
|
||||
@ -963,6 +1006,25 @@ func (val Value) Length() Value {
|
||||
if !val.IsKnown() {
|
||||
return UnknownVal(Number)
|
||||
}
|
||||
if val.Type().IsSetType() {
|
||||
// The Length rules are a little different for sets because if any
|
||||
// unknown values are present then the values they are standing in for
|
||||
// may or may not be equal to other elements in the set, and thus they
|
||||
// may or may not coalesce with other elements and produce fewer
|
||||
// items in the resulting set.
|
||||
storeLength := int64(val.v.(set.Set).Length())
|
||||
if storeLength == 1 || val.IsWhollyKnown() {
|
||||
// If our set is wholly known then we know its length.
|
||||
//
|
||||
// We also know the length if the physical store has only one
|
||||
// element, even if that element is unknown, because there's
|
||||
// nothing else in the set for it to coalesce with and a single
|
||||
// unknown value cannot represent more than one known value.
|
||||
return NumberIntVal(storeLength)
|
||||
}
|
||||
// Otherwise, we cannot predict the length.
|
||||
return UnknownVal(Number)
|
||||
}
|
||||
|
||||
return NumberIntVal(int64(val.LengthInt()))
|
||||
}
|
||||
@ -972,6 +1034,13 @@ func (val Value) Length() Value {
|
||||
//
|
||||
// This is an integration method provided for the convenience of code bridging
|
||||
// into Go's type system.
|
||||
//
|
||||
// For backward compatibility with an earlier implementation error, LengthInt's
|
||||
// result can disagree with Length's result for any set containing unknown
|
||||
// values. Length can potentially indicate the set's length is unknown in that
|
||||
// case, whereas LengthInt will return the maximum possible length as if the
|
||||
// unknown values were each a placeholder for a value not equal to any other
|
||||
// value in the set.
|
||||
func (val Value) LengthInt() int {
|
||||
val.assertUnmarked()
|
||||
if val.Type().IsTupleType() {
|
||||
@ -995,6 +1064,15 @@ func (val Value) LengthInt() int {
|
||||
return len(val.v.([]interface{}))
|
||||
|
||||
case val.ty.IsSetType():
|
||||
// NOTE: This is technically not correct in cases where the set
|
||||
// contains unknown values, because in that case we can't know how
|
||||
// many known values those unknown values are standing in for -- they
|
||||
// might coalesce with other values once known.
|
||||
//
|
||||
// However, this incorrect behavior is preserved for backward
|
||||
// compatibility with callers that were relying on LengthInt rather
|
||||
// than calling Length. Instead of panicking when a set contains an
|
||||
// unknown value, LengthInt returns the largest possible length.
|
||||
return val.v.(set.Set).Length()
|
||||
|
||||
case val.ty.IsMapType():
|
||||
|
59
vendor/github.com/zclconf/go-cty/cty/walk.go
generated
vendored
59
vendor/github.com/zclconf/go-cty/cty/walk.go
generated
vendored
@ -61,6 +61,34 @@ func walk(path Path, val Value, cb func(Path, Value) (bool, error)) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Transformer is the interface used to optionally transform values in a
|
||||
// possibly-complex structure. The Enter method is called before traversing
|
||||
// through a given path, and the Exit method is called when traversal of a
|
||||
// path is complete.
|
||||
//
|
||||
// Use Enter when you want to transform a complex value before traversal
|
||||
// (preorder), and Exit when you want to transform a value after traversal
|
||||
// (postorder).
|
||||
//
|
||||
// The path passed to the given function may not be used after that function
|
||||
// returns, since its backing array is re-used for other calls.
|
||||
type Transformer interface {
|
||||
Enter(Path, Value) (Value, error)
|
||||
Exit(Path, Value) (Value, error)
|
||||
}
|
||||
|
||||
type postorderTransformer struct {
|
||||
callback func(Path, Value) (Value, error)
|
||||
}
|
||||
|
||||
func (t *postorderTransformer) Enter(p Path, v Value) (Value, error) {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (t *postorderTransformer) Exit(p Path, v Value) (Value, error) {
|
||||
return t.callback(p, v)
|
||||
}
|
||||
|
||||
// Transform visits all of the values in a possibly-complex structure,
|
||||
// calling a given function for each value which has an opportunity to
|
||||
// replace that value.
|
||||
@ -77,7 +105,7 @@ func walk(path Path, val Value, cb func(Path, Value) (bool, error)) error {
|
||||
// value constructor functions. An easy way to preserve invariants is to
|
||||
// ensure that the transform function never changes the value type.
|
||||
//
|
||||
// The callback function my halt the walk altogether by
|
||||
// The callback function may halt the walk altogether by
|
||||
// returning a non-nil error. If the returned error is about the element
|
||||
// currently being visited, it is recommended to use the provided path
|
||||
// value to produce a PathError describing that context.
|
||||
@ -86,10 +114,23 @@ func walk(path Path, val Value, cb func(Path, Value) (bool, error)) error {
|
||||
// returns, since its backing array is re-used for other calls.
|
||||
func Transform(val Value, cb func(Path, Value) (Value, error)) (Value, error) {
|
||||
var path Path
|
||||
return transform(path, val, cb)
|
||||
return transform(path, val, &postorderTransformer{cb})
|
||||
}
|
||||
|
||||
func transform(path Path, val Value, cb func(Path, Value) (Value, error)) (Value, error) {
|
||||
// TransformWithTransformer allows the caller to more closely control the
|
||||
// traversal used for transformation. See the documentation for Transformer for
|
||||
// more details.
|
||||
func TransformWithTransformer(val Value, t Transformer) (Value, error) {
|
||||
var path Path
|
||||
return transform(path, val, t)
|
||||
}
|
||||
|
||||
func transform(path Path, val Value, t Transformer) (Value, error) {
|
||||
val, err := t.Enter(path, val)
|
||||
if err != nil {
|
||||
return DynamicVal, err
|
||||
}
|
||||
|
||||
ty := val.Type()
|
||||
var newVal Value
|
||||
|
||||
@ -112,7 +153,7 @@ func transform(path Path, val Value, cb func(Path, Value) (Value, error)) (Value
|
||||
path := append(path, IndexStep{
|
||||
Key: kv,
|
||||
})
|
||||
newEv, err := transform(path, ev, cb)
|
||||
newEv, err := transform(path, ev, t)
|
||||
if err != nil {
|
||||
return DynamicVal, err
|
||||
}
|
||||
@ -143,7 +184,7 @@ func transform(path Path, val Value, cb func(Path, Value) (Value, error)) (Value
|
||||
path := append(path, IndexStep{
|
||||
Key: kv,
|
||||
})
|
||||
newEv, err := transform(path, ev, cb)
|
||||
newEv, err := transform(path, ev, t)
|
||||
if err != nil {
|
||||
return DynamicVal, err
|
||||
}
|
||||
@ -165,7 +206,7 @@ func transform(path Path, val Value, cb func(Path, Value) (Value, error)) (Value
|
||||
path := append(path, GetAttrStep{
|
||||
Name: name,
|
||||
})
|
||||
newAV, err := transform(path, av, cb)
|
||||
newAV, err := transform(path, av, t)
|
||||
if err != nil {
|
||||
return DynamicVal, err
|
||||
}
|
||||
@ -178,5 +219,9 @@ func transform(path Path, val Value, cb func(Path, Value) (Value, error)) (Value
|
||||
newVal = val
|
||||
}
|
||||
|
||||
return cb(path, newVal)
|
||||
newVal, err = t.Exit(path, newVal)
|
||||
if err != nil {
|
||||
return DynamicVal, err
|
||||
}
|
||||
return newVal, err
|
||||
}
|
||||
|
Reference in New Issue
Block a user