Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
93aadf6
Prepare for a potential future 1.6.1 release
apparentlymart Aug 31, 2020
51d8b5a
build: remove go 1.11 and 1.12 from travis config
mildwonkey Sep 2, 2020
2603478
cty: RawEquals correct behavior for sets with partially-unknown values
mildwonkey Sep 2, 2020
c4ddf7d
v1.6.1
apparentlymart Sep 2, 2020
667b56a
Prepare for an assumed future v1.6.2 release
apparentlymart Sep 2, 2020
c7d1c95
cty: Utilities to unmark with marks at specific paths
pselle Sep 8, 2020
9a0281a
Update CHANGELOG.md
apparentlymart Sep 8, 2020
8577dd3
cty: Fix deep marks functions with new transformer
alisdair Sep 23, 2020
603ae02
cty: Remove already-fixed FIXME for SetVal
alisdair Sep 23, 2020
604042b
functions/stdlib: formatdate to correctly handle literals at the end …
OwenTuz Oct 5, 2020
860524c
Update CHANGELOG.md
apparentlymart Oct 5, 2020
f45ff62
cty: Fix path array reuse with UnmarkDeepWithPaths
alisdair Oct 6, 2020
f4ac520
stdlib: Add tests for join function
alisdair Oct 7, 2020
2709c23
function: Deeply unmark function arguments
alisdair Oct 7, 2020
ddae4f3
Update CHANGELOG.md
apparentlymart Oct 13, 2020
d454379
v1.7.0
apparentlymart Oct 21, 2020
5c915fc
Update CHANGELOG.md
bbasata Mar 25, 2025
1de8d43
CHANGELOG: Prepare for a potential future v1.7.1 release
apparentlymart Oct 21, 2020
862cbcc
cty: NilType.Equals(...) without a panic
radeksimko Nov 10, 2020
389a62d
cty: Maintain precision correctly in Multiply and Modulo
jbardin Nov 12, 2020
b17d03c
Update CHANGELOG.md
apparentlymart Nov 12, 2020
5612d12
convert: Fix MismatchMessage for object attributes
alisdair Nov 23, 2020
d1d65c6
Update CHANGELOG.md
apparentlymart Nov 23, 2020
a5a1836
functions/stdlib: merge to return empty object if all args are null
mildwonkey Dec 16, 2020
f760b30
Update CHANGELOG.md
apparentlymart Dec 16, 2020
592d293
function/stdlib: coalescelist should not panic when an argument is null
alisdair Dec 16, 2020
a146583
Update CHANGELOG.md
apparentlymart Dec 16, 2020
5aa6f96
v1.7.1 release
apparentlymart Dec 16, 2020
f784305
Prepare for a possible forthcoming v1.7.2 release
apparentlymart Dec 16, 2020
ec7b79a
function/stdlib: Fix an incorrect test for the
mildwonkey Dec 16, 2020
d074e52
cty: fix GoString for object types with optional attributes
mildwonkey Feb 22, 2021
85c8e76
Update CHANGELOG.md
apparentlymart Feb 22, 2021
3eff97e
build: Run Travis-CI tests on Go 1.15 and Go 1.16
apparentlymart Feb 23, 2021
06aa1a6
v1.7.2
apparentlymart Feb 23, 2021
6cf90b0
Unicode 13 support
apparentlymart Feb 23, 2021
ba1aae7
Update CHANGELOG.md
apparentlymart Feb 23, 2021
8ff9f0b
v1.8.0
apparentlymart Feb 23, 2021
8d37496
Update github.com/apparentlymart/go-textseg
bbasata Mar 25, 2025
a8db686
Merge branch 'master' into forward-to-1.8.0
bbasata Mar 28, 2025
57fe0eb
Merge branch 'master' into forward-to-1.8.0
bbasata Mar 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
language: go

go:
- 1.13.x
- 1.15.x
- 1.16.x
- tip

before_install:
Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
# 1.8.0 (Unreleased)

* `cty`: When running on Go 1.16 or later, the `cty.String` type will now normalize incoming string values using the Unicode 13 normalization rules.
* `function/stdlib`: The various string functions which split strings into individual characters as part of their work will now use the Unicode 13 version of the text segmentation algorithm to do so.

# 1.7.2 (Unreleased)

