Skip to content

Instantly share code, notes, and snippets.

@mniak
Last active April 20, 2022 18:58
Show Gist options
  • Save mniak/4364d173e06bbbba5aaae8f24cf94571 to your computer and use it in GitHub Desktop.
Save mniak/4364d173e06bbbba5aaae8f24cf94571 to your computer and use it in GitHub Desktop.
package utils
import "encoding/json"
type Optional[T any] struct {
explicit bool
hasValue bool
value T
}
func (o *Optional[T]) UnmarshalJSON(b []byte) error {
if len(b) == 0 {
return nil
}
o.explicit = true
if string(b) == "null" {
return nil
}
o.hasValue = true
return json.Unmarshal(b, &o.value)
}
func (o *Optional[T]) Explicit() bool {
return o.explicit
}
func (o *Optional[T]) Value() *T {
if !o.explicit || !o.hasValue {
return nil
}
return &o.value
}
package utils
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var (
_ json.Unmarshaler = &Optional[int]{}
_ json.Unmarshaler = &Optional[string]{}
)
type WithOptionals struct {
IntField1 Optional[int]
IntField2 Optional[int] `json:"int_field_2"`
}
func TestOptional(t *testing.T) {
t.Run("Default should be implicit nulls", func(t *testing.T) {
var data WithOptionals
assert.False(t, data.IntField1.Explicit())
assert.Nil(t, data.IntField1.Value())
assert.False(t, data.IntField2.Explicit())
assert.Nil(t, data.IntField2.Value())
})
}
func TestOptional_Unmarshal(t *testing.T) {
t.Run("Implicit null", func(t *testing.T) {
var data WithOptionals
const text = `{}`
err := json.Unmarshal([]byte(text), &data)
require.NoError(t, err)
assert.False(t, data.IntField1.Explicit())
assert.Nil(t, data.IntField1.Value())
assert.False(t, data.IntField2.Explicit())
assert.Nil(t, data.IntField2.Value())
})
t.Run("Explicit nulls", func(t *testing.T) {
var data WithOptionals
const text = `{
"IntField1": null,
"int_field_2": null
}`
err := json.Unmarshal([]byte(text), &data)
require.NoError(t, err)
assert.True(t, data.IntField1.Explicit())
assert.Nil(t, data.IntField1.Value())
assert.True(t, data.IntField2.Explicit())
assert.Nil(t, data.IntField2.Value())
})
t.Run(`Explicit string "null" should fail`, func(t *testing.T) {
var data WithOptionals
const text = `{
"IntField1": "null",
"int_field_2": "null"
}`
err := json.Unmarshal([]byte(text), &data)
require.Error(t, err)
})
t.Run("Happy scenario", func(t *testing.T) {
var data WithOptionals
const text = `{
"IntField1": 123,
"int_field_2": 456
}`
err := json.Unmarshal([]byte(text), &data)
require.NoError(t, err)
assert.True(t, data.IntField1.Explicit())
assert.Equal(t, 123, *data.IntField1.Value())
assert.True(t, data.IntField2.Explicit())
assert.Equal(t, 456, *data.IntField2.Value())
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment