vendor vendors
This commit is contained in:
parent
f013164298
commit
e195c627fb
|
@ -138,6 +138,15 @@ func getConversionKnown(in cty.Type, out cty.Type, unsafe bool) conversion {
|
||||||
outEty := out.ElementType()
|
outEty := out.ElementType()
|
||||||
return conversionObjectToMap(in, outEty, unsafe)
|
return conversionObjectToMap(in, outEty, unsafe)
|
||||||
|
|
||||||
|
case out.IsObjectType() && in.IsMapType():
|
||||||
|
if !unsafe {
|
||||||
|
// Converting a map to an object is an "unsafe" conversion,
|
||||||
|
// because we don't know if all the map keys will correspond to
|
||||||
|
// object attributes.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return conversionMapToObject(in, out, unsafe)
|
||||||
|
|
||||||
case in.IsCapsuleType() || out.IsCapsuleType():
|
case in.IsCapsuleType() || out.IsCapsuleType():
|
||||||
if !unsafe {
|
if !unsafe {
|
||||||
// Capsule types can only participate in "unsafe" conversions,
|
// Capsule types can only participate in "unsafe" conversions,
|
||||||
|
|
|
@ -15,18 +15,18 @@ func conversionCollectionToList(ety cty.Type, conv conversion) conversion {
|
||||||
return func(val cty.Value, path cty.Path) (cty.Value, error) {
|
return func(val cty.Value, path cty.Path) (cty.Value, error) {
|
||||||
elems := make([]cty.Value, 0, val.LengthInt())
|
elems := make([]cty.Value, 0, val.LengthInt())
|
||||||
i := int64(0)
|
i := int64(0)
|
||||||
path = append(path, nil)
|
elemPath := append(path.Copy(), nil)
|
||||||
it := val.ElementIterator()
|
it := val.ElementIterator()
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
_, val := it.Element()
|
_, val := it.Element()
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
path[len(path)-1] = cty.IndexStep{
|
elemPath[len(elemPath)-1] = cty.IndexStep{
|
||||||
Key: cty.NumberIntVal(i),
|
Key: cty.NumberIntVal(i),
|
||||||
}
|
}
|
||||||
|
|
||||||
if conv != nil {
|
if conv != nil {
|
||||||
val, err = conv(val, path)
|
val, err = conv(val, elemPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cty.NilVal, err
|
return cty.NilVal, err
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,9 @@ func conversionCollectionToList(ety cty.Type, conv conversion) conversion {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(elems) == 0 {
|
if len(elems) == 0 {
|
||||||
|
if ety == cty.DynamicPseudoType {
|
||||||
|
ety = val.Type().ElementType()
|
||||||
|
}
|
||||||
return cty.ListValEmpty(ety), nil
|
return cty.ListValEmpty(ety), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,18 +58,18 @@ func conversionCollectionToSet(ety cty.Type, conv conversion) conversion {
|
||||||
return func(val cty.Value, path cty.Path) (cty.Value, error) {
|
return func(val cty.Value, path cty.Path) (cty.Value, error) {
|
||||||
elems := make([]cty.Value, 0, val.LengthInt())
|
elems := make([]cty.Value, 0, val.LengthInt())
|
||||||
i := int64(0)
|
i := int64(0)
|
||||||
path = append(path, nil)
|
elemPath := append(path.Copy(), nil)
|
||||||
it := val.ElementIterator()
|
it := val.ElementIterator()
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
_, val := it.Element()
|
_, val := it.Element()
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
path[len(path)-1] = cty.IndexStep{
|
elemPath[len(elemPath)-1] = cty.IndexStep{
|
||||||
Key: cty.NumberIntVal(i),
|
Key: cty.NumberIntVal(i),
|
||||||
}
|
}
|
||||||
|
|
||||||
if conv != nil {
|
if conv != nil {
|
||||||
val, err = conv(val, path)
|
val, err = conv(val, elemPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cty.NilVal, err
|
return cty.NilVal, err
|
||||||
}
|
}
|
||||||
|
@ -77,6 +80,11 @@ func conversionCollectionToSet(ety cty.Type, conv conversion) conversion {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(elems) == 0 {
|
if len(elems) == 0 {
|
||||||
|
// Prefer a concrete type over a dynamic type when returning an
|
||||||
|
// empty set
|
||||||
|
if ety == cty.DynamicPseudoType {
|
||||||
|
ety = val.Type().ElementType()
|
||||||
|
}
|
||||||
return cty.SetValEmpty(ety), nil
|
return cty.SetValEmpty(ety), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,13 +101,13 @@ func conversionCollectionToSet(ety cty.Type, conv conversion) conversion {
|
||||||
func conversionCollectionToMap(ety cty.Type, conv conversion) conversion {
|
func conversionCollectionToMap(ety cty.Type, conv conversion) conversion {
|
||||||
return func(val cty.Value, path cty.Path) (cty.Value, error) {
|
return func(val cty.Value, path cty.Path) (cty.Value, error) {
|
||||||
elems := make(map[string]cty.Value, 0)
|
elems := make(map[string]cty.Value, 0)
|
||||||
path = append(path, nil)
|
elemPath := append(path.Copy(), nil)
|
||||||
it := val.ElementIterator()
|
it := val.ElementIterator()
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
key, val := it.Element()
|
key, val := it.Element()
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
path[len(path)-1] = cty.IndexStep{
|
elemPath[len(elemPath)-1] = cty.IndexStep{
|
||||||
Key: key,
|
Key: key,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,11 +115,11 @@ func conversionCollectionToMap(ety cty.Type, conv conversion) conversion {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Should never happen, because keys can only be numbers or
|
// Should never happen, because keys can only be numbers or
|
||||||
// strings and both can convert to string.
|
// strings and both can convert to string.
|
||||||
return cty.DynamicVal, path.NewErrorf("cannot convert key type %s to string for map", key.Type().FriendlyName())
|
return cty.DynamicVal, elemPath.NewErrorf("cannot convert key type %s to string for map", key.Type().FriendlyName())
|
||||||
}
|
}
|
||||||
|
|
||||||
if conv != nil {
|
if conv != nil {
|
||||||
val, err = conv(val, path)
|
val, err = conv(val, elemPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cty.NilVal, err
|
return cty.NilVal, err
|
||||||
}
|
}
|
||||||
|
@ -121,9 +129,25 @@ func conversionCollectionToMap(ety cty.Type, conv conversion) conversion {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(elems) == 0 {
|
if len(elems) == 0 {
|
||||||
|
// Prefer a concrete type over a dynamic type when returning an
|
||||||
|
// empty map
|
||||||
|
if ety == cty.DynamicPseudoType {
|
||||||
|
ety = val.Type().ElementType()
|
||||||
|
}
|
||||||
return cty.MapValEmpty(ety), nil
|
return cty.MapValEmpty(ety), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ety.IsCollectionType() || ety.IsObjectType() {
|
||||||
|
var err error
|
||||||
|
if elems, err = conversionUnifyCollectionElements(elems, path, false); err != nil {
|
||||||
|
return cty.NilVal, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := conversionCheckMapElementTypes(elems, path); err != nil {
|
||||||
|
return cty.NilVal, err
|
||||||
|
}
|
||||||
|
|
||||||
return cty.MapVal(elems), nil
|
return cty.MapVal(elems), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,20 +195,20 @@ func conversionTupleToSet(tupleType cty.Type, listEty cty.Type, unsafe bool) con
|
||||||
// element conversions in elemConvs
|
// element conversions in elemConvs
|
||||||
return func(val cty.Value, path cty.Path) (cty.Value, error) {
|
return func(val cty.Value, path cty.Path) (cty.Value, error) {
|
||||||
elems := make([]cty.Value, 0, len(elemConvs))
|
elems := make([]cty.Value, 0, len(elemConvs))
|
||||||
path = append(path, nil)
|
elemPath := append(path.Copy(), nil)
|
||||||
i := int64(0)
|
i := int64(0)
|
||||||
it := val.ElementIterator()
|
it := val.ElementIterator()
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
_, val := it.Element()
|
_, val := it.Element()
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
path[len(path)-1] = cty.IndexStep{
|
elemPath[len(elemPath)-1] = cty.IndexStep{
|
||||||
Key: cty.NumberIntVal(i),
|
Key: cty.NumberIntVal(i),
|
||||||
}
|
}
|
||||||
|
|
||||||
conv := elemConvs[i]
|
conv := elemConvs[i]
|
||||||
if conv != nil {
|
if conv != nil {
|
||||||
val, err = conv(val, path)
|
val, err = conv(val, elemPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cty.NilVal, err
|
return cty.NilVal, err
|
||||||
}
|
}
|
||||||
|
@ -241,20 +265,20 @@ func conversionTupleToList(tupleType cty.Type, listEty cty.Type, unsafe bool) co
|
||||||
// element conversions in elemConvs
|
// element conversions in elemConvs
|
||||||
return func(val cty.Value, path cty.Path) (cty.Value, error) {
|
return func(val cty.Value, path cty.Path) (cty.Value, error) {
|
||||||
elems := make([]cty.Value, 0, len(elemConvs))
|
elems := make([]cty.Value, 0, len(elemConvs))
|
||||||
path = append(path, nil)
|
elemPath := append(path.Copy(), nil)
|
||||||
i := int64(0)
|
i := int64(0)
|
||||||
it := val.ElementIterator()
|
it := val.ElementIterator()
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
_, val := it.Element()
|
_, val := it.Element()
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
path[len(path)-1] = cty.IndexStep{
|
elemPath[len(elemPath)-1] = cty.IndexStep{
|
||||||
Key: cty.NumberIntVal(i),
|
Key: cty.NumberIntVal(i),
|
||||||
}
|
}
|
||||||
|
|
||||||
conv := elemConvs[i]
|
conv := elemConvs[i]
|
||||||
if conv != nil {
|
if conv != nil {
|
||||||
val, err = conv(val, path)
|
val, err = conv(val, elemPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cty.NilVal, err
|
return cty.NilVal, err
|
||||||
}
|
}
|
||||||
|
@ -315,19 +339,19 @@ func conversionObjectToMap(objectType cty.Type, mapEty cty.Type, unsafe bool) co
|
||||||
// element conversions in elemConvs
|
// element conversions in elemConvs
|
||||||
return func(val cty.Value, path cty.Path) (cty.Value, error) {
|
return func(val cty.Value, path cty.Path) (cty.Value, error) {
|
||||||
elems := make(map[string]cty.Value, len(elemConvs))
|
elems := make(map[string]cty.Value, len(elemConvs))
|
||||||
path = append(path, nil)
|
elemPath := append(path.Copy(), nil)
|
||||||
it := val.ElementIterator()
|
it := val.ElementIterator()
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
name, val := it.Element()
|
name, val := it.Element()
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
path[len(path)-1] = cty.IndexStep{
|
elemPath[len(elemPath)-1] = cty.IndexStep{
|
||||||
Key: name,
|
Key: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
conv := elemConvs[name.AsString()]
|
conv := elemConvs[name.AsString()]
|
||||||
if conv != nil {
|
if conv != nil {
|
||||||
val, err = conv(val, path)
|
val, err = conv(val, elemPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cty.NilVal, err
|
return cty.NilVal, err
|
||||||
}
|
}
|
||||||
|
@ -335,6 +359,130 @@ func conversionObjectToMap(objectType cty.Type, mapEty cty.Type, unsafe bool) co
|
||||||
elems[name.AsString()] = val
|
elems[name.AsString()] = val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if mapEty.IsCollectionType() || mapEty.IsObjectType() {
|
||||||
|
var err error
|
||||||
|
if elems, err = conversionUnifyCollectionElements(elems, path, unsafe); err != nil {
|
||||||
|
return cty.NilVal, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := conversionCheckMapElementTypes(elems, path); err != nil {
|
||||||
|
return cty.NilVal, err
|
||||||
|
}
|
||||||
|
|
||||||
return cty.MapVal(elems), nil
|
return cty.MapVal(elems), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// conversionMapToObject returns a conversion that will take a value of the
|
||||||
|
// given map type and return an object of the given type. The object attribute
|
||||||
|
// types must all be compatible with the map element type.
|
||||||
|
//
|
||||||
|
// Will panic if the given mapType and objType are not maps and objects
|
||||||
|
// respectively.
|
||||||
|
func conversionMapToObject(mapType cty.Type, objType cty.Type, unsafe bool) conversion {
|
||||||
|
objectAtys := objType.AttributeTypes()
|
||||||
|
mapEty := mapType.ElementType()
|
||||||
|
|
||||||
|
elemConvs := make(map[string]conversion, len(objectAtys))
|
||||||
|
for name, objectAty := range objectAtys {
|
||||||
|
if objectAty.Equals(mapEty) {
|
||||||
|
// no conversion required
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
elemConvs[name] = getConversion(mapEty, objectAty, unsafe)
|
||||||
|
if elemConvs[name] == nil {
|
||||||
|
// If any of our element conversions are impossible, then the our
|
||||||
|
// whole conversion is impossible.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we fall out here then a conversion is possible, using the
|
||||||
|
// element conversions in elemConvs
|
||||||
|
return func(val cty.Value, path cty.Path) (cty.Value, error) {
|
||||||
|
elems := make(map[string]cty.Value, len(elemConvs))
|
||||||
|
elemPath := append(path.Copy(), nil)
|
||||||
|
it := val.ElementIterator()
|
||||||
|
for it.Next() {
|
||||||
|
name, val := it.Element()
|
||||||
|
|
||||||
|
// if there is no corresponding attribute, we skip this key
|
||||||
|
if _, ok := objectAtys[name.AsString()]; !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
elemPath[len(elemPath)-1] = cty.IndexStep{
|
||||||
|
Key: name,
|
||||||
|
}
|
||||||
|
|
||||||
|
conv := elemConvs[name.AsString()]
|
||||||
|
if conv != nil {
|
||||||
|
val, err = conv(val, elemPath)
|
||||||
|
if err != nil {
|
||||||
|
return cty.NilVal, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
elems[name.AsString()] = val
|
||||||
|
}
|
||||||
|
|
||||||
|
return cty.ObjectVal(elems), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func conversionUnifyCollectionElements(elems map[string]cty.Value, path cty.Path, unsafe bool) (map[string]cty.Value, error) {
|
||||||
|
elemTypes := make([]cty.Type, 0, len(elems))
|
||||||
|
for _, elem := range elems {
|
||||||
|
elemTypes = append(elemTypes, elem.Type())
|
||||||
|
}
|
||||||
|
unifiedType, _ := unify(elemTypes, unsafe)
|
||||||
|
if unifiedType == cty.NilType {
|
||||||
|
}
|
||||||
|
|
||||||
|
unifiedElems := make(map[string]cty.Value)
|
||||||
|
elemPath := append(path.Copy(), nil)
|
||||||
|
|
||||||
|
for name, elem := range elems {
|
||||||
|
if elem.Type().Equals(unifiedType) {
|
||||||
|
unifiedElems[name] = elem
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
conv := getConversion(elem.Type(), unifiedType, unsafe)
|
||||||
|
if conv == nil {
|
||||||
|
}
|
||||||
|
elemPath[len(elemPath)-1] = cty.IndexStep{
|
||||||
|
Key: cty.StringVal(name),
|
||||||
|
}
|
||||||
|
val, err := conv(elem, elemPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
unifiedElems[name] = val
|
||||||
|
}
|
||||||
|
|
||||||
|
return unifiedElems, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func conversionCheckMapElementTypes(elems map[string]cty.Value, path cty.Path) error {
|
||||||
|
elementType := cty.NilType
|
||||||
|
elemPath := append(path.Copy(), nil)
|
||||||
|
|
||||||
|
for name, elem := range elems {
|
||||||
|
if elementType == cty.NilType {
|
||||||
|
elementType = elem.Type()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !elementType.Equals(elem.Type()) {
|
||||||
|
elemPath[len(elemPath)-1] = cty.IndexStep{
|
||||||
|
Key: cty.StringVal(name),
|
||||||
|
}
|
||||||
|
return elemPath.NewErrorf("%s is required", elementType.FriendlyName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -28,11 +28,14 @@ func unify(types []cty.Type, unsafe bool) (cty.Type, []Conversion) {
|
||||||
// a subset of that type, which would be a much less useful conversion for
|
// a subset of that type, which would be a much less useful conversion for
|
||||||
// unification purposes.
|
// unification purposes.
|
||||||
{
|
{
|
||||||
|
mapCt := 0
|
||||||
objectCt := 0
|
objectCt := 0
|
||||||
tupleCt := 0
|
tupleCt := 0
|
||||||
dynamicCt := 0
|
dynamicCt := 0
|
||||||
for _, ty := range types {
|
for _, ty := range types {
|
||||||
switch {
|
switch {
|
||||||
|
case ty.IsMapType():
|
||||||
|
mapCt++
|
||||||
case ty.IsObjectType():
|
case ty.IsObjectType():
|
||||||
objectCt++
|
objectCt++
|
||||||
case ty.IsTupleType():
|
case ty.IsTupleType():
|
||||||
|
@ -44,6 +47,8 @@ func unify(types []cty.Type, unsafe bool) (cty.Type, []Conversion) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
|
case mapCt > 0 && (mapCt+dynamicCt) == len(types):
|
||||||
|
return unifyMapTypes(types, unsafe, dynamicCt > 0)
|
||||||
case objectCt > 0 && (objectCt+dynamicCt) == len(types):
|
case objectCt > 0 && (objectCt+dynamicCt) == len(types):
|
||||||
return unifyObjectTypes(types, unsafe, dynamicCt > 0)
|
return unifyObjectTypes(types, unsafe, dynamicCt > 0)
|
||||||
case tupleCt > 0 && (tupleCt+dynamicCt) == len(types):
|
case tupleCt > 0 && (tupleCt+dynamicCt) == len(types):
|
||||||
|
@ -95,6 +100,44 @@ Preferences:
|
||||||
return cty.NilType, nil
|
return cty.NilType, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func unifyMapTypes(types []cty.Type, unsafe bool, hasDynamic bool) (cty.Type, []Conversion) {
|
||||||
|
// If we had any dynamic types in the input here then we can't predict
|
||||||
|
// what path we'll take through here once these become known types, so
|
||||||
|
// we'll conservatively produce DynamicVal for these.
|
||||||
|
if hasDynamic {
|
||||||
|
return unifyAllAsDynamic(types)
|
||||||
|
}
|
||||||
|
|
||||||
|
elemTypes := make([]cty.Type, 0, len(types))
|
||||||
|
for _, ty := range types {
|
||||||
|
elemTypes = append(elemTypes, ty.ElementType())
|
||||||
|
}
|
||||||
|
retElemType, _ := unify(elemTypes, unsafe)
|
||||||
|
if retElemType == cty.NilType {
|
||||||
|
return cty.NilType, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
retTy := cty.Map(retElemType)
|
||||||
|
|
||||||
|
conversions := make([]Conversion, len(types))
|
||||||
|
for i, ty := range types {
|
||||||
|
if ty.Equals(retTy) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if unsafe {
|
||||||
|
conversions[i] = GetConversionUnsafe(ty, retTy)
|
||||||
|
} else {
|
||||||
|
conversions[i] = GetConversion(ty, retTy)
|
||||||
|
}
|
||||||
|
if conversions[i] == nil {
|
||||||
|
// Shouldn't be reachable, since we were able to unify
|
||||||
|
return cty.NilType, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retTy, conversions
|
||||||
|
}
|
||||||
|
|
||||||
func unifyObjectTypes(types []cty.Type, unsafe bool, hasDynamic bool) (cty.Type, []Conversion) {
|
func unifyObjectTypes(types []cty.Type, unsafe bool, hasDynamic bool) (cty.Type, []Conversion) {
|
||||||
// If we had any dynamic types in the input here then we can't predict
|
// If we had any dynamic types in the input here then we can't predict
|
||||||
// what path we'll take through here once these become known types, so
|
// what path we'll take through here once these become known types, so
|
||||||
|
|
|
@ -299,7 +299,7 @@ var ContainsFunc = function.New(&function.Spec{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Type: function.StaticReturnType(cty.Bool),
|
Type: function.StaticReturnType(cty.Bool),
|
||||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||||
arg := args[0]
|
arg := args[0]
|
||||||
ty := arg.Type()
|
ty := arg.Type()
|
||||||
|
|
||||||
|
@ -307,12 +307,39 @@ var ContainsFunc = function.New(&function.Spec{
|
||||||
return cty.NilVal, errors.New("argument must be list, tuple, or set")
|
return cty.NilVal, errors.New("argument must be list, tuple, or set")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = Index(cty.TupleVal(arg.AsValueSlice()), args[1])
|
if args[0].IsNull() {
|
||||||
if err != nil {
|
return cty.NilVal, errors.New("cannot search a nil list or set")
|
||||||
|
}
|
||||||
|
|
||||||
|
if args[0].LengthInt() == 0 {
|
||||||
return cty.False, nil
|
return cty.False, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return cty.True, nil
|
if !args[0].IsKnown() || !args[1].IsKnown() {
|
||||||
|
return cty.UnknownVal(cty.Bool), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
containsUnknown := false
|
||||||
|
for it := args[0].ElementIterator(); it.Next(); {
|
||||||
|
_, v := it.Element()
|
||||||
|
eq := args[1].Equals(v)
|
||||||
|
if !eq.IsKnown() {
|
||||||
|
// We may have an unknown value which could match later, but we
|
||||||
|
// first need to continue checking all values for an exact
|
||||||
|
// match.
|
||||||
|
containsUnknown = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if eq.True() {
|
||||||
|
return cty.True, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if containsUnknown {
|
||||||
|
return cty.UnknownVal(cty.Bool), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return cty.False, nil
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -566,19 +593,12 @@ var LookupFunc = function.New(&function.Spec{
|
||||||
Name: "key",
|
Name: "key",
|
||||||
Type: cty.String,
|
Type: cty.String,
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
VarParam: &function.Parameter{
|
Name: "default",
|
||||||
Name: "default",
|
Type: cty.DynamicPseudoType,
|
||||||
Type: cty.DynamicPseudoType,
|
},
|
||||||
AllowUnknown: true,
|
|
||||||
AllowDynamicType: true,
|
|
||||||
AllowNull: true,
|
|
||||||
},
|
},
|
||||||
Type: func(args []cty.Value) (ret cty.Type, err error) {
|
Type: func(args []cty.Value) (ret cty.Type, err error) {
|
||||||
if len(args) < 1 || len(args) > 3 {
|
|
||||||
return cty.NilType, fmt.Errorf("lookup() takes two or three arguments, got %d", len(args))
|
|
||||||
}
|
|
||||||
|
|
||||||
ty := args[0].Type()
|
ty := args[0].Type()
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
@ -609,13 +629,7 @@ var LookupFunc = function.New(&function.Spec{
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||||
var defaultVal cty.Value
|
defaultVal := args[2]
|
||||||
defaultValueSet := false
|
|
||||||
|
|
||||||
if len(args) == 3 {
|
|
||||||
defaultVal = args[2]
|
|
||||||
defaultValueSet = true
|
|
||||||
}
|
|
||||||
|
|
||||||
mapVar := args[0]
|
mapVar := args[0]
|
||||||
lookupKey := args[1].AsString()
|
lookupKey := args[1].AsString()
|
||||||
|
@ -632,48 +646,128 @@ var LookupFunc = function.New(&function.Spec{
|
||||||
return mapVar.Index(cty.StringVal(lookupKey)), nil
|
return mapVar.Index(cty.StringVal(lookupKey)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if defaultValueSet {
|
defaultVal, err = convert.Convert(defaultVal, retType)
|
||||||
defaultVal, err = convert.Convert(defaultVal, retType)
|
if err != nil {
|
||||||
if err != nil {
|
return cty.NilVal, err
|
||||||
return cty.NilVal, err
|
|
||||||
}
|
|
||||||
return defaultVal, nil
|
|
||||||
}
|
}
|
||||||
|
return defaultVal, nil
|
||||||
return cty.UnknownVal(cty.DynamicPseudoType), fmt.Errorf(
|
|
||||||
"lookup failed to find '%s'", lookupKey)
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// MergeFunc is a function that takes an arbitrary number of maps and
|
// MergeFunc constructs a function that takes an arbitrary number of maps or
|
||||||
// returns a single map that contains a merged set of elements from all of the maps.
|
// objects, and returns a single value that contains a merged set of keys and
|
||||||
|
// values from all of the inputs.
|
||||||
//
|
//
|
||||||
// If more than one given map defines the same key then the one that is later in
|
// If more than one given map or object defines the same key then the one that
|
||||||
// the argument sequence takes precedence.
|
// is later in the argument sequence takes precedence.
|
||||||
var MergeFunc = function.New(&function.Spec{
|
var MergeFunc = function.New(&function.Spec{
|
||||||
Params: []function.Parameter{},
|
Params: []function.Parameter{},
|
||||||
VarParam: &function.Parameter{
|
VarParam: &function.Parameter{
|
||||||
Name: "maps",
|
Name: "maps",
|
||||||
Type: cty.DynamicPseudoType,
|
Type: cty.DynamicPseudoType,
|
||||||
AllowDynamicType: true,
|
AllowDynamicType: true,
|
||||||
|
AllowNull: true,
|
||||||
|
},
|
||||||
|
Type: func(args []cty.Value) (cty.Type, error) {
|
||||||
|
// empty args is accepted, so assume an empty object since we have no
|
||||||
|
// key-value types.
|
||||||
|
if len(args) == 0 {
|
||||||
|
return cty.EmptyObject, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect the possible object attrs
|
||||||
|
attrs := map[string]cty.Type{}
|
||||||
|
|
||||||
|
first := cty.NilType
|
||||||
|
matching := true
|
||||||
|
attrsKnown := true
|
||||||
|
for i, arg := range args {
|
||||||
|
ty := arg.Type()
|
||||||
|
// any dynamic args mean we can't compute a type
|
||||||
|
if ty.Equals(cty.DynamicPseudoType) {
|
||||||
|
return cty.DynamicPseudoType, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for invalid arguments
|
||||||
|
if !ty.IsMapType() && !ty.IsObjectType() {
|
||||||
|
return cty.NilType, fmt.Errorf("arguments must be maps or objects, got %#v", ty.FriendlyName())
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case ty.IsObjectType() && !arg.IsNull():
|
||||||
|
for attr, aty := range ty.AttributeTypes() {
|
||||||
|
attrs[attr] = aty
|
||||||
|
}
|
||||||
|
case ty.IsMapType():
|
||||||
|
switch {
|
||||||
|
case arg.IsNull():
|
||||||
|
// pass, nothing to add
|
||||||
|
case arg.IsKnown():
|
||||||
|
ety := arg.Type().ElementType()
|
||||||
|
for it := arg.ElementIterator(); it.Next(); {
|
||||||
|
attr, _ := it.Element()
|
||||||
|
attrs[attr.AsString()] = ety
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// any unknown maps means we don't know all possible attrs
|
||||||
|
// for the return type
|
||||||
|
attrsKnown = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// record the first argument type for comparison
|
||||||
|
if i == 0 {
|
||||||
|
first = arg.Type()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ty.Equals(first) && matching {
|
||||||
|
matching = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the types all match, so use the first argument type
|
||||||
|
if matching {
|
||||||
|
return first, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// We had a mix of unknown maps and objects, so we can't predict the
|
||||||
|
// attributes
|
||||||
|
if !attrsKnown {
|
||||||
|
return cty.DynamicPseudoType, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return cty.Object(attrs), nil
|
||||||
},
|
},
|
||||||
Type: function.StaticReturnType(cty.DynamicPseudoType),
|
|
||||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||||
outputMap := make(map[string]cty.Value)
|
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 {
|
for _, arg := range args {
|
||||||
if !arg.IsWhollyKnown() {
|
if arg.IsNull() {
|
||||||
return cty.UnknownVal(retType), nil
|
continue
|
||||||
}
|
} else {
|
||||||
if !arg.Type().IsObjectType() && !arg.Type().IsMapType() {
|
allNull = false
|
||||||
return cty.NilVal, fmt.Errorf("arguments must be maps or objects, got %#v", arg.Type().FriendlyName())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for it := arg.ElementIterator(); it.Next(); {
|
for it := arg.ElementIterator(); it.Next(); {
|
||||||
k, v := it.Element()
|
k, v := it.Element()
|
||||||
outputMap[k.AsString()] = v
|
outputMap[k.AsString()] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cty.ObjectVal(outputMap), nil
|
|
||||||
|
switch {
|
||||||
|
case allNull:
|
||||||
|
return cty.NullVal(retType), nil
|
||||||
|
case retType.IsMapType():
|
||||||
|
return cty.MapVal(outputMap), nil
|
||||||
|
case retType.IsObjectType(), retType.Equals(cty.DynamicPseudoType):
|
||||||
|
return cty.ObjectVal(outputMap), nil
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unexpected return type: %#v", retType))
|
||||||
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1184,8 +1278,8 @@ func Keys(inputMap cty.Value) (cty.Value, error) {
|
||||||
// Lookup performs a dynamic lookup into a map.
|
// Lookup performs a dynamic lookup into a map.
|
||||||
// There are two required arguments, map and key, plus an optional default,
|
// There are two required arguments, map and key, plus an optional default,
|
||||||
// which is a value to return if no key is found in map.
|
// which is a value to return if no key is found in map.
|
||||||
func Lookup(args ...cty.Value) (cty.Value, error) {
|
func Lookup(inputMap, key, defaultValue cty.Value) (cty.Value, error) {
|
||||||
return LookupFunc.Call(args)
|
return LookupFunc.Call([]cty.Value{inputMap, key, defaultValue})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge takes an arbitrary number of maps and returns a single map that contains
|
// Merge takes an arbitrary number of maps and returns a single map that contains
|
||||||
|
|
|
@ -217,7 +217,7 @@ var TimeAddFunc = function.New(&function.Spec{
|
||||||
},
|
},
|
||||||
Type: function.StaticReturnType(cty.String),
|
Type: function.StaticReturnType(cty.String),
|
||||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||||
ts, err := time.Parse(time.RFC3339, args[0].AsString())
|
ts, err := parseTimestamp(args[0].AsString())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cty.UnknownVal(cty.String), err
|
return cty.UnknownVal(cty.String), err
|
||||||
}
|
}
|
||||||
|
|
|
@ -638,7 +638,7 @@ github.com/yandex-cloud/go-sdk/pkg/retry
|
||||||
github.com/yandex-cloud/go-sdk/pkg/sdkerrors
|
github.com/yandex-cloud/go-sdk/pkg/sdkerrors
|
||||||
github.com/yandex-cloud/go-sdk/pkg/singleflight
|
github.com/yandex-cloud/go-sdk/pkg/singleflight
|
||||||
github.com/yandex-cloud/go-sdk/sdkresolvers
|
github.com/yandex-cloud/go-sdk/sdkresolvers
|
||||||
# github.com/zclconf/go-cty v1.2.1 => github.com/azr/go-cty v1.1.1-0.20200203143058-28fcda2fe0cc
|
# github.com/zclconf/go-cty v1.3.1
|
||||||
github.com/zclconf/go-cty/cty
|
github.com/zclconf/go-cty/cty
|
||||||
github.com/zclconf/go-cty/cty/convert
|
github.com/zclconf/go-cty/cty/convert
|
||||||
github.com/zclconf/go-cty/cty/function
|
github.com/zclconf/go-cty/cty/function
|
||||||
|
|
Loading…
Reference in New Issue