* `cty`: The `Type.GoString` implementation for object types with optional attributes was previously producing incorrect results due to an implementation bug. ([#86](https://github.com/zclconf/go-cty/pull/86))

# 1.7.1 (Unreleased)

* `cty`: The `Value.Multiply` and `Value.Modulo` functions now correctly propagate the floating point precision of the arguments, which avoids generating incorrect results for large integer operands. ([#75](https://github.com/zclconf/go-cty/pull/75))
* `convert`: The `convert.MismatchMessage` function will now correctly identify mismatching attributes in objects, rather than misreporting attributes that are actually present and correct. ([#78](https://github.com/zclconf/go-cty/pull/78))
* `function/stdlib`: The `merge` function now returns an empty object if all of its arguments are `null`, rather than returning `null` as before. That's more consistent with its usual behavior of ignoring `null` arguments when there is at least one non-null argument. ([#82](https://github.com/zclconf/go-cty/pull/82))
* `function/stdlib`: The `coalescelist` function now ignores any arguments that are null, rather than panicking as before.. ([#81](https://github.com/zclconf/go-cty/pull/81))

# 1.7.0 (Unreleased)

* `cty`: `Value.UnmarkDeepWithPaths` and `Value.MarkWithPaths` are like `Value.UnmarkDeep` and `Value.Mark` but they retain path information for each marked value, so that marks can be re-applied later without all the loss of detail that results from `Value.UnmarkDeep` aggregating together all of the nested marks.
Expand Down
4 changes: 4 additions & 0 deletions cty/convert/mismatch_msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ func mismatchMessageObjects(got, want cty.Type) string {
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
Expand Down
18 changes: 18 additions & 0 deletions cty/convert/mismatch_msg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,24 @@ func TestMismatchMessage(t *testing.T) {
cty.List(cty.DynamicPseudoType),
`all list elements must have the same type`,
},
{
cty.Object(map[string]cty.Type{
"foo": cty.Bool,
"bar": cty.String,
"baz": cty.Object(map[string]cty.Type{
"boop": cty.Number,
}),
}),
cty.Object(map[string]cty.Type{
"foo": cty.Bool,
"bar": cty.String,
"baz": cty.Object(map[string]cty.Type{
"boop": cty.Number,
"beep": cty.Bool,
}),
}),
`attribute "baz": attribute "beep" is required`,
},
}

for _, test := range tests {
Expand Down
12 changes: 4 additions & 8 deletions cty/function/stdlib/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,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
}
Expand Down Expand Up @@ -758,25 +762,17 @@ 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
}
}

switch {
case allNull:
return cty.NullVal(retType), nil
case retType.IsMapType():
if len(outputMap) == 0 {
return cty.MapValEmpty(retType.ElementType()), nil
Expand Down
144 changes: 142 additions & 2 deletions cty/function/stdlib/collection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -291,14 +291,21 @@ func TestMerge(t *testing.T) {
}),
false,
},
{ // handle null map
{ // all inputs are null
[]cty.Value{
cty.NullVal(cty.Map(cty.String)),
cty.NullVal(cty.Object(map[string]cty.Type{
"a": cty.List(cty.String),
})),
},
cty.NullVal(cty.EmptyObject),
cty.EmptyObjectVal,
false,
},
{ // single null input
[]cty.Value{
cty.MapValEmpty(cty.String),
},
cty.MapValEmpty(cty.String),
false,
},
{ // handle null object
Expand Down Expand Up @@ -843,3 +850,136 @@ func TestElement(t *testing.T) {
})
}
}

func TestCoalesceList(t *testing.T) {
tests := map[string]struct {
Values []cty.Value
Want cty.Value
Err bool
}{
"returns first list if non-empty": {
[]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("a"),
cty.StringVal("b"),
}),
false,
},
"returns second list if first is empty": {
[]cty.Value{
cty.ListValEmpty(cty.String),
cty.ListVal([]cty.Value{
cty.StringVal("c"),
cty.StringVal("d"),
}),
},
cty.ListVal([]cty.Value{
cty.StringVal("c"),
cty.StringVal("d"),
}),
false,
},
"return type is dynamic, not unified": {
[]cty.Value{
cty.ListValEmpty(cty.String),
cty.ListVal([]cty.Value{
cty.NumberIntVal(3),
cty.NumberIntVal(4),
}),
},
cty.ListVal([]cty.Value{
cty.NumberIntVal(3),
cty.NumberIntVal(4),
}),
false,
},
"works with tuples": {
[]cty.Value{
cty.EmptyTupleVal,
cty.TupleVal([]cty.Value{
cty.StringVal("c"),
cty.StringVal("d"),
}),
},
cty.TupleVal([]cty.Value{
cty.StringVal("c"),
cty.StringVal("d"),
}),
false,
},
"unknown arguments": {
[]cty.Value{
cty.UnknownVal(cty.List(cty.String)),
cty.ListVal([]cty.Value{
cty.StringVal("c"),
cty.StringVal("d"),
}),
},
cty.DynamicVal,
false,
},
"null arguments": {
[]cty.Value{
cty.NullVal(cty.List(cty.String)),
cty.ListVal([]cty.Value{
cty.StringVal("c"),
cty.StringVal("d"),
}),
},
cty.ListVal([]cty.Value{
cty.StringVal("c"),
cty.StringVal("d"),
}),
false,
},
"all null arguments": {
[]cty.Value{
cty.NullVal(cty.List(cty.String)),
cty.NullVal(cty.List(cty.String)),
},
cty.NilVal,
true,
},
"invalid arguments": {
[]cty.Value{
cty.MapVal(map[string]cty.Value{"a": cty.True}),
cty.ObjectVal(map[string]cty.Value{"b": cty.False}),
},
cty.NilVal,
true,
},
"no arguments": {
[]cty.Value{},
cty.NilVal,
true,
},
}

for name, test := range tests {
t.Run(name, func(t *testing.T) {
got, err := CoalesceList(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)
}
})
}
}
2 changes: 1 addition & 1 deletion cty/function/stdlib/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"math/big"
"strings"

"github.com/apparentlymart/go-textseg/v12/textseg"
"github.com/apparentlymart/go-textseg/v13/textseg"

"github.com/hashicorp/go-cty/cty"
"github.com/hashicorp/go-cty/cty/convert"
Expand Down
2 changes: 1 addition & 1 deletion cty/function/stdlib/string.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"sort"
"strings"

"github.com/apparentlymart/go-textseg/v12/textseg"
"github.com/apparentlymart/go-textseg/v13/textseg"

"github.com/hashicorp/go-cty/cty"
"github.com/hashicorp/go-cty/cty/function"
Expand Down
2 changes: 1 addition & 1 deletion cty/object_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func (t typeObject) GoString() string {
return "cty.EmptyObject"
}
if len(t.AttrOptional) > 0 {
opt := make([]string, len(t.AttrOptional))
var opt []string
for k := range t.AttrOptional {
opt = append(opt, k)
}
Expand Down
3 changes: 3 additions & 0 deletions cty/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
95 changes: 95 additions & 0 deletions cty/type_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,98 @@ func TestHasDynamicTypes(t *testing.T) {
})
}
}

