diff --git a/.changes/unreleased/ENHANCEMENTS-20230314-144231.yaml b/.changes/unreleased/ENHANCEMENTS-20230314-144231.yaml new file mode 100644 index 000000000..22b81a51e --- /dev/null +++ b/.changes/unreleased/ENHANCEMENTS-20230314-144231.yaml @@ -0,0 +1,6 @@ +kind: ENHANCEMENTS +body: 'types/basetypes: Add `BoolValue` type `NewBoolPointerValue()` creation function + and `ValueBoolPointer()` method' +time: 2023-03-14T14:42:31.20202-04:00 +custom: + Issue: "689" diff --git a/.changes/unreleased/ENHANCEMENTS-20230314-144232.yaml b/.changes/unreleased/ENHANCEMENTS-20230314-144232.yaml new file mode 100644 index 000000000..486d85abf --- /dev/null +++ b/.changes/unreleased/ENHANCEMENTS-20230314-144232.yaml @@ -0,0 +1,6 @@ +kind: ENHANCEMENTS +body: 'types/basetypes: Add `Float64Value` type `NewFloat64PointerValue()` creation function + and `ValueFloat64Pointer()` method' +time: 2023-03-14T14:42:32.20202-04:00 +custom: + Issue: "689" diff --git a/.changes/unreleased/ENHANCEMENTS-20230314-144233.yaml b/.changes/unreleased/ENHANCEMENTS-20230314-144233.yaml new file mode 100644 index 000000000..9e0949ed1 --- /dev/null +++ b/.changes/unreleased/ENHANCEMENTS-20230314-144233.yaml @@ -0,0 +1,6 @@ +kind: ENHANCEMENTS +body: 'types/basetypes: Add `Int64Value` type `NewInt64PointerValue()` creation function + and `ValueInt64Pointer()` method' +time: 2023-03-14T14:42:33.20202-04:00 +custom: + Issue: "689" diff --git a/.changes/unreleased/ENHANCEMENTS-20230314-144234.yaml b/.changes/unreleased/ENHANCEMENTS-20230314-144234.yaml new file mode 100644 index 000000000..51c8cd0ac --- /dev/null +++ b/.changes/unreleased/ENHANCEMENTS-20230314-144234.yaml @@ -0,0 +1,6 @@ +kind: ENHANCEMENTS +body: 'types/basetypes: Add `StringValue` type `NewStringPointerValue()` creation function + and `ValueStringPointer()` method' +time: 2023-03-14T14:42:34.20202-04:00 +custom: + Issue: "689" diff --git a/types/basetypes/bool.go b/types/basetypes/bool.go index d0e3a2910..a368cca52 100644 --- a/types/basetypes/bool.go +++ b/types/basetypes/bool.go @@ -48,6 +48,16 @@ func NewBoolValue(value bool) BoolValue { } } +// NewBoolPointerValue creates a Bool with a null value if nil or a known +// value. Access the value via the Bool type ValueBoolPointer method. +func NewBoolPointerValue(value *bool) BoolValue { + if value == nil { + return NewBoolNull() + } + + return NewBoolValue(*value) +} + // BoolValue represents a boolean value. type BoolValue struct { // state represents whether the value is null, unknown, or known. The @@ -131,6 +141,16 @@ func (b BoolValue) ValueBool() bool { return b.value } +// ValueBoolPointer returns a pointer to the known bool value, nil for a null +// value, or a pointer to false for an unknown value. +func (b BoolValue) ValueBoolPointer() *bool { + if b.IsNull() { + return nil + } + + return &b.value +} + // ToBoolValue returns Bool. func (b BoolValue) ToBoolValue(context.Context) (BoolValue, diag.Diagnostics) { return b, nil diff --git a/types/basetypes/bool_test.go b/types/basetypes/bool_test.go index 6c1accf91..b10cd5c06 100644 --- a/types/basetypes/bool_test.go +++ b/types/basetypes/bool_test.go @@ -382,3 +382,75 @@ func TestBoolValueValueBool(t *testing.T) { }) } } + +func TestBoolValueValueBoolPointer(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + input BoolValue + expected *bool + }{ + "known-false": { + input: NewBoolValue(false), + expected: pointer(false), + }, + "known-true": { + input: NewBoolValue(true), + expected: pointer(true), + }, + "null": { + input: NewBoolNull(), + expected: nil, + }, + "unknown": { + input: NewBoolUnknown(), + expected: pointer(false), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.input.ValueBoolPointer() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestNewBoolPointerValue(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + value *bool + expected BoolValue + }{ + "nil": { + value: nil, + expected: NewBoolNull(), + }, + "value": { + value: pointer(true), + expected: NewBoolValue(true), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := NewBoolPointerValue(testCase.value) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/types/basetypes/float64.go b/types/basetypes/float64.go index d59b9cf99..34b58c4b7 100644 --- a/types/basetypes/float64.go +++ b/types/basetypes/float64.go @@ -54,6 +54,16 @@ func NewFloat64Value(value float64) Float64Value { } } +// NewFloat64PointerValue creates a Float64 with a null value if nil or a known +// value. Access the value via the Float64 type ValueFloat64Pointer method. +func NewFloat64PointerValue(value *float64) Float64Value { + if value == nil { + return NewFloat64Null() + } + + return NewFloat64Value(*value) +} + // Float64Value represents a 64-bit floating point value, exposed as a float64. type Float64Value struct { // state represents whether the value is null, unknown, or known. The @@ -137,6 +147,16 @@ func (f Float64Value) ValueFloat64() float64 { return f.value } +// ValueFloat64Pointer returns a pointer to the known float64 value, nil for a +// null value, or a pointer to 0.0 for an unknown value. +func (f Float64Value) ValueFloat64Pointer() *float64 { + if f.IsNull() { + return nil + } + + return &f.value +} + // ToFloat64Value returns Float64. func (f Float64Value) ToFloat64Value(context.Context) (Float64Value, diag.Diagnostics) { return f, nil diff --git a/types/basetypes/float64_test.go b/types/basetypes/float64_test.go index 07aa023bf..baebe36a6 100644 --- a/types/basetypes/float64_test.go +++ b/types/basetypes/float64_test.go @@ -516,3 +516,71 @@ func TestFloat64ValueValueFloat64(t *testing.T) { }) } } + +func TestFloat64ValueValueFloat64Pointer(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + input Float64Value + expected *float64 + }{ + "known": { + input: NewFloat64Value(2.4), + expected: pointer(2.4), + }, + "null": { + input: NewFloat64Null(), + expected: nil, + }, + "unknown": { + input: NewFloat64Unknown(), + expected: pointer(0.0), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.input.ValueFloat64Pointer() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestNewFloat64PointerValue(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + value *float64 + expected Float64Value + }{ + "nil": { + value: nil, + expected: NewFloat64Null(), + }, + "value": { + value: pointer(1.2), + expected: NewFloat64Value(1.2), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := NewFloat64PointerValue(testCase.value) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/types/basetypes/int64.go b/types/basetypes/int64.go index ef451e4c6..e537aae15 100644 --- a/types/basetypes/int64.go +++ b/types/basetypes/int64.go @@ -48,6 +48,16 @@ func NewInt64Value(value int64) Int64Value { } } +// NewInt64PointerValue creates a Int64 with a null value if nil or a known +// value. Access the value via the Int64 type ValueInt64Pointer method. +func NewInt64PointerValue(value *int64) Int64Value { + if value == nil { + return NewInt64Null() + } + + return NewInt64Value(*value) +} + // Int64Value represents a 64-bit integer value, exposed as an int64. type Int64Value struct { // state represents whether the value is null, unknown, or known. The @@ -131,6 +141,16 @@ func (i Int64Value) ValueInt64() int64 { return i.value } +// ValueInt64Pointer returns a pointer to the known int64 value, nil for a +// null value, or a pointer to 0 for an unknown value. +func (i Int64Value) ValueInt64Pointer() *int64 { + if i.IsNull() { + return nil + } + + return &i.value +} + // ToInt64Value returns Int64. func (i Int64Value) ToInt64Value(context.Context) (Int64Value, diag.Diagnostics) { return i, nil diff --git a/types/basetypes/int64_test.go b/types/basetypes/int64_test.go index 86c7dfe69..921262fc5 100644 --- a/types/basetypes/int64_test.go +++ b/types/basetypes/int64_test.go @@ -344,3 +344,71 @@ func TestInt64ValueValueInt64(t *testing.T) { }) } } + +func TestInt64ValueValueInt64Pointer(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + input Int64Value + expected *int64 + }{ + "known": { + input: NewInt64Value(24), + expected: pointer(int64(24)), + }, + "null": { + input: NewInt64Null(), + expected: nil, + }, + "unknown": { + input: NewInt64Unknown(), + expected: pointer(int64(0)), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.input.ValueInt64Pointer() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestNewInt64PointerValue(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + value *int64 + expected Int64Value + }{ + "nil": { + value: nil, + expected: NewInt64Null(), + }, + "value": { + value: pointer(int64(123)), + expected: NewInt64Value(123), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := NewInt64PointerValue(testCase.value) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/types/basetypes/string.go b/types/basetypes/string.go index a9c7b8cb5..d645f237c 100644 --- a/types/basetypes/string.go +++ b/types/basetypes/string.go @@ -57,6 +57,16 @@ func NewStringValue(value string) StringValue { } } +// NewStringPointerValue creates a String with a null value if nil or a known +// value. Access the value via the String type ValueStringPointer method. +func NewStringPointerValue(value *string) StringValue { + if value == nil { + return NewStringNull() + } + + return NewStringValue(*value) +} + // StringValue represents a UTF-8 string value. type StringValue struct { // state represents whether the value is null, unknown, or known. The @@ -142,6 +152,16 @@ func (s StringValue) ValueString() string { return s.value } +// ValueStringPointer returns a pointer to the known string value, nil for a +// null value, or a pointer to "" for an unknown value. +func (s StringValue) ValueStringPointer() *string { + if s.IsNull() { + return nil + } + + return &s.value +} + // ToStringValue returns String. func (s StringValue) ToStringValue(context.Context) (StringValue, diag.Diagnostics) { return s, nil diff --git a/types/basetypes/string_test.go b/types/basetypes/string_test.go index 896cf022c..57700d7eb 100644 --- a/types/basetypes/string_test.go +++ b/types/basetypes/string_test.go @@ -338,3 +338,71 @@ func TestStringValueValueString(t *testing.T) { }) } } + +func TestStringValueValueStringPointer(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + input StringValue + expected *string + }{ + "known": { + input: NewStringValue("test"), + expected: pointer("test"), + }, + "null": { + input: NewStringNull(), + expected: nil, + }, + "unknown": { + input: NewStringUnknown(), + expected: pointer(""), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.input.ValueStringPointer() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestNewStringPointerValue(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + value *string + expected StringValue + }{ + "nil": { + value: nil, + expected: NewStringNull(), + }, + "value": { + value: pointer("test"), + expected: NewStringValue("test"), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := NewStringPointerValue(testCase.value) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/types/bool_value.go b/types/bool_value.go index fd6591a66..35108c3c0 100644 --- a/types/bool_value.go +++ b/types/bool_value.go @@ -21,3 +21,8 @@ func BoolUnknown() basetypes.BoolValue { func BoolValue(value bool) basetypes.BoolValue { return basetypes.NewBoolValue(value) } + +// BoolPointerValue creates a Bool with a null value if nil or a known value. +func BoolPointerValue(value *bool) basetypes.BoolValue { + return basetypes.NewBoolPointerValue(value) +} diff --git a/types/float64_value.go b/types/float64_value.go index cd6bb6c67..c222dd83b 100644 --- a/types/float64_value.go +++ b/types/float64_value.go @@ -21,3 +21,8 @@ func Float64Unknown() basetypes.Float64Value { func Float64Value(value float64) basetypes.Float64Value { return basetypes.NewFloat64Value(value) } + +// Float64PointerValue creates a Float64 with a null value if nil or a known value. +func Float64PointerValue(value *float64) basetypes.Float64Value { + return basetypes.NewFloat64PointerValue(value) +} diff --git a/types/int64_value.go b/types/int64_value.go index fdb146f52..12b1e8c3e 100644 --- a/types/int64_value.go +++ b/types/int64_value.go @@ -21,3 +21,8 @@ func Int64Unknown() basetypes.Int64Value { func Int64Value(value int64) basetypes.Int64Value { return basetypes.NewInt64Value(value) } + +// Int64PointerValue creates a Int64 with a null value if nil or a known value. +func Int64PointerValue(value *int64) basetypes.Int64Value { + return basetypes.NewInt64PointerValue(value) +} diff --git a/types/string_value.go b/types/string_value.go index b7934199e..e31993ead 100644 --- a/types/string_value.go +++ b/types/string_value.go @@ -21,3 +21,8 @@ func StringUnknown() basetypes.StringValue { func StringValue(value string) basetypes.StringValue { return basetypes.NewStringValue(value) } + +// StringPointerValue creates a String with a null value if nil or a known value. +func StringPointerValue(value *string) basetypes.StringValue { + return basetypes.NewStringPointerValue(value) +}