diff --git a/cty/convert/conversion_object.go b/cty/convert/conversion_object.go index 098c109b..e356489a 100644 --- a/cty/convert/conversion_object.go +++ b/cty/convert/conversion_object.go @@ -80,13 +80,17 @@ func conversionObjectToObject(in, out cty.Type, unsafe bool) conversion { } } + if val.Equals(cty.NullVal(val.Type())).True() { + val = cty.NullVal(val.Type().WithoutOptionalAttributesDeep()) + } + attrVals[name] = val } for name := range outOptionals { if _, exists := attrVals[name]; !exists { wantTy := outAtys[name] - attrVals[name] = cty.NullVal(wantTy) + attrVals[name] = cty.NullVal(wantTy.WithoutOptionalAttributesDeep()) } } diff --git a/cty/convert/public_test.go b/cty/convert/public_test.go index d7ed9edb..1ed9e391 100644 --- a/cty/convert/public_test.go +++ b/cty/convert/public_test.go @@ -991,6 +991,104 @@ func TestConvert(t *testing.T) { }), WantError: false, }, + { + Value: cty.TupleVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "d": cty.NumberVal(big.NewFloat(10)), + "c": cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("foo"), + "b": cty.BoolVal(true), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "d": cty.NumberVal(big.NewFloat(5)), + "c": cty.NullVal(cty.ObjectWithOptionalAttrs(map[string]cty.Type{ + "a": cty.String, + "b": cty.Bool, + }, []string{"b"})), + }), + }), + Type: cty.Set(cty.ObjectWithOptionalAttrs(map[string]cty.Type{ + "c": cty.ObjectWithOptionalAttrs(map[string]cty.Type{ + "a": cty.String, + "b": cty.Bool, + }, []string{"b"}), + "d": cty.Number, + }, []string{"c"})), + Want: cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "d": cty.NumberVal(big.NewFloat(10)), + "c": cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("foo"), + "b": cty.BoolVal(true), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "d": cty.NumberVal(big.NewFloat(5)), + "c": cty.NullVal(cty.Object(map[string]cty.Type{ + "a": cty.String, + "b": cty.Bool, + })), + }), + }), + }, + { + Value: cty.TupleVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "d": cty.NumberVal(big.NewFloat(10)), + "c": cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("foo"), + "b": cty.BoolVal(true), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "d": cty.NumberVal(big.NewFloat(5)), + }), + }), + Type: cty.Set(cty.ObjectWithOptionalAttrs(map[string]cty.Type{ + "c": cty.ObjectWithOptionalAttrs(map[string]cty.Type{ + "a": cty.String, + "b": cty.Bool, + }, []string{"b"}), + "d": cty.Number, + }, []string{"c"})), + Want: cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "d": cty.NumberVal(big.NewFloat(10)), + "c": cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("foo"), + "b": cty.BoolVal(true), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "d": cty.NumberVal(big.NewFloat(5)), + "c": cty.NullVal(cty.Object(map[string]cty.Type{ + "a": cty.String, + "b": cty.Bool, + })), + }), + }), + }, + { + Value: cty.MapVal(map[string]cty.Value{ + "a": cty.StringVal("boop"), + }), + Type: cty.ObjectWithOptionalAttrs(map[string]cty.Type{ + "a": cty.String, + "b": cty.String, + "c": cty.Object(map[string]cty.Type{ + "d": cty.String, + }), + }, []string{"b", "c"}), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("boop"), + "b": cty.NullVal(cty.String), + "c": cty.NullVal(cty.Object(map[string]cty.Type{ + "d": cty.String, + })), + }), + WantError: false, + }, { Value: cty.ListVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{