Skip to content

Commit b0733dd

Browse files
committed
WIP
1 parent 5996204 commit b0733dd

File tree

2 files changed

+354
-2
lines changed

2 files changed

+354
-2
lines changed

types/nullable.go

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,49 @@ import (
66
"fmt"
77
)
88

9+
type OptionalNullable[T any] struct {
10+
Value T
11+
// Set indicates whether the underlying Value was sent in the request TODO
12+
Set bool
13+
// Null indicates whether the underlying Value was sent in the request as a literal `null` value. Should only be checked if Set==true TODO
14+
Null bool
15+
}
16+
17+
// UnmarshalJSON implements the Unmarshaler interface.
18+
func (t *OptionalNullable[T]) UnmarshalJSON(data []byte) error {
19+
t.Set = true
20+
if bytes.Equal(data, []byte("null")) {
21+
t.Null = true
22+
return nil
23+
}
24+
if err := json.Unmarshal(data, &t.Value); err != nil {
25+
return fmt.Errorf("couldn't unmarshal JSON: %w", err)
26+
}
27+
t.Null = false
28+
return nil
29+
}
30+
31+
// MarshalJSON implements the Marshaler interface.
32+
func (t OptionalNullable[T]) MarshalJSON() ([]byte, error) {
33+
if t.Set && t.Null {
34+
return []byte("null"), nil
35+
}
36+
return json.Marshal(t.Value)
37+
}
38+
939
// Nullable type which can help distinguish between if a value was explicitly
1040
// provided `null` in JSON or not
1141
type Nullable[T any] struct {
1242
Value T
13-
Set bool
14-
Null bool
43+
// Set indicates whether the underlying Value was sent in the request TODO
44+
Set bool
45+
// Null indicates whether the underlying Value was sent in the request as a literal `null` value. Should only be checked if Set==true TODO
46+
Null bool
1547
}
1648

