diff --git a/.changelog/pending.txt b/.changelog/pending.txt new file mode 100644 index 000000000..4b6fc7280 --- /dev/null +++ b/.changelog/pending.txt @@ -0,0 +1,11 @@ +```release-note:note +types: The `Bool` type `Null`, `Unknown`, and `Value` fields have been deprecated in preference of the `NullBool()`, `UnknownBool()`, and `ValueBool()` creation functions and `BoolValue()`, `IsNull()`, and `IsUnknown()` methods. The fields will be removed in a future release. +``` + +```release-note:enhancement +types: Added `NullBool()`, `UnknownBool()`, `ValueBool` functions, which create immutable `Bool` values +``` + +```release-note:enhancement +types: Added `Bool` type `BoolValue()` method, which returns the `bool` of the known value or `false` if null or unknown +``` diff --git a/internal/reflect/primitive_test.go b/internal/reflect/primitive_test.go index 5301ea969..af7616022 100644 --- a/internal/reflect/primitive_test.go +++ b/internal/reflect/primitive_test.go @@ -160,9 +160,7 @@ func TestFromBool(t *testing.T) { val: true, typ: testtypes.BoolTypeWithValidateWarning{}, expected: testtypes.Bool{ - Bool: types.Bool{ - Value: true, - }, + Bool: types.ValueBool(true), CreatedBy: testtypes.BoolTypeWithValidateWarning{}, }, expectedDiags: diag.Diagnostics{ diff --git a/internal/testing/types/bool.go b/internal/testing/types/bool.go index 2ae8247d8..152c4c641 100644 --- a/internal/testing/types/bool.go +++ b/internal/testing/types/bool.go @@ -41,13 +41,13 @@ func (t BoolType) TerraformType(_ context.Context) tftypes.Type { func (t BoolType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { if in.IsNull() { return Bool{ - Bool: types.Bool{Null: true}, + Bool: types.NullBool(), CreatedBy: t, }, nil } if !in.IsKnown() { return Bool{ - Bool: types.Bool{Unknown: true}, + Bool: types.UnknownBool(), CreatedBy: t, }, nil } @@ -56,7 +56,7 @@ func (t BoolType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (att if err != nil { return nil, err } - return Bool{Bool: types.Bool{Value: b}, CreatedBy: t}, nil + return Bool{Bool: types.ValueBool(b), CreatedBy: t}, nil } // ValueType returns the Value type. @@ -91,13 +91,5 @@ func (b Bool) IsUnknown() bool { } func (b Bool) String() string { - if b.Bool.IsUnknown() { - return attr.UnknownValueString - } - - if b.Bool.IsNull() { - return attr.NullValueString - } - - return fmt.Sprintf("%t", b.Value) + return b.Bool.String() } diff --git a/types/bool.go b/types/bool.go index 3566c1c19..4354221a1 100644 --- a/types/bool.go +++ b/types/bool.go @@ -12,15 +12,51 @@ var ( _ attr.Value = Bool{} ) +// NullBool creates a Bool with a null value. Determine whether the value is +// null via the Bool type IsNull method. +// +// Setting the deprecated Bool type Null, Unknown, or Value fields after +// creating a Bool with this function has no effect. +func NullBool() Bool { + return Bool{ + state: valueStateNull, + } +} + +// UnknownBool creates a Bool with an unknown value. Determine whether the +// value is unknown via the Bool type IsUnknown method. +// +// Setting the deprecated Bool type Null, Unknown, or Value fields after +// creating a Bool with this function has no effect. +func UnknownBool() Bool { + return Bool{ + state: valueStateUnknown, + } +} + +// ValueBool creates a Bool with a known value. Access the value via the Bool +// type BoolValue method. +// +// Setting the deprecated Bool type Null, Unknown, or Value fields after +// creating a Bool with this function has no effect. +func ValueBool(value bool) Bool { + return Bool{ + state: valueStateKnown, + value: value, + } +} + func boolValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { if in.IsNull() { return Bool{ - Null: true, + Null: true, + state: valueStateDeprecated, }, nil } if !in.IsKnown() { return Bool{ Unknown: true, + state: valueStateDeprecated, }, nil } var b bool @@ -28,21 +64,61 @@ func boolValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, if err != nil { return nil, err } - return Bool{Value: b}, nil + return Bool{ + Value: b, + state: valueStateDeprecated, + }, nil } // Bool represents a boolean value. type Bool struct { // Unknown will be true if the value is not yet known. + // + // If the Bool was created with the BoolValue, NullBool, or UnknownBool + // functions, changing this field has no effect. + // + // Deprecated: Use the [UnknownBool] function to create an unknown Bool + // value or use the [IsUnknown] method to determine whether the Bool value + // is unknown instead. Unknown bool // Null will be true if the value was not set, or was explicitly set to // null. + // + // If the Bool was created with the BoolValue, NullBool, or UnknownBool + // functions, changing this field has no effect. + // + // Deprecated: Use the [NullBool] function to create a null Bool value or + // use the [IsNull] method to determine whether the Bool value is null + // instead. Null bool // Value contains the set value, as long as Unknown and Null are both // false. + // + // If the Bool was created with the BoolValue, NullBool, or UnknownBool + // functions, changing this field has no effect. + // + // Deprecated: Use the [BoolValue] function to create a known Bool value or + // use the [BoolValue] method to retrieve the Bool value instead. Value bool + + // state represents whether the value is null, unknown, or known. The + // zero-value for this field is null. + state valueState + + // value contains the known value, if not null or unknown. + value bool +} + +// BoolValue returns the known bool value. If Bool is null or unknown, returns +// false. +func (b Bool) BoolValue() bool { + if b.state == valueStateDeprecated { + return b.Value + } + + return b.value } // Type returns a BoolType. @@ -52,16 +128,31 @@ func (b Bool) Type(_ context.Context) attr.Type { // ToTerraformValue returns the data contained in the Bool as a tftypes.Value. func (b Bool) ToTerraformValue(_ context.Context) (tftypes.Value, error) { - if b.Null { + switch b.state { + case valueStateDeprecated: + if b.Null { + return tftypes.NewValue(tftypes.Bool, nil), nil + } + if b.Unknown { + return tftypes.NewValue(tftypes.Bool, tftypes.UnknownValue), nil + } + if err := tftypes.ValidateValue(tftypes.Bool, b.Value); err != nil { + return tftypes.NewValue(tftypes.Bool, tftypes.UnknownValue), err + } + return tftypes.NewValue(tftypes.Bool, b.Value), nil + case valueStateKnown: + if err := tftypes.ValidateValue(tftypes.Bool, b.value); err != nil { + return tftypes.NewValue(tftypes.Bool, tftypes.UnknownValue), err + } + + return tftypes.NewValue(tftypes.Bool, b.value), nil + case valueStateNull: return tftypes.NewValue(tftypes.Bool, nil), nil - } - if b.Unknown { + case valueStateUnknown: return tftypes.NewValue(tftypes.Bool, tftypes.UnknownValue), nil + default: + panic(fmt.Sprintf("unhandled Bool state in ToTerraformValue: %s", b.state)) } - if err := tftypes.ValidateValue(tftypes.Bool, b.Value); err != nil { - return tftypes.NewValue(tftypes.Bool, tftypes.UnknownValue), err - } - return tftypes.NewValue(tftypes.Bool, b.Value), nil } // Equal returns true if `other` is a *Bool and has the same value as `b`. @@ -70,6 +161,12 @@ func (b Bool) Equal(other attr.Value) bool { if !ok { return false } + if b.state != o.state { + return false + } + if b.state == valueStateKnown { + return b.value == o.value + } if b.Unknown != o.Unknown { return false } @@ -81,25 +178,37 @@ func (b Bool) Equal(other attr.Value) bool { // IsNull returns true if the Bool represents a null value. func (b Bool) IsNull() bool { - return b.Null + if b.state == valueStateNull { + return true + } + + return b.state == valueStateDeprecated && b.Null } // IsUnknown returns true if the Bool represents a currently unknown value. func (b Bool) IsUnknown() bool { - return b.Unknown + if b.state == valueStateUnknown { + return true + } + + return b.state == valueStateDeprecated && b.Unknown } // String returns a human-readable representation of the Bool value. // The string returned here is not protected by any compatibility guarantees, // and is intended for logging and error reporting. func (b Bool) String() string { - if b.Unknown { + if b.IsUnknown() { return attr.UnknownValueString } - if b.Null { + if b.IsNull() { return attr.NullValueString } + if b.state == valueStateKnown { + return fmt.Sprintf("%t", b.value) + } + return fmt.Sprintf("%t", b.Value) } diff --git a/types/bool_test.go b/types/bool_test.go index 2b0428944..f848fee69 100644 --- a/types/bool_test.go +++ b/types/bool_test.go @@ -9,6 +9,84 @@ import ( "github.com/hashicorp/terraform-plugin-go/tftypes" ) +// This test verifies the assumptions that creating the Value via function then +// setting the fields directly has no effects. +func TestBoolValueDeprecatedFieldSetting(t *testing.T) { + t.Parallel() + + knownBool := ValueBool(false) + + knownBool.Null = true + + if knownBool.IsNull() { + t.Error("unexpected null update after Null field setting") + } + + knownBool.Unknown = true + + if knownBool.IsUnknown() { + t.Error("unexpected unknown update after Unknown field setting") + } + + knownBool.Value = true + + if knownBool.BoolValue() { + t.Error("unexpected value update after Value field setting") + } +} + +// This test verifies the assumptions that creating the Value via function then +// setting the fields directly has no effects. +func TestNullBoolDeprecatedFieldSetting(t *testing.T) { + t.Parallel() + + nullBool := NullBool() + + nullBool.Null = false + + if !nullBool.IsNull() { + t.Error("unexpected null update after Null field setting") + } + + nullBool.Unknown = true + + if nullBool.IsUnknown() { + t.Error("unexpected unknown update after Unknown field setting") + } + + nullBool.Value = true + + if nullBool.BoolValue() { + t.Error("unexpected value update after Value field setting") + } +} + +// This test verifies the assumptions that creating the Value via function then +// setting the fields directly has no effects. +func TestUnknownBoolDeprecatedFieldSetting(t *testing.T) { + t.Parallel() + + unknownBool := UnknownBool() + + unknownBool.Null = true + + if unknownBool.IsNull() { + t.Error("unexpected null update after Null field setting") + } + + unknownBool.Unknown = false + + if !unknownBool.IsUnknown() { + t.Error("unexpected unknown update after Unknown field setting") + } + + unknownBool.Value = true + + if unknownBool.BoolValue() { + t.Error("unexpected value update after Value field setting") + } +} + func TestBoolValueFromTerraform(t *testing.T) { t.Parallel() @@ -84,6 +162,66 @@ func testBoolValueFromTerraform(t *testing.T, direct bool) { } } +func TestBoolBoolValue(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + input Bool + expected bool + }{ + "known-false": { + input: ValueBool(false), + expected: false, + }, + "known-true": { + input: ValueBool(true), + expected: true, + }, + "deprecated-known-false": { + input: Bool{Value: false}, + expected: false, + }, + "deprecated-known-true": { + input: Bool{Value: true}, + expected: true, + }, + "null": { + input: NullBool(), + expected: false, + }, + "deprecated-null": { + input: Bool{Null: true}, + expected: false, + }, + "unknown": { + input: UnknownBool(), + expected: false, + }, + "deprecated-unknown": { + input: Bool{Unknown: true}, + expected: false, + }, + "deprecated-invalid": { + input: Bool{Null: true, Unknown: true}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.input.BoolValue() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestBoolToTerraformValue(t *testing.T) { t.Parallel() @@ -92,19 +230,35 @@ func TestBoolToTerraformValue(t *testing.T) { expectation interface{} } tests := map[string]testCase{ - "true": { + "known-true": { + input: ValueBool(true), + expectation: tftypes.NewValue(tftypes.Bool, true), + }, + "known-false": { + input: ValueBool(false), + expectation: tftypes.NewValue(tftypes.Bool, false), + }, + "unknown": { + input: UnknownBool(), + expectation: tftypes.NewValue(tftypes.Bool, tftypes.UnknownValue), + }, + "null": { + input: NullBool(), + expectation: tftypes.NewValue(tftypes.Bool, nil), + }, + "deprecated-true": { input: Bool{Value: true}, expectation: tftypes.NewValue(tftypes.Bool, true), }, - "false": { + "deprecated-false": { input: Bool{Value: false}, expectation: tftypes.NewValue(tftypes.Bool, false), }, - "unknown": { + "deprecated-unknown": { input: Bool{Unknown: true}, expectation: tftypes.NewValue(tftypes.Bool, tftypes.UnknownValue), }, - "null": { + "deprecated-null": { input: Bool{Null: true}, expectation: tftypes.NewValue(tftypes.Bool, nil), }, @@ -136,122 +290,342 @@ func TestBoolEqual(t *testing.T) { expectation bool } tests := map[string]testCase{ - "true-true": { + "known-true-nil": { + input: ValueBool(true), + candidate: nil, + expectation: false, + }, + "known-true-wrongtype": { + input: ValueBool(true), + candidate: String{Value: "true"}, + expectation: false, + }, + "known-true-known-false": { + input: ValueBool(true), + candidate: ValueBool(false), + expectation: false, + }, + "known-true-known-true": { + input: ValueBool(true), + candidate: ValueBool(true), + expectation: true, + }, + "known-true-deprecated-false": { + input: ValueBool(true), + candidate: Bool{Value: false}, + expectation: false, + }, + "known-true-deprecated-true": { + input: ValueBool(true), + candidate: Bool{Value: true}, + expectation: false, // intentional + }, + "known-true-null": { + input: ValueBool(true), + candidate: NullBool(), + expectation: false, + }, + "known-true-deprecated-null": { + input: ValueBool(true), + candidate: Bool{Null: true}, + expectation: false, + }, + "known-true-unknown": { + input: ValueBool(true), + candidate: UnknownBool(), + expectation: false, + }, + "known-true-deprecated-unknown": { + input: ValueBool(true), + candidate: Bool{Unknown: true}, + expectation: false, + }, + "known-false-nil": { + input: ValueBool(false), + candidate: nil, + expectation: false, + }, + "known-false-wrongtype": { + input: ValueBool(false), + candidate: String{Value: "false"}, + expectation: false, + }, + "known-false-known-false": { + input: ValueBool(false), + candidate: ValueBool(false), + expectation: true, + }, + "known-false-known-true": { + input: ValueBool(false), + candidate: ValueBool(true), + expectation: false, + }, + "known-false-deprecated-false": { + input: ValueBool(false), + candidate: Bool{Value: false}, + expectation: false, // intentional + }, + "known-false-deprecated-true": { + input: ValueBool(false), + candidate: Bool{Value: true}, + expectation: false, + }, + "known-false-null": { + input: ValueBool(false), + candidate: NullBool(), + expectation: false, + }, + "known-false-deprecated-null": { + input: ValueBool(false), + candidate: Bool{Null: true}, + expectation: false, + }, + "known-false-unknown": { + input: ValueBool(false), + candidate: UnknownBool(), + expectation: false, + }, + "known-false-deprecated-unknown": { + input: ValueBool(false), + candidate: Bool{Unknown: true}, + expectation: false, + }, + "null-nil": { + input: NullBool(), + candidate: nil, + expectation: false, + }, + "null-wrongtype": { + input: NullBool(), + candidate: String{Value: "true"}, + expectation: false, + }, + "null-known-false": { + input: NullBool(), + candidate: ValueBool(false), + expectation: false, + }, + "null-known-true": { + input: NullBool(), + candidate: ValueBool(true), + expectation: false, + }, + "null-deprecated-true": { + input: NullBool(), + candidate: Bool{Value: true}, + expectation: false, + }, + "null-deprecated-false": { + input: NullBool(), + candidate: Bool{Value: false}, + expectation: false, + }, + "null-null": { + input: NullBool(), + candidate: NullBool(), + expectation: true, + }, + "null-deprecated-null": { + input: NullBool(), + candidate: Bool{Null: true}, + expectation: false, // intentional + }, + "null-unknown": { + input: NullBool(), + candidate: UnknownBool(), + expectation: false, + }, + "null-deprecated-unknown": { + input: NullBool(), + candidate: Bool{Unknown: true}, + expectation: false, + }, + "deprecated-true-known-false": { + input: Bool{Value: true}, + candidate: ValueBool(false), + expectation: false, + }, + "deprecated-true-known-true": { + input: Bool{Value: true}, + candidate: ValueBool(true), + expectation: false, // intentional + }, + "deprecated-true-deprecated-true": { input: Bool{Value: true}, candidate: Bool{Value: true}, expectation: true, }, - "true-false": { + "deprecated-true-deprecated-false": { input: Bool{Value: true}, candidate: Bool{Value: false}, expectation: false, }, - "true-unknown": { + "deprecated-true-unknown": { + input: Bool{Value: true}, + candidate: UnknownBool(), + expectation: false, + }, + "deprecated-true-deprecated-unknown": { input: Bool{Value: true}, candidate: Bool{Unknown: true}, expectation: false, }, - "true-null": { + "deprecated-true-null": { + input: Bool{Value: true}, + candidate: NullBool(), + expectation: false, + }, + "deprecated-true-deprecated-null": { input: Bool{Value: true}, candidate: Bool{Null: true}, expectation: false, }, - "true-wrongType": { + "deprecated-true-wrongType": { input: Bool{Value: true}, candidate: &String{Value: "oops"}, expectation: false, }, - "true-nil": { + "deprecated-true-nil": { input: Bool{Value: true}, candidate: nil, expectation: false, }, - "false-true": { + "deprecated-false-known-false": { + input: Bool{Value: false}, + candidate: ValueBool(false), + expectation: false, // intentional + }, + "deprecated-false-known-true": { + input: Bool{Value: false}, + candidate: ValueBool(true), + expectation: false, + }, + "deprecated-false-deprecated-true": { input: Bool{Value: false}, candidate: Bool{Value: true}, expectation: false, }, - "false-false": { + "deprecated-false-deprecated-false": { input: Bool{Value: false}, candidate: Bool{Value: false}, expectation: true, }, - "false-unknown": { + "deprecated-false-unknown": { + input: Bool{Value: false}, + candidate: UnknownBool(), + expectation: false, + }, + "deprecated-false-deprecated-unknown": { input: Bool{Value: false}, candidate: Bool{Unknown: true}, expectation: false, }, - "false-null": { + "deprecated-false-null": { + input: Bool{Value: false}, + candidate: NullBool(), + expectation: false, + }, + "deprecated-false-deprecated-null": { input: Bool{Value: false}, candidate: Bool{Null: true}, expectation: false, }, - "false-wrongType": { + "deprecated-false-wrongType": { input: Bool{Value: false}, candidate: &String{Value: "oops"}, expectation: false, }, - "false-nil": { + "deprecated-false-nil": { input: Bool{Value: false}, candidate: nil, expectation: false, }, - "unknown-true": { + "deprecated-unknown-known-false": { + input: Bool{Unknown: true}, + candidate: ValueBool(false), + expectation: false, + }, + "deprecated-unknown-known-true": { + input: Bool{Unknown: true}, + candidate: ValueBool(true), + expectation: false, + }, + "deprecated-unknown-deprecated-true": { input: Bool{Unknown: true}, candidate: Bool{Value: true}, expectation: false, }, - "unknown-false": { + "deprecated-unknown-deprecated-false": { input: Bool{Unknown: true}, candidate: Bool{Value: false}, expectation: false, }, - "unknown-unknown": { + "deprecated-unknown-deprecated-unknown": { input: Bool{Unknown: true}, candidate: Bool{Unknown: true}, expectation: true, }, - "unknown-null": { + "deprecated-unknown-deprecated-null": { input: Bool{Unknown: true}, candidate: Bool{Null: true}, expectation: false, }, - "unknown-wrongType": { + "deprecated-unknown-wrongType": { input: Bool{Unknown: true}, candidate: &String{Value: "oops"}, expectation: false, }, - "unknown-nil": { + "deprecated-unknown-nil": { input: Bool{Unknown: true}, candidate: nil, expectation: false, }, - "null-true": { + "deprecated-null-deprecated-true": { input: Bool{Null: true}, candidate: Bool{Value: true}, expectation: false, }, - "null-false": { + "deprecated-null-deprecated-false": { input: Bool{Null: true}, candidate: Bool{Value: false}, expectation: false, }, - "null-unknown": { + "deprecated-null-known-false": { + input: Bool{Null: true}, + candidate: ValueBool(false), + expectation: false, + }, + "deprecated-null-known-true": { + input: Bool{Null: true}, + candidate: ValueBool(true), + expectation: false, + }, + "deprecated-null-unknown": { + input: Bool{Null: true}, + candidate: UnknownBool(), + expectation: false, + }, + "deprecated-null-deprecated-unknown": { input: Bool{Null: true}, candidate: Bool{Unknown: true}, expectation: false, }, - "null-null": { + "deprecated-null-null": { + input: Bool{Null: true}, + candidate: NullBool(), + expectation: false, // intentional + }, + "deprecated-null-deprecated-null": { input: Bool{Null: true}, candidate: Bool{Null: true}, expectation: true, }, - "null-wrongType": { + "deprecated-null-wrongType": { input: Bool{Null: true}, candidate: &String{Value: "oops"}, expectation: false, }, - "null-nil": { + "deprecated-null-nil": { input: Bool{Null: true}, candidate: nil, expectation: false, @@ -270,6 +644,110 @@ func TestBoolEqual(t *testing.T) { } } +func TestBoolIsNull(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + input Bool + expected bool + }{ + "known": { + input: ValueBool(true), + expected: false, + }, + "deprecated-known": { + input: Bool{Value: true}, + expected: false, + }, + "null": { + input: NullBool(), + expected: true, + }, + "deprecated-null": { + input: Bool{Null: true}, + expected: true, + }, + "unknown": { + input: UnknownBool(), + expected: false, + }, + "deprecated-unknown": { + input: Bool{Unknown: true}, + expected: false, + }, + "deprecated-invalid": { + input: Bool{Null: true, Unknown: true}, + expected: true, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.input.IsNull() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestBoolIsUnknown(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + input Bool + expected bool + }{ + "known": { + input: ValueBool(true), + expected: false, + }, + "deprecated-known": { + input: Bool{Value: true}, + expected: false, + }, + "null": { + input: NullBool(), + expected: false, + }, + "deprecated-null": { + input: Bool{Null: true}, + expected: false, + }, + "unknown": { + input: UnknownBool(), + expected: true, + }, + "deprecated-unknown": { + input: Bool{Unknown: true}, + expected: true, + }, + "deprecated-invalid": { + input: Bool{Null: true, Unknown: true}, + expected: true, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.input.IsUnknown() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestBoolString(t *testing.T) { t.Parallel() @@ -278,19 +756,35 @@ func TestBoolString(t *testing.T) { expectation string } tests := map[string]testCase{ - "true": { + "known-true": { + input: ValueBool(true), + expectation: "true", + }, + "known-false": { + input: ValueBool(false), + expectation: "false", + }, + "null": { + input: NullBool(), + expectation: "", + }, + "unknown": { + input: UnknownBool(), + expectation: "", + }, + "deprecated-true": { input: Bool{Value: true}, expectation: "true", }, - "false": { + "deprecated-false": { input: Bool{Value: false}, expectation: "false", }, - "unknown": { + "deprecated-unknown": { input: Bool{Unknown: true}, expectation: "", }, - "null": { + "deprecated-null": { input: Bool{Null: true}, expectation: "", }, diff --git a/types/value_state.go b/types/value_state.go new file mode 100644 index 000000000..fb3a59951 --- /dev/null +++ b/types/value_state.go @@ -0,0 +1,42 @@ +package types + +import "fmt" + +const ( + // valueStateDeprecated represents a value where it can potentially be + // controlled by exported fields such as Null and Unknown. Since consumers + // can adjust those fields directly and it would not modify the internal + // valueState value, this sentinel value is a placeholder which can be + // checked in logic before assuming the valueState value is accurate. + // + // This value is 0 so it is the zero-value of existing implementations to + // preserve existing behaviors. A future version will switch the zero-value + // to null. + valueStateDeprecated valueState = 0 + + // valueStateNull represents a value which is null. + valueStateNull valueState = 1 + + // valueStateUnknown represents a value which is unknown. + valueStateUnknown valueState = 2 + + // valueStateKnown represents a value which is known (not null or unknown). + valueStateKnown valueState = 3 +) + +type valueState uint8 + +func (s valueState) String() string { + switch s { + case valueStateDeprecated: + return "deprecated" + case valueStateKnown: + return "known" + case valueStateNull: + return "null" + case valueStateUnknown: + return "unknown" + default: + panic(fmt.Sprintf("unhandled valueState in String: %d", s)) + } +}