Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

function/stdlib: ChunklistFunc precise mark handling #100

Merged
merged 1 commit into from
Apr 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
39 changes: 23 additions & 16 deletions cty/function/stdlib/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,48 +398,55 @@ var DistinctFunc = function.New(&function.Spec{
var ChunklistFunc = function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "list",
Type: cty.List(cty.DynamicPseudoType),
Name: "list",
Type: cty.List(cty.DynamicPseudoType),
AllowMarked: true,
},
{
Name: "size",
Type: cty.Number,
Name: "size",
Type: cty.Number,
AllowMarked: true,
},
},
Type: func(args []cty.Value) (cty.Type, error) {
return cty.List(args[0].Type()), nil
},
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
listVal := args[0]
if !listVal.IsKnown() {
return cty.UnknownVal(retType), nil
}

if listVal.LengthInt() == 0 {
return cty.ListValEmpty(listVal.Type()), nil
}
sizeVal := args[1]
listVal, listMarks := listVal.Unmark()
sizeVal, sizeMarks := sizeVal.Unmark()
// All return paths below must include .WithMarks(retMarks) to propagate
// the top-level marks into the return value. Deep marks inside the
// list will just propagate naturally because we treat those values
// as opaque here.
retMarks := cty.NewValueMarks(listMarks, sizeMarks)

var size int
err = gocty.FromCtyValue(args[1], &size)
err = gocty.FromCtyValue(sizeVal, &size)
if err != nil {
return cty.NilVal, fmt.Errorf("invalid index: %s", err)
return cty.NilVal, fmt.Errorf("invalid size: %s", err)
}

if size < 0 {
return cty.NilVal, errors.New("the size argument must be positive")
}

if listVal.LengthInt() == 0 {
return cty.ListValEmpty(listVal.Type()).WithMarks(retMarks), nil
}

output := make([]cty.Value, 0)

// if size is 0, returns a list made of the initial list
if size == 0 {
output = append(output, listVal)
return cty.ListVal(output), nil
return cty.ListVal(output).WithMarks(retMarks), nil
}

chunk := make([]cty.Value, 0)

l := args[0].LengthInt()
l := listVal.LengthInt()
i := 0

for it := listVal.ElementIterator(); it.Next(); {
Expand All @@ -454,7 +461,7 @@ var ChunklistFunc = function.New(&function.Spec{
i++
}

return cty.ListVal(output), nil
return cty.ListVal(output).WithMarks(retMarks), nil
},
})

Expand Down
227 changes: 227 additions & 0 deletions cty/function/stdlib/collection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,233 @@ func TestHasIndex(t *testing.T) {
}
}

func TestChunklist(t *testing.T) {
tests := []struct {
List cty.Value
Len cty.Value
Want cty.Value
Err string
}{
{
cty.ListValEmpty(cty.String),
cty.NumberIntVal(2),
cty.ListValEmpty(cty.List(cty.String)),
``,
},
{
cty.UnknownVal(cty.List(cty.String)),
cty.NumberIntVal(2),
cty.UnknownVal(cty.List(cty.List(cty.String))),
``,
},
{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
}),
cty.NumberIntVal(2),
cty.ListVal([]cty.Value{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
}),
}),
``,
},
{
cty.ListVal([]cty.Value{
cty.StringVal("a").Mark("b"),
}),
cty.NumberIntVal(2),
cty.ListVal([]cty.Value{
cty.ListVal([]cty.Value{
cty.StringVal("a").Mark("b"),
}),
}),
``,
},
{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
}).Mark("a"),
cty.NumberIntVal(2),
cty.ListVal([]cty.Value{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
}),
}).Mark("a"),
``,
},
{
cty.ListVal([]cty.Value{
cty.StringVal("a").Mark("b"),
}).Mark("a"),
cty.NumberIntVal(2),
cty.ListVal([]cty.Value{
cty.ListVal([]cty.Value{
cty.StringVal("a").Mark("b"),
}),
}).Mark("a"),
``,
},
{
cty.ListVal([]cty.Value{
cty.UnknownVal(cty.String),
}),
cty.NumberIntVal(2),
cty.ListVal([]cty.Value{
cty.ListVal([]cty.Value{
cty.UnknownVal(cty.String),
}),
}),
``,
},
{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
}),
cty.NumberIntVal(2),
cty.ListVal([]cty.Value{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
}),
}),
``,
},
{ // Multiple result elements, one shorter
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
cty.StringVal("c"),
}),
cty.NumberIntVal(2),
cty.ListVal([]cty.Value{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
}),
cty.ListVal([]cty.Value{
cty.StringVal("c"),
}),
}),
``,
},
{ // Multiple result elements, all "full"
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
cty.StringVal("c"),
cty.StringVal("d"),
cty.StringVal("e"),
cty.StringVal("f"),
}),
cty.NumberIntVal(2),
cty.ListVal([]cty.Value{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b"),
}),
cty.ListVal([]cty.Value{
cty.StringVal("c"),
cty.StringVal("d"),
}),
cty.ListVal([]cty.Value{
cty.StringVal("e"),
cty.StringVal("f"),
}),
}),
``,
},
{ // We treat length zero as infinite length
cty.ListVal([]cty.Value{
cty.StringVal("a"),
}),
cty.Zero,
cty.ListVal([]cty.Value{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
}),
}),
``,
},
{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
}).Mark("a"),
cty.Zero,
cty.ListVal([]cty.Value{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
}),
}).Mark("a"),
``,
},
{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
}),
cty.Zero.Mark("a"),
cty.ListVal([]cty.Value{
cty.ListVal([]cty.Value{
cty.StringVal("a"),
}),
}).Mark("a"),
``,
},
{
cty.ListVal([]cty.Value{
cty.StringVal("a").Mark("b"),
}),
cty.Zero,
cty.ListVal([]cty.Value{
cty.ListVal([]cty.Value{
cty.StringVal("a").Mark("b"),
}),
}),
``,
},
{
cty.ListValEmpty(cty.String),
cty.NumberIntVal(-1),
cty.NilVal,
`the size argument must be positive`,
},
{
cty.ListValEmpty(cty.String),
cty.PositiveInfinity,
cty.NilVal,
`invalid size: value must be a whole number, between -9223372036854775808 and 9223372036854775807`,
},
{
cty.ListValEmpty(cty.String),
cty.NumberFloatVal(1.5),
cty.NilVal,
`invalid size: value must be a whole number, between -9223372036854775808 and 9223372036854775807`,
},
}

for _, test := range tests {
t.Run(fmt.Sprintf("Chunklist(%#v, %#v)", test.List, test.Len), func(t *testing.T) {
got, err := Chunklist(test.List, test.Len)
if test.Err != "" {
if err == nil {
t.Fatal("succeeded; want error")
}
if got, want := err.Error(), test.Err; got != want {
t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want)
}
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 TestContains(t *testing.T) {
listOfStrings := cty.ListVal([]cty.Value{
cty.StringVal("the"),
Expand Down