1749
// UnmarshalJSON implements the Unmarshaler interface.
1850
func (t *Nullable[T]) UnmarshalJSON(data []byte) error {
51+
// fmt.Println("UnmarshalJSON")
1952
t.Set = true
2053
if bytes.Equal(data, []byte("null")) {
2154
t.Null = true

types/nullable_examples_test.go

Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
package types_test
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
7+
"github.com/oapi-codegen/runtime/types"
8+
)
9+
10+
func ExampleNullable_marshalRequired() {
11+
obj := struct {
12+
ID types.Nullable[int] `json:"id"`
13+
}{}
14+
15+
// when it's not set
16+
b, err := json.Marshal(obj)
17+
if err != nil {
18+
fmt.Printf("Error: %v\n", err)
19+
return
20+
}
21+
fmt.Printf(`JSON: %s`+"\n", b)
22+
fmt.Println("---")
23+
24+
// when it's set explicitly to nil
25+
obj.ID.Value = 0
26+
obj.ID.Null = true
27+
obj.ID.Set = true
28+
29+
b, err = json.Marshal(obj)
30+
if err != nil {
31+
fmt.Printf("Error: %v\n", err)
32+
return
33+
}
34+
fmt.Printf(`JSON: %s`+"\n", b)
35+
fmt.Println("---")
36+
37+
// when it's set explicitly to the zero value
38+
var v int
39+
obj.ID.Value = v
40+
obj.ID.Set = true
41+
42+
b, err = json.Marshal(obj)
43+
if err != nil {
44+
fmt.Printf("Error: %v\n", err)
45+
return
46+
}
47+
fmt.Printf(`JSON: %s`+"\n", b)
48+
fmt.Println("---")
49+
50+
// when it's set explicitly to a specific value
51+
v = 12345
52+
obj.ID.Value = v
53+
obj.ID.Set = true
54+
55+
b, err = json.Marshal(obj)
56+
if err != nil {
57+
fmt.Printf("Error: %v\n", err)
58+
return
59+
}
60+
fmt.Printf(`JSON: %s`+"\n", b)
61+
fmt.Println("---")
62+
63+
// Output:
64+
// JSON: {}
65+
// ---
66+
// JSON: {"id":null}
67+
// ---
68+
// JSON: {"id":0}
69+
// ---
70+
// JSON: {"id":12345}
71+
// ---
72+
}
73+
74+
func ExampleNullable_marshalOptional() {
75+
obj := struct {
76+
ID *types.Nullable[int] `json:"id"`
77+
}{}
78+
79+
// when it's not set
80+
b, err := json.Marshal(obj)
81+
if err != nil {
82+
fmt.Printf("Error: %v\n", err)
83+
return
84+
}
85+
fmt.Printf(`JSON: %s`+"\n", b)
86+
fmt.Println("---")
87+
88+
// when it's set explicitly to nil
89+
obj.ID = &types.Nullable[int]{
90+
Value: 0,
91+
Null: true,
92+
Set: true,
93+
}
94+
95+
b, err = json.Marshal(obj)
96+
if err != nil {
97+
fmt.Printf("Error: %v\n", err)
98+
return
99+
}
100+
fmt.Printf(`JSON: %s`+"\n", b)
101+
fmt.Println("---")
102+
103+
// when it's set explicitly to the zero value
104+
var v int
105+
obj.ID = &types.Nullable[int]{
106+
Value: v,
107+
Set: true,
108+
}
109+
110+
b, err = json.Marshal(obj)
111+
if err != nil {
112+
fmt.Printf("Error: %v\n", err)
113+
return
114+
}
115+
fmt.Printf(`JSON: %s`+"\n", b)
116+
fmt.Println("---")
117+
118+
// when it's set explicitly to a specific value
119+
v = 12345
120+
obj.ID = &types.Nullable[int]{
121+
Value: v,
122+
Set: true,
123+
}
124+
125+
b, err = json.Marshal(obj)
126+
if err != nil {
127+
fmt.Printf("Error: %v\n", err)
128+
return
129+
}
130+
fmt.Printf(`JSON: %s`+"\n", b)
131+
fmt.Println("---")
132+
133+
// Output:
134+
// JSON: {}
135+
// ---
136+
// JSON: {"id":null}
137+
// ---
138+
// JSON: {"id":0}
139+
// ---
140+
// JSON: {"id":12345}
141+
// ---
142+
}
143+
144+
func ExampleNullable_unmarshalRequired() {
145+
obj := struct {
146+
Name types.Nullable[string] `json:"name"`
147+
}{}
148+
149+
// when it's not set
150+
err := json.Unmarshal([]byte(`
151+
{
152+
}
153+
`), &obj)
154+
if err != nil {
155+
fmt.Printf("Error: %v\n", err)
156+
return
157+
}
158+
fmt.Printf("obj.Name.Set: %v\n", obj.Name.Set)
159+
fmt.Printf("obj.Name.Null: %v\n", obj.Name.Null)
160+
fmt.Printf("obj.Name.Value: %#v\n", obj.Name.Value)
161+
fmt.Println("---")
162+
163+
// when it's set explicitly to nil
164+
err = json.Unmarshal([]byte(`
165+
{
166+
"name": null
167+
}
168+
`), &obj)
169+
if err != nil {
170+
fmt.Printf("Error: %v\n", err)
171+
return
172+
}
173+
fmt.Printf("obj.Name.Set: %v\n", obj.Name.Set)
174+
fmt.Printf("obj.Name.Null: %v\n", obj.Name.Null)
175+
fmt.Printf("obj.Name.Value: %#v\n", obj.Name.Value)
176+
fmt.Println("---")
177+
178+
// when it's set explicitly to the zero value
179+
err = json.Unmarshal([]byte(`
180+
{
181+
"name": ""
182+
}
183+
`), &obj)
184+
if err != nil {
185+
fmt.Printf("Error: %v\n", err)
186+
return
187+
}
188+
fmt.Printf("obj.Name.Set: %v\n", obj.Name.Set)
189+
fmt.Printf("obj.Name.Null: %v\n", obj.Name.Null)
190+
fmt.Printf("obj.Name.Value: %#v\n", obj.Name.Value)
191+
// if obj.Name.Value == nil {
192+
// fmt.Println("Error: expected obj.Name.Value to have a value, but was <nil>")
193+
// return
194+
// }
195+
// fmt.Printf("obj.Name.Value: %#v\n", *obj.Name.Value)
196+
fmt.Println("---")
197+
198+
// when it's set explicitly to a specific value
199+
err = json.Unmarshal([]byte(`
200+
{
201+
"name": "foo"
202+
}
203+
`), &obj)
204+
if err != nil {
205+
fmt.Printf("Error: %v\n", err)
206+
return
207+
}
208+
fmt.Printf("obj.Name.Set: %v\n", obj.Name.Set)
209+
fmt.Printf("obj.Name.Null: %v\n", obj.Name.Null)
210+
fmt.Printf("obj.Name.Value: %#v\n", obj.Name.Value)
211+
// fmt.Printf("obj.Name.Value: %v\n", obj.Name.Value)
212+
// if obj.Name.Value == nil {
213+
// fmt.Println("Error: expected obj.Name.Value to have a value, but was <nil>")
214+
// return
215+
// }
216+
// fmt.Printf("obj.Name.Value: %#v\n", *obj.Name.Value)
217+
fmt.Println("---")
218+
219+
// Output:
220+
// obj.Name.Set: false
221+
// obj.Name.Null: false
222+
// obj.Name.Value: ""
223+
// ---
224+
// obj.Name.Set: true
225+
// obj.Name.Null: true
226+
// obj.Name.Value: ""
227+
// ---
228+
// obj.Name.Set: true
229+
// obj.Name.Null: false
230+
// obj.Name.Value: ""
231+
// ---
232+
// obj.Name.Set: true
233+
// obj.Name.Null: false
234+
// obj.Name.Value: "foo"
235+
// ---
236+
}
237+
238+
func ExampleNullable_unmarshalOptional() {
239+
obj := struct {
240+
Name types.OptionalNullable[string] `json:"name"`
241+
}{}
242+
243+
// when it's not set
244+
err := json.Unmarshal([]byte(`
245+
{
246+
}
247+
`), &obj)
248+
if err != nil {
249+
fmt.Printf("Error: %v\n", err)
250+
return
251+
}
252+
fmt.Printf("obj.Name.Set: %v\n", obj.Name.Set)
253+
fmt.Printf("obj.Name.Null: %v\n", obj.Name.Null)
254+
fmt.Printf("obj.Name.Value: %#v\n", obj.Name.Value)
255+
fmt.Println("---")
256+
257+
// when it's set explicitly to nil
258+
err = json.Unmarshal([]byte(`
259+
{
260+
"name": null
261+
}
262+
`), &obj)
263+
if err != nil {
264+
fmt.Printf("Error: %v\n", err)
265+
return
266+
}
267+
fmt.Printf("obj.Name.Set: %v\n", obj.Name.Set)
268+
fmt.Printf("obj.Name.Null: %v\n", obj.Name.Null)
269+
fmt.Printf("obj.Name.Value: %#v\n", obj.Name.Value)
270+
fmt.Println("---")
271+
272+
// when it's set explicitly to the zero value
273+
err = json.Unmarshal([]byte(`
274+
{
275+
"name": ""
276+
}
277+
`), &obj)
278+
if err != nil {
279+
fmt.Printf("Error: %v\n", err)
280+
return
281+
}
282+
fmt.Printf("obj.Name.Set: %v\n", obj.Name.Set)
283+
fmt.Printf("obj.Name.Null: %v\n", obj.Name.Null)
284+
fmt.Printf("obj.Name.Value: %#v\n", obj.Name.Value)
285+
fmt.Println("---")
286+
287+
// when it's set explicitly to a specific value
288+
err = json.Unmarshal([]byte(`
289+
{
290+
"name": "foo"
291+
}
292+
`), &obj)
293+
if err != nil {
294+
fmt.Printf("Error: %v\n", err)
295+
return
296+
}
297+
fmt.Printf("obj.Name.Set: %v\n", obj.Name.Set)
298+
fmt.Printf("obj.Name.Null: %v\n", obj.Name.Null)
299+
fmt.Printf("obj.Name.Value: %#v\n", obj.Name.Value)
300+
fmt.Println("---")
301+
302+
// Output:
303+
// obj.Name.Set: false
304+
// obj.Name.Null: false
305+
// obj.Name.Value: ""
306+
// ---
307+
// obj.Name.Set: true
308+
// obj.Name.Null: true
309+
// obj.Name.Value: ""
310+
// ---
311+
// obj.Name.Set: true
312+
// obj.Name.Null: false
313+
// obj.Name.Value: ""
314+
// ---
315+
// obj.Name.Set: true
316+
// obj.Name.Null: false
317+
// obj.Name.Value: "foo"
318+
// ---
319+
}

0 commit comments

Comments
 (0)