diff --git a/scripts/gen/main.go b/scripts/gen/main.go index bf83a86..69cbb1e 100644 --- a/scripts/gen/main.go +++ b/scripts/gen/main.go @@ -6,6 +6,7 @@ import ( "os" "os/exec" "path" + "strconv" "strings" "text/template" ) @@ -14,8 +15,6 @@ import ( type templateContext struct { Indexes []int Len int - TypeName string - TypeDecl string GenericTypesForward string } @@ -24,7 +23,7 @@ const maxTupleLength = 9 var funcMap = template.FuncMap{ "quote": func(value interface{}) string { - return fmt.Sprintf("%q", fmt.Sprint(value)) + return strconv.Quote(fmt.Sprint(value)) }, "inc": func(value int) int { return value + 1 @@ -43,6 +42,14 @@ var funcMap = template.FuncMap{ }, "genericTypesDecl": genTypesDecl, "genericTypesDeclGenericConstraint": genTypesDeclGenericConstraint, + "buildSingleTypedOverload": func(indexes []int, typ string) string { + typesArray := make([]string, 0, len(indexes)) + for range indexes { + typesArray = append(typesArray, typ) + } + + return fmt.Sprintf("T%d[%s]", len(indexes), strings.Join(typesArray, ", ")) + }, } //go:embed tuple.tpl diff --git a/scripts/gen/tuple.tpl b/scripts/gen/tuple.tpl index 988b869..117daad 100644 --- a/scripts/gen/tuple.tpl +++ b/scripts/gen/tuple.tpl @@ -1,7 +1,9 @@ package tuple import ( + "encoding/json" "fmt" + "golang.org/x/exp/constraints" ) @@ -229,3 +231,24 @@ func GreaterOrEqual{{.Len}}[{{genericTypesDecl .Indexes "constraints.Ordered"}}] func GreaterOrEqual{{.Len}}C[{{genericTypesDeclGenericConstraint .Indexes "Comparable"}}](host, guest T{{.Len}}[{{.GenericTypesForward}}]) bool { return Compare{{.Len}}C(host, guest).GE() } + +// MarshalJSON marshals the tuple into a JSON array. +func (t {{$typeRef}}) MarshalJSON() ([]byte, error) { + return json.Marshal(t.Slice()) +} + +// MarshalJSON unmarshals the tuple from a JSON array. +func (t *{{$typeRef}}) UnmarshalJSON(data []byte) error { + var slice []any + if err := json.Unmarshal(data, &slice); err != nil { + return err + } + + unmarshalled, err := FromSlice{{.Len}}[{{.GenericTypesForward}}](slice) + if err != nil { + return err + } + + *t = unmarshalled + return nil +} diff --git a/scripts/gen/tuple_test.tpl b/scripts/gen/tuple_test.tpl index ec9f408..44d3d1e 100644 --- a/scripts/gen/tuple_test.tpl +++ b/scripts/gen/tuple_test.tpl @@ -1,6 +1,7 @@ package tuple import ( + "encoding/json" "testing" "github.com/stretchr/testify/require" @@ -9,10 +10,11 @@ import ( {{/* These variables can be used when the context of dot changes. */}} {{$indexes := .Indexes}} {{$len := .Len}} +{{$stringOverload := buildSingleTypedOverload $indexes "string"}} func TestT{{.Len}}_New(t *testing.T) { tup := New{{.Len}}({{range .Indexes}}{{. | quote}},{{end}}) - require.Equal(t, T{{.Len}}[{{range $i, $index := .Indexes}}{{if gt $i 0}}, {{end}}string{{end}}]{ + require.Equal(t, {{$stringOverload}}{ {{range .Indexes -}} V{{.}}: {{. | quote}}, {{end}} @@ -227,7 +229,7 @@ func TestT{{.Len}}_String(t *testing.T) { func TestT{{.Len}}_GoString(t *testing.T) { tup := New{{.Len}}({{range .Indexes}}{{. | quote}},{{end}}) - require.Equal(t, `tuple.T{{.Len}}[{{range $i, $index := .Indexes}}{{if gt $i 0}}, {{end}}string{{end}}]{ + require.Equal(t, `tuple.{{$stringOverload}}{ {{- range $i, $index := .Indexes -}} {{- if gt $i 0}}, {{end -}} V{{$index}}: {{$index | quote}} @@ -281,7 +283,7 @@ func TestT{{.Len}}_FromArrayX(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - do := func () T{{.Len}}[{{range $i, $index := .Indexes}}{{if gt $i 0}}, {{end}}string{{end}}] { + do := func () {{$stringOverload}} { return FromArray{{.Len}}X[{{range $i, $index := .Indexes}}{{if gt $i 0}}, {{end}}string{{end}}](tt.array) } @@ -393,7 +395,7 @@ func TestT{{.Len}}_FromSliceX(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - do := func () T{{.Len}}[{{range $i, $index := .Indexes}}{{if gt $i 0}}, {{end}}string{{end}}] { + do := func () {{$stringOverload}} { return FromSlice{{.Len}}X[{{range $i, $index := .Indexes}}{{if gt $i 0}}, {{end}}string{{end}}](tt.slice) } @@ -472,3 +474,96 @@ func TestT{{.Len}}_FromSlice(t *testing.T) { }) } } + +func TestT{{.Len}}_MarshalJSON(t *testing.T) { + tup := New{{.Len}}({{range .Indexes}}{{. | quote}},{{end}}) + + got, err := json.Marshal(tup) + require.NoError(t, err) + require.Equal(t, got, []byte(`[{{range .Indexes}}{{if ne . 1}},{{end}}{{. | quote}}{{end}}]`)) +} + +func TestT{{.Len}}_UnmarshalJSON(t *testing.T) { + tests := []struct{ + name string + data []byte + want {{$stringOverload}} + wantErr bool + }{ + { + name: "nil data", + data: nil, + wantErr: true, + }, + { + name: "empty data", + data: []byte{}, + wantErr: true, + }, + { + name: "string data", + data: []byte(`"hi"`), + wantErr: true, + }, + { + name: "empty json array", + data: []byte(`[]`), + wantErr: true, + }, + { + name: "longer json array", + data: []byte(`["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]`), + wantErr: true, + }, + { + name: "json array of invalid types", + data: []byte(`[{{range $.Indexes}}{{if ne . 1}},{{end}}{{.}}{{end}}]`), + wantErr: true, + }, + {{if gt .Len 1 -}} + { + name: "json array with 1 invalid type", + data: []byte(`[{{range $.Indexes}}{{if ne . 1}},{{end}}{{if eq . 1}}{{.}}{{else}}{{. | quote}}{{end}}{{end}}]`), + wantErr: true, + }, + {{- end}} + { + name: "json array of valid types", + data: []byte(`[{{range $.Indexes}}{{if ne . 1}},{{end}}{{. | quote}}{{end}}]`), + want: New{{.Len}}({{range .Indexes}}{{. | quote}},{{end}}), + wantErr: false, + }, + { + name: "json object of valid types", + data: []byte(`{{"{"}}{{range $.Indexes}}{{if ne . 1}},{{end}}"V{{.}}": {{. | quote}}{{end}}{{"}"}}`), + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var got {{$stringOverload}} + err := json.Unmarshal(tt.data, &got) + if tt.wantErr { + require.Error(t, err) + return + } + + require.NoError(t, err) + require.Equal(t, tt.want, got) + }) + } +} + +func TestT{{.Len}}_Marshal_Unmarshal(t *testing.T) { + tup := New{{.Len}}({{range .Indexes}}{{. | quote}},{{end}}) + + marshalled, err := json.Marshal(tup) + require.NoError(t, err) + + var unmarshalled {{$stringOverload}} + err = json.Unmarshal(marshalled, &unmarshalled) + + require.NoError(t, err) + require.Equal(t, tup, unmarshalled) +} diff --git a/tuple1.go b/tuple1.go index 64ebbec..d9920dc 100644 --- a/tuple1.go +++ b/tuple1.go @@ -1,7 +1,9 @@ package tuple import ( + "encoding/json" "fmt" + "golang.org/x/exp/constraints" ) @@ -194,3 +196,24 @@ func GreaterOrEqual1[Ty1 constraints.Ordered](host, guest T1[Ty1]) bool { func GreaterOrEqual1C[Ty1 Comparable[Ty1]](host, guest T1[Ty1]) bool { return Compare1C(host, guest).GE() } + +// MarshalJSON marshals the tuple into a JSON array. +func (t T1[Ty1]) MarshalJSON() ([]byte, error) { + return json.Marshal(t.Slice()) +} + +// MarshalJSON unmarshals the tuple from a JSON array. +func (t *T1[Ty1]) UnmarshalJSON(data []byte) error { + var slice []any + if err := json.Unmarshal(data, &slice); err != nil { + return err + } + + unmarshalled, err := FromSlice1[Ty1](slice) + if err != nil { + return err + } + + *t = unmarshalled + return nil +} diff --git a/tuple1_test.go b/tuple1_test.go index 6dbd44b..4d6723b 100644 --- a/tuple1_test.go +++ b/tuple1_test.go @@ -1,6 +1,7 @@ package tuple import ( + "encoding/json" "testing" "github.com/stretchr/testify/require" @@ -419,3 +420,90 @@ func TestT1_FromSlice(t *testing.T) { }) } } + +func TestT1_MarshalJSON(t *testing.T) { + tup := New1("1") + + got, err := json.Marshal(tup) + require.NoError(t, err) + require.Equal(t, got, []byte(`["1"]`)) +} + +func TestT1_UnmarshalJSON(t *testing.T) { + tests := []struct { + name string + data []byte + want T1[string] + wantErr bool + }{ + { + name: "nil data", + data: nil, + wantErr: true, + }, + { + name: "empty data", + data: []byte{}, + wantErr: true, + }, + { + name: "string data", + data: []byte(`"hi"`), + wantErr: true, + }, + { + name: "empty json array", + data: []byte(`[]`), + wantErr: true, + }, + { + name: "longer json array", + data: []byte(`["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]`), + wantErr: true, + }, + { + name: "json array of invalid types", + data: []byte(`[1]`), + wantErr: true, + }, + + { + name: "json array of valid types", + data: []byte(`["1"]`), + want: New1("1"), + wantErr: false, + }, + { + name: "json object of valid types", + data: []byte(`{"V1": "1"}`), + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var got T1[string] + err := json.Unmarshal(tt.data, &got) + if tt.wantErr { + require.Error(t, err) + return + } + + require.NoError(t, err) + require.Equal(t, tt.want, got) + }) + } +} + +func TestT1_Marshal_Unmarshal(t *testing.T) { + tup := New1("1") + + marshalled, err := json.Marshal(tup) + require.NoError(t, err) + + var unmarshalled T1[string] + err = json.Unmarshal(marshalled, &unmarshalled) + + require.NoError(t, err) + require.Equal(t, tup, unmarshalled) +} diff --git a/tuple2.go b/tuple2.go index d333996..9b70e23 100644 --- a/tuple2.go +++ b/tuple2.go @@ -1,7 +1,9 @@ package tuple import ( + "encoding/json" "fmt" + "golang.org/x/exp/constraints" ) @@ -210,3 +212,24 @@ func GreaterOrEqual2[Ty1, Ty2 constraints.Ordered](host, guest T2[Ty1, Ty2]) boo func GreaterOrEqual2C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2]](host, guest T2[Ty1, Ty2]) bool { return Compare2C(host, guest).GE() } + +// MarshalJSON marshals the tuple into a JSON array. +func (t T2[Ty1, Ty2]) MarshalJSON() ([]byte, error) { + return json.Marshal(t.Slice()) +} + +// MarshalJSON unmarshals the tuple from a JSON array. +func (t *T2[Ty1, Ty2]) UnmarshalJSON(data []byte) error { + var slice []any + if err := json.Unmarshal(data, &slice); err != nil { + return err + } + + unmarshalled, err := FromSlice2[Ty1, Ty2](slice) + if err != nil { + return err + } + + *t = unmarshalled + return nil +} diff --git a/tuple2_test.go b/tuple2_test.go index 090ebf9..9dab93c 100644 --- a/tuple2_test.go +++ b/tuple2_test.go @@ -1,6 +1,7 @@ package tuple import ( + "encoding/json" "testing" "github.com/stretchr/testify/require" @@ -445,3 +446,94 @@ func TestT2_FromSlice(t *testing.T) { }) } } + +func TestT2_MarshalJSON(t *testing.T) { + tup := New2("1", "2") + + got, err := json.Marshal(tup) + require.NoError(t, err) + require.Equal(t, got, []byte(`["1","2"]`)) +} + +func TestT2_UnmarshalJSON(t *testing.T) { + tests := []struct { + name string + data []byte + want T2[string, string] + wantErr bool + }{ + { + name: "nil data", + data: nil, + wantErr: true, + }, + { + name: "empty data", + data: []byte{}, + wantErr: true, + }, + { + name: "string data", + data: []byte(`"hi"`), + wantErr: true, + }, + { + name: "empty json array", + data: []byte(`[]`), + wantErr: true, + }, + { + name: "longer json array", + data: []byte(`["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]`), + wantErr: true, + }, + { + name: "json array of invalid types", + data: []byte(`[1,2]`), + wantErr: true, + }, + { + name: "json array with 1 invalid type", + data: []byte(`[1,"2"]`), + wantErr: true, + }, + { + name: "json array of valid types", + data: []byte(`["1","2"]`), + want: New2("1", "2"), + wantErr: false, + }, + { + name: "json object of valid types", + data: []byte(`{"V1": "1","V2": "2"}`), + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var got T2[string, string] + err := json.Unmarshal(tt.data, &got) + if tt.wantErr { + require.Error(t, err) + return + } + + require.NoError(t, err) + require.Equal(t, tt.want, got) + }) + } +} + +func TestT2_Marshal_Unmarshal(t *testing.T) { + tup := New2("1", "2") + + marshalled, err := json.Marshal(tup) + require.NoError(t, err) + + var unmarshalled T2[string, string] + err = json.Unmarshal(marshalled, &unmarshalled) + + require.NoError(t, err) + require.Equal(t, tup, unmarshalled) +} diff --git a/tuple3.go b/tuple3.go index dcedbcc..d9f2f33 100644 --- a/tuple3.go +++ b/tuple3.go @@ -1,7 +1,9 @@ package tuple import ( + "encoding/json" "fmt" + "golang.org/x/exp/constraints" ) @@ -226,3 +228,24 @@ func GreaterOrEqual3[Ty1, Ty2, Ty3 constraints.Ordered](host, guest T3[Ty1, Ty2, func GreaterOrEqual3C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3]](host, guest T3[Ty1, Ty2, Ty3]) bool { return Compare3C(host, guest).GE() } + +// MarshalJSON marshals the tuple into a JSON array. +func (t T3[Ty1, Ty2, Ty3]) MarshalJSON() ([]byte, error) { + return json.Marshal(t.Slice()) +} + +// MarshalJSON unmarshals the tuple from a JSON array. +func (t *T3[Ty1, Ty2, Ty3]) UnmarshalJSON(data []byte) error { + var slice []any + if err := json.Unmarshal(data, &slice); err != nil { + return err + } + + unmarshalled, err := FromSlice3[Ty1, Ty2, Ty3](slice) + if err != nil { + return err + } + + *t = unmarshalled + return nil +} diff --git a/tuple3_test.go b/tuple3_test.go index d6ea7af..893fbf5 100644 --- a/tuple3_test.go +++ b/tuple3_test.go @@ -1,6 +1,7 @@ package tuple import ( + "encoding/json" "testing" "github.com/stretchr/testify/require" @@ -475,3 +476,94 @@ func TestT3_FromSlice(t *testing.T) { }) } } + +func TestT3_MarshalJSON(t *testing.T) { + tup := New3("1", "2", "3") + + got, err := json.Marshal(tup) + require.NoError(t, err) + require.Equal(t, got, []byte(`["1","2","3"]`)) +} + +func TestT3_UnmarshalJSON(t *testing.T) { + tests := []struct { + name string + data []byte + want T3[string, string, string] + wantErr bool + }{ + { + name: "nil data", + data: nil, + wantErr: true, + }, + { + name: "empty data", + data: []byte{}, + wantErr: true, + }, + { + name: "string data", + data: []byte(`"hi"`), + wantErr: true, + }, + { + name: "empty json array", + data: []byte(`[]`), + wantErr: true, + }, + { + name: "longer json array", + data: []byte(`["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]`), + wantErr: true, + }, + { + name: "json array of invalid types", + data: []byte(`[1,2,3]`), + wantErr: true, + }, + { + name: "json array with 1 invalid type", + data: []byte(`[1,"2","3"]`), + wantErr: true, + }, + { + name: "json array of valid types", + data: []byte(`["1","2","3"]`), + want: New3("1", "2", "3"), + wantErr: false, + }, + { + name: "json object of valid types", + data: []byte(`{"V1": "1","V2": "2","V3": "3"}`), + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var got T3[string, string, string] + err := json.Unmarshal(tt.data, &got) + if tt.wantErr { + require.Error(t, err) + return + } + + require.NoError(t, err) + require.Equal(t, tt.want, got) + }) + } +} + +func TestT3_Marshal_Unmarshal(t *testing.T) { + tup := New3("1", "2", "3") + + marshalled, err := json.Marshal(tup) + require.NoError(t, err) + + var unmarshalled T3[string, string, string] + err = json.Unmarshal(marshalled, &unmarshalled) + + require.NoError(t, err) + require.Equal(t, tup, unmarshalled) +} diff --git a/tuple4.go b/tuple4.go index 82d0ae7..9a86d2c 100644 --- a/tuple4.go +++ b/tuple4.go @@ -1,7 +1,9 @@ package tuple import ( + "encoding/json" "fmt" + "golang.org/x/exp/constraints" ) @@ -242,3 +244,24 @@ func GreaterOrEqual4[Ty1, Ty2, Ty3, Ty4 constraints.Ordered](host, guest T4[Ty1, func GreaterOrEqual4C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4]](host, guest T4[Ty1, Ty2, Ty3, Ty4]) bool { return Compare4C(host, guest).GE() } + +// MarshalJSON marshals the tuple into a JSON array. +func (t T4[Ty1, Ty2, Ty3, Ty4]) MarshalJSON() ([]byte, error) { + return json.Marshal(t.Slice()) +} + +// MarshalJSON unmarshals the tuple from a JSON array. +func (t *T4[Ty1, Ty2, Ty3, Ty4]) UnmarshalJSON(data []byte) error { + var slice []any + if err := json.Unmarshal(data, &slice); err != nil { + return err + } + + unmarshalled, err := FromSlice4[Ty1, Ty2, Ty3, Ty4](slice) + if err != nil { + return err + } + + *t = unmarshalled + return nil +} diff --git a/tuple4_test.go b/tuple4_test.go index 9d7cddf..c33f72f 100644 --- a/tuple4_test.go +++ b/tuple4_test.go @@ -1,6 +1,7 @@ package tuple import ( + "encoding/json" "testing" "github.com/stretchr/testify/require" @@ -501,3 +502,94 @@ func TestT4_FromSlice(t *testing.T) { }) } } + +func TestT4_MarshalJSON(t *testing.T) { + tup := New4("1", "2", "3", "4") + + got, err := json.Marshal(tup) + require.NoError(t, err) + require.Equal(t, got, []byte(`["1","2","3","4"]`)) +} + +func TestT4_UnmarshalJSON(t *testing.T) { + tests := []struct { + name string + data []byte + want T4[string, string, string, string] + wantErr bool + }{ + { + name: "nil data", + data: nil, + wantErr: true, + }, + { + name: "empty data", + data: []byte{}, + wantErr: true, + }, + { + name: "string data", + data: []byte(`"hi"`), + wantErr: true, + }, + { + name: "empty json array", + data: []byte(`[]`), + wantErr: true, + }, + { + name: "longer json array", + data: []byte(`["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]`), + wantErr: true, + }, + { + name: "json array of invalid types", + data: []byte(`[1,2,3,4]`), + wantErr: true, + }, + { + name: "json array with 1 invalid type", + data: []byte(`[1,"2","3","4"]`), + wantErr: true, + }, + { + name: "json array of valid types", + data: []byte(`["1","2","3","4"]`), + want: New4("1", "2", "3", "4"), + wantErr: false, + }, + { + name: "json object of valid types", + data: []byte(`{"V1": "1","V2": "2","V3": "3","V4": "4"}`), + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var got T4[string, string, string, string] + err := json.Unmarshal(tt.data, &got) + if tt.wantErr { + require.Error(t, err) + return + } + + require.NoError(t, err) + require.Equal(t, tt.want, got) + }) + } +} + +func TestT4_Marshal_Unmarshal(t *testing.T) { + tup := New4("1", "2", "3", "4") + + marshalled, err := json.Marshal(tup) + require.NoError(t, err) + + var unmarshalled T4[string, string, string, string] + err = json.Unmarshal(marshalled, &unmarshalled) + + require.NoError(t, err) + require.Equal(t, tup, unmarshalled) +} diff --git a/tuple5.go b/tuple5.go index 5535e59..4618c28 100644 --- a/tuple5.go +++ b/tuple5.go @@ -1,7 +1,9 @@ package tuple import ( + "encoding/json" "fmt" + "golang.org/x/exp/constraints" ) @@ -258,3 +260,24 @@ func GreaterOrEqual5[Ty1, Ty2, Ty3, Ty4, Ty5 constraints.Ordered](host, guest T5 func GreaterOrEqual5C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5]](host, guest T5[Ty1, Ty2, Ty3, Ty4, Ty5]) bool { return Compare5C(host, guest).GE() } + +// MarshalJSON marshals the tuple into a JSON array. +func (t T5[Ty1, Ty2, Ty3, Ty4, Ty5]) MarshalJSON() ([]byte, error) { + return json.Marshal(t.Slice()) +} + +// MarshalJSON unmarshals the tuple from a JSON array. +func (t *T5[Ty1, Ty2, Ty3, Ty4, Ty5]) UnmarshalJSON(data []byte) error { + var slice []any + if err := json.Unmarshal(data, &slice); err != nil { + return err + } + + unmarshalled, err := FromSlice5[Ty1, Ty2, Ty3, Ty4, Ty5](slice) + if err != nil { + return err + } + + *t = unmarshalled + return nil +} diff --git a/tuple5_test.go b/tuple5_test.go index 74ff5b5..3883b94 100644 --- a/tuple5_test.go +++ b/tuple5_test.go @@ -1,6 +1,7 @@ package tuple import ( + "encoding/json" "testing" "github.com/stretchr/testify/require" @@ -527,3 +528,94 @@ func TestT5_FromSlice(t *testing.T) { }) } } + +func TestT5_MarshalJSON(t *testing.T) { + tup := New5("1", "2", "3", "4", "5") + + got, err := json.Marshal(tup) + require.NoError(t, err) + require.Equal(t, got, []byte(`["1","2","3","4","5"]`)) +} + +func TestT5_UnmarshalJSON(t *testing.T) { + tests := []struct { + name string + data []byte + want T5[string, string, string, string, string] + wantErr bool + }{ + { + name: "nil data", + data: nil, + wantErr: true, + }, + { + name: "empty data", + data: []byte{}, + wantErr: true, + }, + { + name: "string data", + data: []byte(`"hi"`), + wantErr: true, + }, + { + name: "empty json array", + data: []byte(`[]`), + wantErr: true, + }, + { + name: "longer json array", + data: []byte(`["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]`), + wantErr: true, + }, + { + name: "json array of invalid types", + data: []byte(`[1,2,3,4,5]`), + wantErr: true, + }, + { + name: "json array with 1 invalid type", + data: []byte(`[1,"2","3","4","5"]`), + wantErr: true, + }, + { + name: "json array of valid types", + data: []byte(`["1","2","3","4","5"]`), + want: New5("1", "2", "3", "4", "5"), + wantErr: false, + }, + { + name: "json object of valid types", + data: []byte(`{"V1": "1","V2": "2","V3": "3","V4": "4","V5": "5"}`), + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var got T5[string, string, string, string, string] + err := json.Unmarshal(tt.data, &got) + if tt.wantErr { + require.Error(t, err) + return + } + + require.NoError(t, err) + require.Equal(t, tt.want, got) + }) + } +} + +func TestT5_Marshal_Unmarshal(t *testing.T) { + tup := New5("1", "2", "3", "4", "5") + + marshalled, err := json.Marshal(tup) + require.NoError(t, err) + + var unmarshalled T5[string, string, string, string, string] + err = json.Unmarshal(marshalled, &unmarshalled) + + require.NoError(t, err) + require.Equal(t, tup, unmarshalled) +} diff --git a/tuple6.go b/tuple6.go index 3638f9c..829fb20 100644 --- a/tuple6.go +++ b/tuple6.go @@ -1,7 +1,9 @@ package tuple import ( + "encoding/json" "fmt" + "golang.org/x/exp/constraints" ) @@ -274,3 +276,24 @@ func GreaterOrEqual6[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6 constraints.Ordered](host, gue func GreaterOrEqual6C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6]](host, guest T6[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6]) bool { return Compare6C(host, guest).GE() } + +// MarshalJSON marshals the tuple into a JSON array. +func (t T6[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6]) MarshalJSON() ([]byte, error) { + return json.Marshal(t.Slice()) +} + +// MarshalJSON unmarshals the tuple from a JSON array. +func (t *T6[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6]) UnmarshalJSON(data []byte) error { + var slice []any + if err := json.Unmarshal(data, &slice); err != nil { + return err + } + + unmarshalled, err := FromSlice6[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6](slice) + if err != nil { + return err + } + + *t = unmarshalled + return nil +} diff --git a/tuple6_test.go b/tuple6_test.go index 05ab09a..d294814 100644 --- a/tuple6_test.go +++ b/tuple6_test.go @@ -1,6 +1,7 @@ package tuple import ( + "encoding/json" "testing" "github.com/stretchr/testify/require" @@ -553,3 +554,94 @@ func TestT6_FromSlice(t *testing.T) { }) } } + +func TestT6_MarshalJSON(t *testing.T) { + tup := New6("1", "2", "3", "4", "5", "6") + + got, err := json.Marshal(tup) + require.NoError(t, err) + require.Equal(t, got, []byte(`["1","2","3","4","5","6"]`)) +} + +func TestT6_UnmarshalJSON(t *testing.T) { + tests := []struct { + name string + data []byte + want T6[string, string, string, string, string, string] + wantErr bool + }{ + { + name: "nil data", + data: nil, + wantErr: true, + }, + { + name: "empty data", + data: []byte{}, + wantErr: true, + }, + { + name: "string data", + data: []byte(`"hi"`), + wantErr: true, + }, + { + name: "empty json array", + data: []byte(`[]`), + wantErr: true, + }, + { + name: "longer json array", + data: []byte(`["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]`), + wantErr: true, + }, + { + name: "json array of invalid types", + data: []byte(`[1,2,3,4,5,6]`), + wantErr: true, + }, + { + name: "json array with 1 invalid type", + data: []byte(`[1,"2","3","4","5","6"]`), + wantErr: true, + }, + { + name: "json array of valid types", + data: []byte(`["1","2","3","4","5","6"]`), + want: New6("1", "2", "3", "4", "5", "6"), + wantErr: false, + }, + { + name: "json object of valid types", + data: []byte(`{"V1": "1","V2": "2","V3": "3","V4": "4","V5": "5","V6": "6"}`), + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var got T6[string, string, string, string, string, string] + err := json.Unmarshal(tt.data, &got) + if tt.wantErr { + require.Error(t, err) + return + } + + require.NoError(t, err) + require.Equal(t, tt.want, got) + }) + } +} + +func TestT6_Marshal_Unmarshal(t *testing.T) { + tup := New6("1", "2", "3", "4", "5", "6") + + marshalled, err := json.Marshal(tup) + require.NoError(t, err) + + var unmarshalled T6[string, string, string, string, string, string] + err = json.Unmarshal(marshalled, &unmarshalled) + + require.NoError(t, err) + require.Equal(t, tup, unmarshalled) +} diff --git a/tuple7.go b/tuple7.go index 6e9dcb4..3f095a6 100644 --- a/tuple7.go +++ b/tuple7.go @@ -1,7 +1,9 @@ package tuple import ( + "encoding/json" "fmt" + "golang.org/x/exp/constraints" ) @@ -290,3 +292,24 @@ func GreaterOrEqual7[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7 constraints.Ordered](host func GreaterOrEqual7C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6], Ty7 Comparable[Ty7]](host, guest T7[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7]) bool { return Compare7C(host, guest).GE() } + +// MarshalJSON marshals the tuple into a JSON array. +func (t T7[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7]) MarshalJSON() ([]byte, error) { + return json.Marshal(t.Slice()) +} + +// MarshalJSON unmarshals the tuple from a JSON array. +func (t *T7[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7]) UnmarshalJSON(data []byte) error { + var slice []any + if err := json.Unmarshal(data, &slice); err != nil { + return err + } + + unmarshalled, err := FromSlice7[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7](slice) + if err != nil { + return err + } + + *t = unmarshalled + return nil +} diff --git a/tuple7_test.go b/tuple7_test.go index b17a20c..6a63786 100644 --- a/tuple7_test.go +++ b/tuple7_test.go @@ -1,6 +1,7 @@ package tuple import ( + "encoding/json" "testing" "github.com/stretchr/testify/require" @@ -579,3 +580,94 @@ func TestT7_FromSlice(t *testing.T) { }) } } + +func TestT7_MarshalJSON(t *testing.T) { + tup := New7("1", "2", "3", "4", "5", "6", "7") + + got, err := json.Marshal(tup) + require.NoError(t, err) + require.Equal(t, got, []byte(`["1","2","3","4","5","6","7"]`)) +} + +func TestT7_UnmarshalJSON(t *testing.T) { + tests := []struct { + name string + data []byte + want T7[string, string, string, string, string, string, string] + wantErr bool + }{ + { + name: "nil data", + data: nil, + wantErr: true, + }, + { + name: "empty data", + data: []byte{}, + wantErr: true, + }, + { + name: "string data", + data: []byte(`"hi"`), + wantErr: true, + }, + { + name: "empty json array", + data: []byte(`[]`), + wantErr: true, + }, + { + name: "longer json array", + data: []byte(`["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]`), + wantErr: true, + }, + { + name: "json array of invalid types", + data: []byte(`[1,2,3,4,5,6,7]`), + wantErr: true, + }, + { + name: "json array with 1 invalid type", + data: []byte(`[1,"2","3","4","5","6","7"]`), + wantErr: true, + }, + { + name: "json array of valid types", + data: []byte(`["1","2","3","4","5","6","7"]`), + want: New7("1", "2", "3", "4", "5", "6", "7"), + wantErr: false, + }, + { + name: "json object of valid types", + data: []byte(`{"V1": "1","V2": "2","V3": "3","V4": "4","V5": "5","V6": "6","V7": "7"}`), + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var got T7[string, string, string, string, string, string, string] + err := json.Unmarshal(tt.data, &got) + if tt.wantErr { + require.Error(t, err) + return + } + + require.NoError(t, err) + require.Equal(t, tt.want, got) + }) + } +} + +func TestT7_Marshal_Unmarshal(t *testing.T) { + tup := New7("1", "2", "3", "4", "5", "6", "7") + + marshalled, err := json.Marshal(tup) + require.NoError(t, err) + + var unmarshalled T7[string, string, string, string, string, string, string] + err = json.Unmarshal(marshalled, &unmarshalled) + + require.NoError(t, err) + require.Equal(t, tup, unmarshalled) +} diff --git a/tuple8.go b/tuple8.go index cddeb38..ddce947 100644 --- a/tuple8.go +++ b/tuple8.go @@ -1,7 +1,9 @@ package tuple import ( + "encoding/json" "fmt" + "golang.org/x/exp/constraints" ) @@ -306,3 +308,24 @@ func GreaterOrEqual8[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8 constraints.Ordered] func GreaterOrEqual8C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6], Ty7 Comparable[Ty7], Ty8 Comparable[Ty8]](host, guest T8[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8]) bool { return Compare8C(host, guest).GE() } + +// MarshalJSON marshals the tuple into a JSON array. +func (t T8[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8]) MarshalJSON() ([]byte, error) { + return json.Marshal(t.Slice()) +} + +// MarshalJSON unmarshals the tuple from a JSON array. +func (t *T8[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8]) UnmarshalJSON(data []byte) error { + var slice []any + if err := json.Unmarshal(data, &slice); err != nil { + return err + } + + unmarshalled, err := FromSlice8[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8](slice) + if err != nil { + return err + } + + *t = unmarshalled + return nil +} diff --git a/tuple8_test.go b/tuple8_test.go index 94aebc6..53e8db9 100644 --- a/tuple8_test.go +++ b/tuple8_test.go @@ -1,6 +1,7 @@ package tuple import ( + "encoding/json" "testing" "github.com/stretchr/testify/require" @@ -605,3 +606,94 @@ func TestT8_FromSlice(t *testing.T) { }) } } + +func TestT8_MarshalJSON(t *testing.T) { + tup := New8("1", "2", "3", "4", "5", "6", "7", "8") + + got, err := json.Marshal(tup) + require.NoError(t, err) + require.Equal(t, got, []byte(`["1","2","3","4","5","6","7","8"]`)) +} + +func TestT8_UnmarshalJSON(t *testing.T) { + tests := []struct { + name string + data []byte + want T8[string, string, string, string, string, string, string, string] + wantErr bool + }{ + { + name: "nil data", + data: nil, + wantErr: true, + }, + { + name: "empty data", + data: []byte{}, + wantErr: true, + }, + { + name: "string data", + data: []byte(`"hi"`), + wantErr: true, + }, + { + name: "empty json array", + data: []byte(`[]`), + wantErr: true, + }, + { + name: "longer json array", + data: []byte(`["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]`), + wantErr: true, + }, + { + name: "json array of invalid types", + data: []byte(`[1,2,3,4,5,6,7,8]`), + wantErr: true, + }, + { + name: "json array with 1 invalid type", + data: []byte(`[1,"2","3","4","5","6","7","8"]`), + wantErr: true, + }, + { + name: "json array of valid types", + data: []byte(`["1","2","3","4","5","6","7","8"]`), + want: New8("1", "2", "3", "4", "5", "6", "7", "8"), + wantErr: false, + }, + { + name: "json object of valid types", + data: []byte(`{"V1": "1","V2": "2","V3": "3","V4": "4","V5": "5","V6": "6","V7": "7","V8": "8"}`), + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var got T8[string, string, string, string, string, string, string, string] + err := json.Unmarshal(tt.data, &got) + if tt.wantErr { + require.Error(t, err) + return + } + + require.NoError(t, err) + require.Equal(t, tt.want, got) + }) + } +} + +func TestT8_Marshal_Unmarshal(t *testing.T) { + tup := New8("1", "2", "3", "4", "5", "6", "7", "8") + + marshalled, err := json.Marshal(tup) + require.NoError(t, err) + + var unmarshalled T8[string, string, string, string, string, string, string, string] + err = json.Unmarshal(marshalled, &unmarshalled) + + require.NoError(t, err) + require.Equal(t, tup, unmarshalled) +} diff --git a/tuple9.go b/tuple9.go index cb972cd..77feacc 100644 --- a/tuple9.go +++ b/tuple9.go @@ -1,7 +1,9 @@ package tuple import ( + "encoding/json" "fmt" + "golang.org/x/exp/constraints" ) @@ -322,3 +324,24 @@ func GreaterOrEqual9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9 constraints.Ord func GreaterOrEqual9C[Ty1 Comparable[Ty1], Ty2 Comparable[Ty2], Ty3 Comparable[Ty3], Ty4 Comparable[Ty4], Ty5 Comparable[Ty5], Ty6 Comparable[Ty6], Ty7 Comparable[Ty7], Ty8 Comparable[Ty8], Ty9 Comparable[Ty9]](host, guest T9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9]) bool { return Compare9C(host, guest).GE() } + +// MarshalJSON marshals the tuple into a JSON array. +func (t T9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9]) MarshalJSON() ([]byte, error) { + return json.Marshal(t.Slice()) +} + +// MarshalJSON unmarshals the tuple from a JSON array. +func (t *T9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9]) UnmarshalJSON(data []byte) error { + var slice []any + if err := json.Unmarshal(data, &slice); err != nil { + return err + } + + unmarshalled, err := FromSlice9[Ty1, Ty2, Ty3, Ty4, Ty5, Ty6, Ty7, Ty8, Ty9](slice) + if err != nil { + return err + } + + *t = unmarshalled + return nil +} diff --git a/tuple9_test.go b/tuple9_test.go index 042f46c..e565398 100644 --- a/tuple9_test.go +++ b/tuple9_test.go @@ -1,6 +1,7 @@ package tuple import ( + "encoding/json" "testing" "github.com/stretchr/testify/require" @@ -631,3 +632,94 @@ func TestT9_FromSlice(t *testing.T) { }) } } + +func TestT9_MarshalJSON(t *testing.T) { + tup := New9("1", "2", "3", "4", "5", "6", "7", "8", "9") + + got, err := json.Marshal(tup) + require.NoError(t, err) + require.Equal(t, got, []byte(`["1","2","3","4","5","6","7","8","9"]`)) +} + +func TestT9_UnmarshalJSON(t *testing.T) { + tests := []struct { + name string + data []byte + want T9[string, string, string, string, string, string, string, string, string] + wantErr bool + }{ + { + name: "nil data", + data: nil, + wantErr: true, + }, + { + name: "empty data", + data: []byte{}, + wantErr: true, + }, + { + name: "string data", + data: []byte(`"hi"`), + wantErr: true, + }, + { + name: "empty json array", + data: []byte(`[]`), + wantErr: true, + }, + { + name: "longer json array", + data: []byte(`["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]`), + wantErr: true, + }, + { + name: "json array of invalid types", + data: []byte(`[1,2,3,4,5,6,7,8,9]`), + wantErr: true, + }, + { + name: "json array with 1 invalid type", + data: []byte(`[1,"2","3","4","5","6","7","8","9"]`), + wantErr: true, + }, + { + name: "json array of valid types", + data: []byte(`["1","2","3","4","5","6","7","8","9"]`), + want: New9("1", "2", "3", "4", "5", "6", "7", "8", "9"), + wantErr: false, + }, + { + name: "json object of valid types", + data: []byte(`{"V1": "1","V2": "2","V3": "3","V4": "4","V5": "5","V6": "6","V7": "7","V8": "8","V9": "9"}`), + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var got T9[string, string, string, string, string, string, string, string, string] + err := json.Unmarshal(tt.data, &got) + if tt.wantErr { + require.Error(t, err) + return + } + + require.NoError(t, err) + require.Equal(t, tt.want, got) + }) + } +} + +func TestT9_Marshal_Unmarshal(t *testing.T) { + tup := New9("1", "2", "3", "4", "5", "6", "7", "8", "9") + + marshalled, err := json.Marshal(tup) + require.NoError(t, err) + + var unmarshalled T9[string, string, string, string, string, string, string, string, string] + err = json.Unmarshal(marshalled, &unmarshalled) + + require.NoError(t, err) + require.Equal(t, tup, unmarshalled) +}