Skip to content

Commit ba95a2b

Browse files
committed
WIP
1 parent 0d2a587 commit ba95a2b

File tree

4 files changed

+131
-2
lines changed

4 files changed

+131
-2
lines changed

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
module github.com/oapi-codegen/nullable
22

33
go 1.20
4+
5+
require gopkg.in/yaml.v3 v3.0.1

internal/test/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ replace github.com/oapi-codegen/nullable => ../../
77
require (
88
github.com/oapi-codegen/nullable v0.0.0-00010101000000-000000000000
99
github.com/stretchr/testify v1.8.4
10+
gopkg.in/yaml.v3 v3.0.1
1011
)
1112

1213
require (
1314
github.com/davecgh/go-spew v1.1.1 // indirect
1415
github.com/pmezard/go-difflib v1.0.0 // indirect
15-
gopkg.in/yaml.v3 v3.0.1 // indirect
1616
)

internal/test/nullable_test.go

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ import (
55
"testing"
66

77
"github.com/oapi-codegen/nullable"
8+
"gopkg.in/yaml.v3"
89

910
"github.com/stretchr/testify/require"
1011
)
1112

1213
type Obj struct {
13-
Foo nullable.Nullable[string] `json:"foo,omitempty"` // note "omitempty" is important for fields that are optional
14+
Foo nullable.Nullable[string] `json:"foo,omitempty",yaml:"foo,omitempty"` // note "omitempty" is important for fields that are optional
1415
}
1516

1617
func TestNullable(t *testing.T) {
@@ -88,3 +89,79 @@ func serialize(o Obj, t *testing.T) string {
8889
require.NoError(t, err)
8990
return string(data)
9091
}
92+
93+
func TestNullableYAML(t *testing.T) {
94+
// --- parsing from json and serializing back to JSON
95+
96+
// -- case where there is an actual value
97+
data := `foo: bar`
98+
// deserialize from json
99+
myObj := parseYAML(data, t)
100+
require.Equal(t, myObj, Obj{Foo: nullable.Nullable[string]{true: "bar"}})
101+
require.False(t, myObj.Foo.IsNull())
102+
require.True(t, myObj.Foo.IsSpecified())
103+
value, err := myObj.Foo.Get()
104+
require.NoError(t, err)
105+
require.Equal(t, "bar", value)
106+
require.Equal(t, "bar", myObj.Foo.MustGet())
107+
// serialize back to json: leads to the same data
108+
require.Equal(t, data, serializeYAML(myObj, t))
109+
110+
// -- case where no value is specified: parsed from JSON
111+
data = ``
112+
// deserialize from json
113+
myObj = parseYAML(data, t)
114+
require.Equal(t, myObj, Obj{Foo: nil})
115+
require.False(t, myObj.Foo.IsNull())
116+
require.False(t, myObj.Foo.IsSpecified())
117+
_, err = myObj.Foo.Get()
118+
require.ErrorContains(t, err, "value is not specified")
119+
// serialize back to json: leads to the same data
120+
require.Equal(t, data, serializeYAML(myObj, t))
121+
122+
// -- case where the specified value is explicitly null
123+
data = `foo:null`
124+
// deserialize from json
125+
myObj = parseYAML(data, t)
126+
require.Equal(t, myObj, Obj{Foo: nullable.Nullable[string]{false: ""}})
127+
require.True(t, myObj.Foo.IsNull())
128+
require.True(t, myObj.Foo.IsSpecified())
129+
_, err = myObj.Foo.Get()
130+
require.ErrorContains(t, err, "value is null")
131+
require.Panics(t, func() { myObj.Foo.MustGet() })
132+
// serialize back to json: leads to the same data
133+
require.Equal(t, data, serializeYAML(myObj, t))
134+
135+
// --- building objects from a Go client
136+
137+
// - case where there is an actual value
138+
myObj = Obj{}
139+
myObj.Foo.Set("bar")
140+
require.Equal(t, `foo:"bar"`, serialize(myObj, t))
141+
142+
// - case where the value should be unspecified
143+
myObj = Obj{}
144+
// do nothing: unspecified by default
145+
require.Equal(t, ``, serializeYAML(myObj, t))
146+
// explicitly mark unspecified
147+
myObj.Foo.SetUnspecified()
148+
require.Equal(t, ``, serializeYAML(myObj, t))
149+
150+
// - case where the value should be null
151+
myObj = Obj{}
152+
myObj.Foo.SetNull()
153+
require.Equal(t, `foo:null`, serialize(myObj, t))
154+
}
155+
156+
func parseYAML(data string, t *testing.T) Obj {
157+
var myObj Obj
158+
err := yaml.Unmarshal([]byte(data), &myObj)
159+
require.NoError(t, err)
160+
return myObj
161+
}
162+
163+
func serializeYAML(o Obj, t *testing.T) string {
164+
data, err := yaml.Marshal(o)
165+
require.NoError(t, err)
166+
return string(data)
167+
}

nullable.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ import (
44
"bytes"
55
"encoding/json"
66
"errors"
7+
"fmt"
8+
"reflect"
9+
10+
"gopkg.in/yaml.v3"
711
)
812

913
// Nullable is a generic type, which implements a field that can be one of three states:
@@ -115,3 +119,49 @@ func (t *Nullable[T]) UnmarshalJSON(data []byte) error {
115119
t.Set(v)
116120
return nil
117121
}
122+
123+
// TODO pointer receiver https://github.com/go-yaml/yaml/issues/134#issuecomment-2044424851
124+
func (t Nullable[T]) MarshalYAML() (interface{}, error) {
125+
fmt.Println("MarshalYAML")
126+
// if field was specified, and `null`, marshal it
127+
if t.IsNull() {
128+
return []byte("null"), nil
129+
}
130+
131+
// if field was unspecified, and `omitempty` is set on the field's tags, `json.Marshal` will omit this field
132+
133+
// otherwise: we have a value, so marshal it
134+
// fmt.Printf("t[true]: %v\n", t[true])
135+
// b, _ := yaml.Marshal(t[true])
136+
// fmt.Printf("b: %v\n", b)
137+
// return yaml.Marshal(t[true])
138+
vv := (t)[true]
139+
fmt.Printf("vv: %v\n", vv)
140+
fmt.Printf("reflect.ValueOf(vv): %v\n", reflect.ValueOf(vv))
141+
return json.Marshal(t[true])
142+
}
143+
144+
func (t *Nullable[T]) UnmarshalYAML(value *yaml.Node) error {
145+
// if field is unspecified, UnmarshalJSON won't be called
146+
// fmt.Printf("value: %v\n", value)
147+
// value.Kind == yaml.Kind
148+
149+
fmt.Printf("value: %v\n", value)
150+
fmt.Printf("value.Tag: %v\n", value.Tag)
151+
152+
////// // if field is specified, and `null`
153+
////// if bytes.Equal(data, []byte("null")) {
154+
////// t.SetNull()
155+
////// return nil
156+
////// }
157+
// otherwise, we have an actual value, so parse it
158+
var v T
159+
160+
fmt.Printf("reflect.TypeOf(v): %v\n", reflect.TypeOf(v))
161+
if err := value.Decode(&v); err != nil {
162+
return err
163+
}
164+
fmt.Printf("v: %v\n", v)
165+
t.Set(v)
166+
return nil
167+
}

0 commit comments

Comments
 (0)