diff --git a/nullable_unmarshaljson.go b/nullable_unmarshaljson.go new file mode 100644 index 0000000..6155e1e --- /dev/null +++ b/nullable_unmarshaljson.go @@ -0,0 +1,38 @@ +package nul + +import ( + "fmt" + "encoding/json" +) + +var _ json.Unmarshaler = new(Nullable[bool]) +var _ json.Unmarshaler = new(Nullable[string]) + +// UnmarshalJSON makes it so json.Unmarshaler is implemented. +func (receiver *Nullable[T]) UnmarshalJSON(data []byte) error { + switch interface{}(receiver.value).(type) { + case bool, string: + // these are OK. + default: + return fmt.Errorf("cannot unmarshal into something of type %T from JSON because parameterized type is ‘%T’ rather than ‘bool’ or ‘string’", receiver, receiver.value) + } + + if 4 == len(data) && 'n' == data[0] && 'u' == data[1] && 'l' == data[2] && 'l' == data[3] { + *receiver = Null[T]() + return nil + + } + + { + var dst T + + err := json.Unmarshal(data, &dst) + if nil != err { + return err + } + + *receiver = Something(dst) + } + + return nil +} diff --git a/nullable_unmarshaljson_bool_test.go b/nullable_unmarshaljson_bool_test.go new file mode 100644 index 0000000..9026795 --- /dev/null +++ b/nullable_unmarshaljson_bool_test.go @@ -0,0 +1,70 @@ +package nul_test + +import ( + "testing" + + "encoding/json" + + "sourcecode.social/reiver/go-nul" +) + +func TestNullable_UnmarshalJSON_bool(t *testing.T) { + + tests := []struct{ + JSON string + Expected nul.Nullable[bool] + }{ + { + JSON: `null`, + Expected: nul.Null[bool](), + }, + { + JSON: `false`, + Expected: nul.Something(false), + }, + { + JSON: `true`, + Expected: nul.Something(true), + }, + } + + for testNumber, test := range tests { + + var actual nul.Nullable[bool] + + err := json.Unmarshal([]byte(test.JSON), &actual) + if nil != err { + t.Errorf("For test #%d, did not expect an error but actually got one.", testNumber) + t.Logf("ERROR: (%T) %s", err, err) + t.Logf("JSON: %q", test.JSON) + t.Logf("EXPECTED: %#v", test.Expected) + t.Logf("ACTUAL: %#v", actual) + continue + } + + { + expected := test.Expected + + if expected != actual { + t.Errorf("For test #%d, the actual value of the optional type is not what was expected.", testNumber) + t.Logf("EXPECTED: %#v", expected) + t.Logf("ACTUAL: %#v", actual) + t.Logf("JSON: %q", test.JSON) + continue + } + } + } +} + +func TestNullable_UnmarshalJSON_bool_fail(t *testing.T) { + + var op nul.Nullable[bool] + + var jason string = "12345" + + err := json.Unmarshal([]byte(jason), &op) + if nil == err { + t.Errorf("Expected an error but did not actually get one.") + return + } +} diff --git a/nullable_unmarshaljson_int_test.go b/nullable_unmarshaljson_int_test.go new file mode 100644 index 0000000..8e2075e --- /dev/null +++ b/nullable_unmarshaljson_int_test.go @@ -0,0 +1,119 @@ +package nul_test + +import ( + "testing" + + "encoding/json" + + "sourcecode.social/reiver/go-nul" +) + +func TestNullable_UnmarshalJSON_int(t *testing.T) { + + tests := []struct{ + JSON string + }{ + { + JSON: `null`, + }, + + + + { + JSON: `-65536`, + }, + { + JSON: `-65535`, + }, + + + + { + JSON: `-256`, + }, + { + JSON: `-255`, + }, + + + + { + JSON: `-5`, + }, + { + JSON: `-4`, + }, + { + JSON: `-3`, + }, + { + JSON: `-2`, + }, + { + JSON: `-1`, + }, + { + JSON: `0`, + }, + { + JSON: `1`, + }, + { + JSON: `2`, + }, + { + JSON: `3`, + }, + { + JSON: `4`, + }, + { + JSON: `5`, + }, + + + + { + JSON: `-255`, + }, + { + JSON: `-256`, + }, + + + + { + JSON: `65535`, + }, + { + JSON: `65536`, + }, + } + + for testNumber, test := range tests { + + var actual nul.Nullable[int] + + err := json.Unmarshal([]byte(test.JSON), &actual) + if nil == err { + t.Errorf("For test #%d, expected an error but did not actually get one.", testNumber) + t.Logf("ERROR: (%T) %s", err, err) + t.Logf("JSON: %q", test.JSON) + t.Logf("ACTUAL: %#v", actual) + continue + } + } +} + +func TestNullable_UnmarshalJSON_int_fail(t *testing.T) { + + var op nul.Nullable[int] + + var jason string = "12345" + + err := json.Unmarshal([]byte(jason), &op) + if nil == err { + t.Errorf("Expected an error but did not actually get one.") + return + } +} diff --git a/nullable_unmarshaljson_string_test.go b/nullable_unmarshaljson_string_test.go new file mode 100644 index 0000000..38aab38 --- /dev/null +++ b/nullable_unmarshaljson_string_test.go @@ -0,0 +1,132 @@ +package nul_test + +import ( + "testing" + + "encoding/json" + + "sourcecode.social/reiver/go-nul" +) + +func TestNullable_UnmarshalJSON_string(t *testing.T) { + + tests := []struct{ + JSON string + Expected nul.Nullable[string] + }{ + { + JSON: `null`, + Expected: nul.Null[string](), + }, + + + + { + JSON: `""`, + Expected: nul.Something(""), + }, + + + + { + JSON: `"apple"`, + Expected: nul.Something("apple"), + }, + { + JSON: `"banana"`, + Expected: nul.Something("banana"), + }, + { + JSON: `"cherry"`, + Expected: nul.Something("cherry"), + }, + + + + { + JSON: `"ONCE"`, + Expected: nul.Something("ONCE"), + }, + { + JSON: `"TWICE"`, + Expected: nul.Something("TWICE"), + }, + { + JSON: `"THRICE"`, + Expected: nul.Something("THRICE"), + }, + { + JSON: `"FOURCE"`, + Expected: nul.Something("FOURCE"), + }, + + + + { + JSON: `"🙂"`, + Expected: nul.Something("🙂"), + }, + { + JSON: `"😈"`, + Expected: nul.Something("😈"), + }, + { + JSON: `"❤️"`, + Expected: nul.Something("❤️"), + }, + + + + { + JSON: `"٠١٢٣۴۵۶٧٨٩"`, + Expected: nul.Something("٠١٢٣۴۵۶٧٨٩"), + }, + + + + { + JSON: `"𐏑𐏓𐏕"`, + Expected: nul.Something("𐏑𐏓𐏕"), + }, + } + + for testNumber, test := range tests { + + var actual nul.Nullable[string] + + err := json.Unmarshal([]byte(test.JSON), &actual) + if nil != err { + t.Errorf("For test #%d, did not expect an error but actually got one.", testNumber) + t.Logf("ERROR: (%T) %s", err, err) + t.Logf("JSON: %q", test.JSON) + t.Logf("EXPECTED: %#v", test.Expected) + t.Logf("ACTUAL: %#v", actual) + continue + } + + { + expected := test.Expected + + if expected != actual { + t.Errorf("For test #%d, the actual value of the nullable optional type is not what was expected.", testNumber) + t.Logf("EXPECTED: %#v", expected) + t.Logf("ACTUAL: %#v", actual) + t.Logf("JSON: %q", test.JSON) + continue + } + } + } +} + +func TestNullable_UnmarshalJSON_string_fail(t *testing.T) { + + var op nul.Nullable[string] + + var jason string = "12345" + + err := json.Unmarshal([]byte(jason), &op) + if nil == err { + t.Errorf("Expected an error but did not actually get one.") + return + } +}