functions: ZipmapFunc
This commit is contained in:
parent
2ef56b3e05
commit
4f5c03339a
|
@ -775,6 +775,106 @@ var TransposeFunc = function.New(&function.Spec{
|
|||
},
|
||||
})
|
||||
|
||||
// ValuesFunc contructs a function that returns a list of the map values,
|
||||
// in the order of the sorted keys. This function only works on flat maps.
|
||||
var ValuesFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "values",
|
||||
Type: cty.Map(cty.DynamicPseudoType),
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.List(cty.DynamicPseudoType)),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
mapVar := args[0]
|
||||
keys, err := Keys(mapVar)
|
||||
if err != nil {
|
||||
return cty.NilVal, err
|
||||
}
|
||||
|
||||
var values []cty.Value
|
||||
|
||||
for it := keys.ElementIterator(); it.Next(); {
|
||||
_, v := it.Element()
|
||||
value := mapVar.Index(cty.StringVal(v.AsString()))
|
||||
if !value.Type().Equals(cty.String) && !value.Type().Equals(cty.Number) {
|
||||
return cty.NilVal, fmt.Errorf("values only works on flat maps")
|
||||
}
|
||||
values = append(values, value)
|
||||
}
|
||||
|
||||
if len(values) == 0 {
|
||||
return cty.ListValEmpty(cty.DynamicPseudoType), nil
|
||||
}
|
||||
|
||||
return cty.ListVal(values), nil
|
||||
},
|
||||
})
|
||||
|
||||
// ZipmapFunc contructs a function that constructs a map from a list of keys
|
||||
// and a corresponding list of values.
|
||||
var ZipmapFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "keys",
|
||||
Type: cty.List(cty.String),
|
||||
},
|
||||
{
|
||||
Name: "values",
|
||||
Type: cty.List(cty.DynamicPseudoType),
|
||||
},
|
||||
},
|
||||
Type: func(args []cty.Value) (ret cty.Type, err error) {
|
||||
keys := args[0]
|
||||
values := args[1]
|
||||
|
||||
if keys.LengthInt() == 0 {
|
||||
return cty.Map(cty.DynamicPseudoType), nil
|
||||
}
|
||||
|
||||
if keys.LengthInt() != values.LengthInt() {
|
||||
return cty.NilType, fmt.Errorf("count of keys (%d) does not match count of values (%d)",
|
||||
keys.LengthInt(), values.LengthInt())
|
||||
}
|
||||
|
||||
argTypes := make([]cty.Type, values.LengthInt())
|
||||
index := 0
|
||||
|
||||
for it := values.ElementIterator(); it.Next(); {
|
||||
_, v := it.Element()
|
||||
argTypes[index] = v.Type()
|
||||
index++
|
||||
}
|
||||
|
||||
valType, _ := convert.UnifyUnsafe(argTypes)
|
||||
if valType == cty.NilType {
|
||||
return cty.NilType, fmt.Errorf("list elements must have the same type")
|
||||
}
|
||||
|
||||
return cty.Map(valType), nil
|
||||
},
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
keys := args[0]
|
||||
values := args[1]
|
||||
|
||||
if keys.LengthInt() == 0 {
|
||||
return cty.MapValEmpty(cty.DynamicPseudoType), nil
|
||||
}
|
||||
|
||||
output := make(map[string]cty.Value)
|
||||
|
||||
i := 0
|
||||
for it := keys.ElementIterator(); it.Next(); {
|
||||
_, v := it.Element()
|
||||
val := values.Index(cty.NumberIntVal(int64(i)))
|
||||
output[v.AsString()] = val
|
||||
i++
|
||||
}
|
||||
|
||||
return cty.MapVal(output), nil
|
||||
},
|
||||
})
|
||||
|
||||
// helper function to add an element to a list, if it does not already exist
|
||||
func appendIfMissing(slice []cty.Value, element cty.Value) ([]cty.Value, error) {
|
||||
for _, ele := range slice {
|
||||
|
@ -889,3 +989,14 @@ func Slice(list, start, end cty.Value) (cty.Value, error) {
|
|||
func Transpose(values cty.Value) (cty.Value, error) {
|
||||
return TransposeFunc.Call([]cty.Value{values})
|
||||
}
|
||||
|
||||
// Values returns a list of the map values, in the order of the sorted keys.
|
||||
// This function only works on flat maps.
|
||||
func Values(values cty.Value) (cty.Value, error) {
|
||||
return ValuesFunc.Call([]cty.Value{values})
|
||||
}
|
||||
|
||||
// Zipmap constructs a map from a list of keys and a corresponding list of values.
|
||||
func Zipmap(keys, values cty.Value) (cty.Value, error) {
|
||||
return ZipmapFunc.Call([]cty.Value{keys, values})
|
||||
}
|
||||
|
|
|
@ -1687,3 +1687,160 @@ func TestTranspose(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValues(t *testing.T) {
|
||||
tests := []struct {
|
||||
Values cty.Value
|
||||
Want cty.Value
|
||||
Err bool
|
||||
}{
|
||||
{
|
||||
cty.MapVal(map[string]cty.Value{
|
||||
"hello": cty.StringVal("world"),
|
||||
"what's": cty.StringVal("up"),
|
||||
}),
|
||||
cty.ListVal([]cty.Value{
|
||||
cty.StringVal("world"),
|
||||
cty.StringVal("up"),
|
||||
}),
|
||||
false,
|
||||
},
|
||||
{ // note ordering: keys are sorted first
|
||||
cty.MapVal(map[string]cty.Value{
|
||||
"hello": cty.NumberIntVal(1),
|
||||
"goodbye": cty.NumberIntVal(42),
|
||||
}),
|
||||
cty.ListVal([]cty.Value{
|
||||
cty.NumberIntVal(42),
|
||||
cty.NumberIntVal(1),
|
||||
}),
|
||||
false,
|
||||
},
|
||||
{ // error: map of lists
|
||||
cty.MapVal(map[string]cty.Value{
|
||||
"hello": cty.ListVal([]cty.Value{cty.StringVal("world")}),
|
||||
"what's": cty.ListVal([]cty.Value{cty.StringVal("up")}),
|
||||
}),
|
||||
cty.NilVal,
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("values(%#v)", test.Values), func(t *testing.T) {
|
||||
got, err := Values(test.Values)
|
||||
|
||||
if test.Err {
|
||||
if err == nil {
|
||||
t.Fatal("succeeded; want error")
|
||||
}
|
||||
return
|
||||
} else if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
if !got.RawEquals(test.Want) {
|
||||
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestZipmap(t *testing.T) {
|
||||
list1 := cty.ListVal([]cty.Value{
|
||||
cty.StringVal("hello"),
|
||||
cty.StringVal("world"),
|
||||
})
|
||||
list2 := cty.ListVal([]cty.Value{
|
||||
cty.StringVal("bar"),
|
||||
cty.StringVal("baz"),
|
||||
})
|
||||
list3 := cty.ListVal([]cty.Value{
|
||||
cty.StringVal("hello"),
|
||||
cty.StringVal("there"),
|
||||
cty.StringVal("world"),
|
||||
})
|
||||
list4 := cty.ListVal([]cty.Value{
|
||||
cty.NumberIntVal(1),
|
||||
cty.NumberIntVal(42),
|
||||
})
|
||||
list5 := cty.ListVal([]cty.Value{
|
||||
cty.ListVal([]cty.Value{
|
||||
cty.StringVal("bar"),
|
||||
}),
|
||||
cty.ListVal([]cty.Value{
|
||||
cty.StringVal("baz"),
|
||||
}),
|
||||
})
|
||||
tests := []struct {
|
||||
Keys cty.Value
|
||||
Values cty.Value
|
||||
Want cty.Value
|
||||
Err bool
|
||||
}{
|
||||
{
|
||||
list1,
|
||||
list2,
|
||||
cty.MapVal(map[string]cty.Value{
|
||||
"hello": cty.StringVal("bar"),
|
||||
"world": cty.StringVal("baz"),
|
||||
}),
|
||||
false,
|
||||
},
|
||||
{
|
||||
list1,
|
||||
list4,
|
||||
cty.MapVal(map[string]cty.Value{
|
||||
"hello": cty.NumberIntVal(1),
|
||||
"world": cty.NumberIntVal(42),
|
||||
}),
|
||||
false,
|
||||
},
|
||||
{ // length mismatch
|
||||
list1,
|
||||
list3,
|
||||
cty.NilVal,
|
||||
true,
|
||||
},
|
||||
{ // map of lists
|
||||
list1,
|
||||
list5,
|
||||
cty.MapVal(map[string]cty.Value{
|
||||
"hello": cty.ListVal([]cty.Value{cty.StringVal("bar")}),
|
||||
"world": cty.ListVal([]cty.Value{cty.StringVal("baz")}),
|
||||
}),
|
||||
false,
|
||||
},
|
||||
{ // empty input returns an empty map
|
||||
cty.ListValEmpty(cty.String),
|
||||
cty.ListValEmpty(cty.String),
|
||||
cty.MapValEmpty(cty.DynamicPseudoType),
|
||||
false,
|
||||
},
|
||||
{ // keys cannot be a list
|
||||
list5,
|
||||
list1,
|
||||
cty.NilVal,
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("zipmap(%#v, %#v)", test.Keys, test.Values), func(t *testing.T) {
|
||||
got, err := Zipmap(test.Keys, test.Values)
|
||||
|
||||
if test.Err {
|
||||
if err == nil {
|
||||
t.Fatal("succeeded; want error")
|
||||
}
|
||||
return
|
||||
} else if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
if !got.RawEquals(test.Want) {
|
||||
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,8 +95,8 @@ func (s *Scope) Functions() map[string]function.Function {
|
|||
"upper": stdlib.UpperFunc,
|
||||
"urlencode": funcs.URLEncodeFunc,
|
||||
"uuid": funcs.UUIDFunc,
|
||||
"values": unimplFunc, // TODO
|
||||
"zipmap": unimplFunc, // TODO
|
||||
"values": funcs.ValuesFunc,
|
||||
"zipmap": funcs.ZipmapFunc,
|
||||
}
|
||||
|
||||
if s.PureOnly {
|
||||
|
|
Loading…
Reference in New Issue