Skip to content

Commit

Permalink
add to/from json though the sorting part is limited to one type at a …
Browse files Browse the repository at this point in the history
…time
  • Loading branch information
ldemailly committed Feb 23, 2023
1 parent 171cdb4 commit 1d4d490
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 0 deletions.
32 changes: 32 additions & 0 deletions sets.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
package sets // import "fortio.org/sets"

import (
"encoding/json"
"fmt"
"sort"
"strings"
Expand Down Expand Up @@ -173,6 +174,37 @@ func XOR[T comparable](a, b Set[T]) {
RemoveCommon(a, b)
}

// -- Serialization

// MarshalJSON implements the json.Marshaler interface and only gets the elements as an array.
func (s Set[T]) MarshalJSON() ([]byte, error) {
// How to handle all ordered at once??
switch v := any(s).(type) {
case Set[string]:
return json.Marshal(Sort(v))
case Set[int]:
return json.Marshal(Sort(v))
case Set[int8]:
return json.Marshal(Sort(v))
case Set[int64]:
return json.Marshal(Sort(v))
case Set[float64]:
return json.Marshal(Sort(v))
default:
return json.Marshal(s.Elements())
}
}

// UnmarshalJSON implements the json.Unmarshaler interface turns the slice back to a Set.
func (s *Set[T]) UnmarshalJSON(data []byte) error {
var items []T
if err := json.Unmarshal(data, &items); err != nil {
return err
}
*s = New[T](items...)
return nil
}

// -- Additional operations on sets of ordered types

func Sort[Q constraints.Ordered](s Set[Q]) []Q {
Expand Down
70 changes: 70 additions & 0 deletions sets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package sets_test

import (
"encoding/json"
"testing"

"fortio.org/assert"
Expand Down Expand Up @@ -101,3 +102,72 @@ func TestSubset(t *testing.T) {
assert.False(t, setA.Subset(setB))
assert.False(t, setB.Subset(setA))
}

func TestJSON(t *testing.T) {
setA := sets.New("c,d", "a b", "y\000z", "mno")
b, err := json.Marshal(setA)
assert.NoError(t, err)
assert.Equal(t, `["a b","c,d","mno","y\u0000z"]`, string(b))
jsonStr := `[
"a,b",
"c,d"
]`
setB := sets.New[string]()
err = json.Unmarshal([]byte(jsonStr), &setB)
assert.NoError(t, err)
assert.Equal(t, setB.Len(), 2)
assert.True(t, setB.Has("a,b"))
assert.True(t, setB.Has("c,d"))
setI := sets.New(3, 42, 7, 10)
b, err = json.Marshal(setI)
assert.NoError(t, err)
assert.Equal(t, `[3,7,10,42]`, string(b))
smallIntSet := sets.New[int8](66, 65, 67) // if using byte, aka uint8, one gets base64("ABC")
b, err = json.Marshal(smallIntSet)
assert.NoError(t, err)
t.Logf("smallIntSet: %q", string(b))
assert.Equal(t, `[65,66,67]`, string(b))
floatSet := sets.New[float64](2.3, 1.1, -7.6, 42)
b, err = json.Marshal(floatSet)
assert.NoError(t, err)
t.Logf("floatSet: %q", string(b))
assert.Equal(t, `[-7.6,1.1,2.3,42]`, string(b))
i64Set := sets.New[int64](2, 1, -7, 42)
b, err = json.Marshal(i64Set)
assert.NoError(t, err)
t.Logf("i64Set: %q", string(b))
assert.Equal(t, `[-7,1,2,42]`, string(b))
}

type foo struct {
X int
}

func TestNonOrderedJSON(t *testing.T) {
s := sets.New(
foo{3},
foo{1},
foo{2},
foo{4},
)
b, err := json.Marshal(s)
t.Logf("b: %s", string(b))
assert.NoError(t, err)
// though I guess given it could be in any order it could be accidentally sorted too
assert.NotEqual(t, `[{"X":1},{"X":2},{"X":3},{"X":4}]`, string(b))
u := sets.New[foo]()
json.Unmarshal(b, &u)
assert.NoError(t, err)
assert.Equal(t, 4, u.Len())
assert.True(t, s.Equals(u))
}

func TestBadJson(t *testing.T) {
jsonStr := `[
"a,b",
"c,d"
]`
s := sets.New[int]()
err := json.Unmarshal([]byte(jsonStr), &s)
assert.Error(t, err)
}

0 comments on commit 1d4d490

Please sign in to comment.