Skip to content

Commit

Permalink
Fix optional fields.
Browse files Browse the repository at this point in the history
  • Loading branch information
q-uint committed Aug 7, 2024
1 parent 2a82c75 commit ebb81d4
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 28 deletions.
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ Given the following JSON data dump:
{
"users": [
{
"name": "Alice"
"first_name": "Alice",
"username": "alice",
"age": "23"
},
{
"name": "Bob",
"username": "bob",
"age": 42
}
]
Expand Down Expand Up @@ -48,12 +50,11 @@ Output:
```go
package users

import "encoding/json"

type Users struct {
Users []struct {
Age json.Number `json:"age"`
Name string `json:"name"`
Age any /* json.Number, string */ `json:"age"`
FirstName *string `json:"first_name"`
Username string `json:"username"`
} `json:"users"`
}

Expand Down
4 changes: 1 addition & 3 deletions array.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ func (a *Array) Combine(n Node) (Node, error) {
}, nil
}
}
return &Or{
Types: []Node{a, n},
}, nil
return NewOr([]Node{a, n}), nil
}

func (a *Array) Equals(n Node) bool {
Expand Down
2 changes: 1 addition & 1 deletion fmt/gofmt/fmt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func ExampleNodeToGo() {
// type Users struct {
// Users []struct {
// Age any /* json.Number, string */ `json:"age"`
// FirstName string `json:"first_name"`
// FirstName *string `json:"first_name"`
// Username string `json:"username"`
// } `json:"users"`
// }
Expand Down
4 changes: 1 addition & 3 deletions optional.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ func (o *Optional) Combine(n Node) (Node, error) {
}, nil
}
}
return &Or{
Types: []Node{o, n},
}, nil
return NewOr([]Node{o, n}), nil
}

func (o *Optional) Equals(n Node) bool {
Expand Down
26 changes: 17 additions & 9 deletions or.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
package constructor

import (
"slices"
"strings"
)

type Or struct {
Types []Node
}

func NewOr(types []Node) *Or {
slices.SortFunc(types, func(a, b Node) int {
return strings.Compare(a.String(), b.String())
})
return &Or{
Types: types,
}
}

func (o *Or) Combine(n Node) (Node, error) {
switch t := n.(type) {
case *Or:
Expand All @@ -18,9 +32,7 @@ func (o *Or) Combine(n Node) (Node, error) {
}
u = append(u, v)
}
return &Or{
Types: u,
}, nil
return NewOr(u), nil
default:
for i, v := range o.Types {
if v.Equals(n) {
Expand All @@ -30,17 +42,13 @@ func (o *Or) Combine(n Node) (Node, error) {
u := make([]Node, len(o.Types))
copy(u, o.Types)
u[i] = n
return &Or{
Types: u,
}, nil
return NewOr(u), nil
}
}
u := make([]Node, len(o.Types)+1)
copy(u, o.Types)
u[len(o.Types)] = n
return &Or{
Types: u,
}, nil
return NewOr(u), nil
}
}

Expand Down
6 changes: 3 additions & 3 deletions or_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
)

func TestOr_Combine(t *testing.T) {
or := &Or{Types: []Node{new(Any)}}
or := NewOr([]Node{new(Any)})
or1, err := or.Combine(new(Any))
if err != nil {
t.Fatal(err)
Expand All @@ -14,7 +14,7 @@ func TestOr_Combine(t *testing.T) {
if err != nil {
t.Fatal(err)
}
orString := &Or{Types: []Node{new(String)}}
orString := NewOr([]Node{new(String)})
if !or2.Equals(orString) {
t.Errorf("expected %v, got %v", orString, or2)
}
Expand All @@ -29,7 +29,7 @@ func TestOr_Combine(t *testing.T) {
if err != nil {
t.Fatal(err)
}
orArray := &Or{Types: []Node{new(String), &Array{Type: new(String)}}}
orArray := &Or{Types: []Node{&Array{Type: new(String)}, new(String)}}
if !or4.Equals(orArray) {
t.Errorf("expected %v, got %v", orArray, or4)
}
Expand Down
10 changes: 7 additions & 3 deletions struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,21 @@ func (s *Struct) Combine(n Node) (Node, error) {
case *Struct:
types := make(map[string]Node)
for k, v := range s.Types {
types[k] = v
if _, ok := t.Types[k]; ok {
types[k] = v
} else {
types[k] = &Optional{Type: v}
}
}
for k, v := range t.Types {
if v2, ok := types[k]; ok {
if n, err := v2.Combine(v); err == nil {
types[k] = n
} else {
types[k] = &Or{Types: []Node{v, v2}}
types[k] = NewOr([]Node{v, v2})
}
} else {
types[k] = v
types[k] = &Optional{Type: v}
}
}
return NewStruct(types), nil
Expand Down
36 changes: 36 additions & 0 deletions struct_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package constructor

import "testing"

func TestStruct_Combine(t *testing.T) {
t.Run("complex", func(t *testing.T) {
left := NewStruct(map[string]Node{
"first_name": new(String),
"username": new(String),
"age": new(String),
})
right := NewStruct(map[string]Node{
"username": new(String),
"age": new(Number),
})
expected := NewStruct(map[string]Node{
"first_name": &Optional{Type: new(String)},
"username": new(String),
"age": NewOr([]Node{new(Number), new(String)}),
})
actual, err := left.Combine(right)
if err != nil {
t.Fatal(err)
}
if !expected.Equals(actual) {
t.Errorf("expected %v, got %v", expected, actual)
}
inverse, err := right.Combine(left)
if err != nil {
t.Fatal(err)
}
if !expected.Equals(inverse) {
t.Errorf("expected %v, got %v", expected, inverse)
}
})
}

0 comments on commit ebb81d4

Please sign in to comment.