Skip to content

Commit

Permalink
Give it a try with null values
Browse files Browse the repository at this point in the history
  • Loading branch information
MetalBlueberry committed May 25, 2024
1 parent 5664c3b commit cda0973
Show file tree
Hide file tree
Showing 2 changed files with 159 additions and 84 deletions.
23 changes: 19 additions & 4 deletions types/arrayok.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,28 @@ package types

import (
"encoding/json"
"reflect"
)

func ArrayOKValue[T any](value T) ArrayOK[*T] {
v := &value
return ArrayOK[*T]{Value: &v}
}

func ArrayOKArray[T any](array ...T) ArrayOK[*T] {
out := make([]*T, len(array))
for i, v := range array {
value := v
out[i] = &value
}
return ArrayOK[*T]{
Array: out,
}
}

// ArrayOK is a type that allows you to define a single value or an array of values, But not both.
// If Array is defined, Value will be ignored.
type ArrayOK[T any] struct {
Value T
Value *T
Array []T
}

Expand All @@ -21,7 +36,7 @@ func (arrayOK *ArrayOK[T]) MarshalJSON() ([]byte, error) {

func (arrayOK *ArrayOK[T]) UnmarshalJSON(data []byte) error {
arrayOK.Array = nil
arrayOK.Value = reflect.Zero(reflect.TypeOf(arrayOK.Value)).Interface().(T)
arrayOK.Value = nil

var array []T
err := json.Unmarshal(data, &array)
Expand All @@ -35,6 +50,6 @@ func (arrayOK *ArrayOK[T]) UnmarshalJSON(data []byte) error {
if err != nil {
return err
}
arrayOK.Value = value
arrayOK.Value = &value
return nil
}
220 changes: 140 additions & 80 deletions types/arrayok_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,36 @@ type TestMarshallScenario[T any] struct {
func TestFloat64Marshal(t *testing.T) {
RegisterTestingT(t)

scenarios := []TestMarshallScenario[float64]{
scenarios := []TestMarshallScenario[*float64]{
{
Name: "A single number",
Input: types.ArrayOK[float64]{
Value: 12.3,
},
Name: "A single number",
Input: types.ArrayOKValue(12.3),
Expected: "12.3",
},
{
Name: "A single number in a list",
Input: types.ArrayOK[float64]{
Array: []float64{12.3},
},
Name: "A single number in a list",
Input: types.ArrayOKArray(12.3),
Expected: "[12.3]",
},
{
Name: "Multiple values",
Input: types.ArrayOK[float64]{
Array: []float64{1, 2, 3, 4.5},
},
Name: "Multiple values",
Input: types.ArrayOKArray(1, 2, 3, 4.5),
Expected: "[1,2,3,4.5]",
},
{
Name: "Nil Value",
Input: types.ArrayOK[*float64]{},
Expected: "null",
},
{
Name: "Nil Array",
Input: func() types.ArrayOK[*float64] {
original := types.ArrayOKArray(1.2, 0, 3.4)
original.Array[1] = nil
return original
}(),
Expected: "[1.2,null,3.4]",
},
}

for _, tt := range scenarios {
Expand All @@ -53,28 +61,36 @@ func TestFloat64Marshal(t *testing.T) {
func TestStringMarshal(t *testing.T) {
RegisterTestingT(t)

scenarios := []TestMarshallScenario[string]{
scenarios := []TestMarshallScenario[*string]{
{
Name: "A single string",
Input: types.ArrayOK[string]{
Value: "hello",
},
Name: "A single string",
Input: types.ArrayOKValue("hello"),
Expected: `"hello"`,
},
{
Name: "A single string in a list",
Input: types.ArrayOK[string]{
Array: []string{"hello"},
},
Name: "A single string in a list",
Input: types.ArrayOKArray("hello"),
Expected: `["hello"]`,
},
{
Name: "Multiple strings",
Input: types.ArrayOK[string]{
Array: []string{"hello", "world", "foo", "bar"},
},
Name: "Multiple strings",
Input: types.ArrayOKArray("hello", "world", "foo", "bar"),
Expected: `["hello","world","foo","bar"]`,
},
{
Name: "Nil Value",
Input: types.ArrayOK[*string]{},
Expected: "null",
},
{
Name: "Nil Array",
Input: func() types.ArrayOK[*string] {
original := types.ArrayOKArray("hello", "", "world")
original.Array[1] = nil
return original
}(),
Expected: `["hello",null,"world"]`,
},
}

for _, tt := range scenarios {
Expand All @@ -95,33 +111,41 @@ type TestUnmarshalScenario[T any] struct {
func TestFloat64Unmarshal(t *testing.T) {
RegisterTestingT(t)

scenarios := []TestUnmarshalScenario[float64]{
scenarios := []TestUnmarshalScenario[*float64]{
{
Name: "A single number",
Input: "12.3",
Expected: types.ArrayOK[float64]{
Value: 12.3,
},
Name: "A single number",
Input: "12.3",
Expected: types.ArrayOKValue(12.3),
},
{
Name: "A single number in a list",
Input: "[12.3]",
Expected: types.ArrayOK[float64]{
Array: []float64{12.3},
},
Name: "A single number in a list",
Input: "[12.3]",
Expected: types.ArrayOKArray(12.3),
},
{
Name: "Multiple values",
Input: "[1,2,3,4.5]",
Expected: types.ArrayOK[float64]{
Array: []float64{1, 2, 3, 4.5},
},
Name: "Multiple values",
Input: "[1,2,3,4.5]",
Expected: types.ArrayOKArray(1, 2, 3, 4.5),
},
{
Name: "Nil Value",
Input: "null",
Expected: types.ArrayOK[*float64]{},
},
{
Name: "Nil Array",
Input: "[1.2,null,3.4]",
Expected: func() types.ArrayOK[*float64] {
original := types.ArrayOKArray(1.2, 0, 3.4)
original.Array[1] = nil
return original
}(),
},
}

for _, tt := range scenarios {
t.Run(tt.Name, func(t *testing.T) {
result := types.ArrayOK[float64]{}
result := types.ArrayOK[*float64]{}
err := json.Unmarshal([]byte(tt.Input), &result)
Expect(err).To(BeNil())
Expect(result).To(Equal(tt.Expected))
Expand All @@ -132,33 +156,41 @@ func TestFloat64Unmarshal(t *testing.T) {
func TestStringUnmarshal(t *testing.T) {
RegisterTestingT(t)

scenarios := []TestUnmarshalScenario[string]{
scenarios := []TestUnmarshalScenario[*string]{
{
Name: "A single string",
Input: `"hello"`,
Expected: types.ArrayOK[string]{
Value: "hello",
},
Name: "A single string",
Input: `"hello"`,
Expected: types.ArrayOKValue("hello"),
},
{
Name: "A single string in a list",
Input: `["hello"]`,
Expected: types.ArrayOK[string]{
Array: []string{"hello"},
},
Name: "A single string in a list",
Input: `["hello"]`,
Expected: types.ArrayOKArray("hello"),
},
{
Name: "Multiple strings",
Input: `["hello","world","foo","bar"]`,
Expected: types.ArrayOK[string]{
Array: []string{"hello", "world", "foo", "bar"},
},
Name: "Multiple strings",
Input: `["hello","world","foo","bar"]`,
Expected: types.ArrayOKArray("hello", "world", "foo", "bar"),
},
{
Name: "Nil Value",
Input: "null",
Expected: types.ArrayOK[*string]{},
},
{
Name: "Nil Array",
Input: `["hello",null,"world"]`,
Expected: func() types.ArrayOK[*string] {
original := types.ArrayOKArray("hello", "", "world")
original.Array[1] = nil
return original
}(),
},
}

for _, tt := range scenarios {
t.Run(tt.Name, func(t *testing.T) {
result := types.ArrayOK[string]{}
result := types.ArrayOK[*string]{}
err := json.Unmarshal([]byte(tt.Input), &result)
Expect(err).To(BeNil())
Expect(result).To(Equal(tt.Expected))
Expand All @@ -175,37 +207,51 @@ type Color struct {
func TestColorUnmarshal(t *testing.T) {
RegisterTestingT(t)

scenarios := []TestUnmarshalScenario[Color]{
scenarios := []TestUnmarshalScenario[*Color]{
{
Name: "A single color",
Input: `{"red": 255, "green": 255, "blue": 255}`,
Expected: types.ArrayOK[Color]{
Value: Color{Red: 255, Green: 255, Blue: 255},
},
Name: "A single color",
Input: `{"red": 255, "green": 255, "blue": 255}`,
Expected: types.ArrayOKValue(Color{Red: 255, Green: 255, Blue: 255}),
},
{
Name: "A single color in a list",
Input: `[{"red": 255, "green": 255, "blue": 255}]`,
Expected: types.ArrayOK[Color]{
Array: []Color{{Red: 255, Green: 255, Blue: 255}},
Expected: types.ArrayOK[*Color]{
Array: []*Color{{Red: 255, Green: 255, Blue: 255}},
},
},
{
Name: "Multiple colors",
Input: `[{"red": 255, "green": 255, "blue": 255}, {"red": 0, "green": 0, "blue": 0}, {"red": 0, "green": 255, "blue": 0}]`,
Expected: types.ArrayOK[Color]{
Array: []Color{
Expected: types.ArrayOK[*Color]{
Array: []*Color{
{Red: 255, Green: 255, Blue: 255},
{Red: 0, Green: 0, Blue: 0},
{Red: 0, Green: 255, Blue: 0},
},
},
},
{
Name: "Nil Value",
Input: "null",
Expected: types.ArrayOK[*Color]{},
},
{
Name: "Nil Array",
Input: `[{"red": 255, "green": 255, "blue": 255}, null, {"red": 0, "green": 0, "blue": 0}]`,
Expected: types.ArrayOK[*Color]{
Array: []*Color{
{Red: 255, Green: 255, Blue: 255},
nil,
{Red: 0, Green: 0, Blue: 0},
},
},
},
}

for _, tt := range scenarios {
t.Run(tt.Name, func(t *testing.T) {
result := types.ArrayOK[Color]{}
result := types.ArrayOK[*Color]{}
err := json.Unmarshal([]byte(tt.Input), &result)
Expect(err).To(BeNil())
Expect(result).To(Equal(tt.Expected))
Expand All @@ -216,32 +262,46 @@ func TestColorUnmarshal(t *testing.T) {
func TestColorMarshal(t *testing.T) {
RegisterTestingT(t)

scenarios := []TestMarshallScenario[Color]{
scenarios := []TestMarshallScenario[*Color]{
{
Name: "A single color",
Input: types.ArrayOK[Color]{
Value: Color{Red: 255, Green: 255, Blue: 255},
},
Name: "A single color",
Input: types.ArrayOKValue(Color{Red: 255, Green: 255, Blue: 255}),
Expected: `{"red":255,"green":255,"blue":255}`,
},
{
Name: "A single color in a list",
Input: types.ArrayOK[Color]{
Array: []Color{{Red: 255, Green: 255, Blue: 255}},
Input: types.ArrayOK[*Color]{
Array: []*Color{{Red: 255, Green: 255, Blue: 255}},
},
Expected: `[{"red":255,"green":255,"blue":255}]`,
},
{
Name: "Multiple colors",
Input: types.ArrayOK[Color]{
Array: []Color{
Input: types.ArrayOK[*Color]{
Array: []*Color{
{Red: 255, Green: 255, Blue: 255},
{Red: 0, Green: 0, Blue: 0},
{Red: 0, Green: 255, Blue: 0},
},
},
Expected: `[{"red":255,"green":255,"blue":255},{"red":0,"green":0,"blue":0},{"red":0,"green":255,"blue":0}]`,
},
{
Name: "Nil Value",
Input: types.ArrayOK[*Color]{},
Expected: "null",
},
{
Name: "Nil Array",
Input: types.ArrayOK[*Color]{
Array: []*Color{
{Red: 255, Green: 255, Blue: 255},
nil,
{Red: 0, Green: 0, Blue: 0},
},
},
Expected: `[{"red":255,"green":255,"blue":255},null,{"red":0,"green":0,"blue":0}]`,
},
}

for _, tt := range scenarios {
Expand Down

0 comments on commit cda0973

Please sign in to comment.