func TestNilTypeEquals(t *testing.T) {
var typ Type
if !typ.Equals(NilType) {
t.Fatal("expected NilTypes to equal")
}
}

func TestTypeGoString(t *testing.T) {
tests := []struct {
Type Type
Want string
}{
{
DynamicPseudoType,
`cty.DynamicPseudoType`,
},
{
String,
`cty.String`,
},
{
Tuple([]Type{String, Bool}),
`cty.Tuple([]cty.Type{cty.String, cty.Bool})`,
},

{
Number,
`cty.Number`,
},
{
Bool,
`cty.Bool`,
},
{
List(String),
`cty.List(cty.String)`,
},
{
List(List(String)),
`cty.List(cty.List(cty.String))`,
},
{
List(Bool),
`cty.List(cty.Bool)`,
},
{
Set(String),
`cty.Set(cty.String)`,
},
{
Set(Map(String)),
`cty.Set(cty.Map(cty.String))`,
},
{
Set(Bool),
`cty.Set(cty.Bool)`,
},
{
Tuple([]Type{Bool}),
`cty.Tuple([]cty.Type{cty.Bool})`,
},

{
Map(String),
`cty.Map(cty.String)`,
},
{
Map(Set(String)),
`cty.Map(cty.Set(cty.String))`,
},
{
Map(Bool),
`cty.Map(cty.Bool)`,
},
{
Object(map[string]Type{"foo": Bool}),
`cty.Object(map[string]cty.Type{"foo":cty.Bool})`,
},
{
ObjectWithOptionalAttrs(map[string]Type{"foo": Bool, "bar": String}, []string{"bar"}),
`cty.ObjectWithOptionalAttrs(map[string]cty.Type{"bar":cty.String, "foo":cty.Bool}, []string{"bar"})`,
},
}

for _, test := range tests {
t.Run(test.Type.GoString(), func(t *testing.T) {
got := test.Type.GoString()
want := test.Want
if got != want {
t.Errorf("wrong result\ngot: %s\nwant: %s", got, want)
}
})
}
}
Loading