Skip to content

Commit

Permalink
feat: add nullable bool defaulting to false (#758)
Browse files Browse the repository at this point in the history
  • Loading branch information
aeneasr authored Jan 30, 2024
1 parent ee6571a commit 0cdec1a
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 0 deletions.
50 changes: 50 additions & 0 deletions sqlxx/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,56 @@ func (ns *NullBool) UnmarshalJSON(data []byte) error {
return errors.WithStack(json.Unmarshal(data, &ns.Bool))
}

// FalsyNullBool represents a bool that may be null.
// It JSON decodes to false if null.
//
// swagger:type bool
// swagger:model falsyNullBool
type FalsyNullBool struct {
Bool bool
Valid bool // Valid is true if Bool is not NULL
}

// Scan implements the Scanner interface.
func (ns *FalsyNullBool) Scan(value interface{}) error {
var d = sql.NullBool{}
if err := d.Scan(value); err != nil {
return err
}

ns.Bool = d.Bool
ns.Valid = d.Valid
return nil
}

// Value implements the driver Valuer interface.
func (ns FalsyNullBool) Value() (driver.Value, error) {
if !ns.Valid {
return nil, nil
}
return ns.Bool, nil
}

// MarshalJSON returns m as the JSON encoding of m.
func (ns FalsyNullBool) MarshalJSON() ([]byte, error) {
if !ns.Valid {
return []byte("false"), nil
}
return json.Marshal(ns.Bool)
}

// UnmarshalJSON sets *m to a copy of data.
func (ns *FalsyNullBool) UnmarshalJSON(data []byte) error {
if ns == nil {
return errors.New("json.RawMessage: UnmarshalJSON on nil pointer")
}
if len(data) == 0 || string(data) == "null" {
return nil
}
ns.Valid = true
return errors.WithStack(json.Unmarshal(data, &ns.Bool))
}

// swagger:type string
// swagger:model nullString
type NullString string
Expand Down
36 changes: 36 additions & 0 deletions sqlxx/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,42 @@ func TestNullBoolMarshalJSON(t *testing.T) {
}
}

func TestNullBoolDefaultFalseMarshalJSON(t *testing.T) {
type outer struct {
Bool *FalsyNullBool `json:"null_bool,omitempty"`
}

for k, tc := range []struct {
in *outer
expected string
}{
{in: &outer{&FalsyNullBool{Valid: false, Bool: true}}, expected: "{\"null_bool\":false}"},
{in: &outer{&FalsyNullBool{Valid: false, Bool: false}}, expected: "{\"null_bool\":false}"},
{in: &outer{&FalsyNullBool{Valid: true, Bool: true}}, expected: "{\"null_bool\":true}"},
{in: &outer{&FalsyNullBool{Valid: true, Bool: false}}, expected: "{\"null_bool\":false}"},
{in: &outer{}, expected: "{}"},
} {
t.Run(fmt.Sprintf("case=%d", k), func(t *testing.T) {
out, err := json.Marshal(tc.in)
require.NoError(t, err)
assert.EqualValues(t, tc.expected, string(out))

var actual outer
require.NoError(t, json.Unmarshal(out, &actual))
if tc.in.Bool == nil {
assert.Nil(t, actual.Bool)
return
} else if !tc.in.Bool.Valid {
assert.False(t, actual.Bool.Bool)
return
}

assert.EqualValues(t, tc.in.Bool.Bool, actual.Bool.Bool)
assert.EqualValues(t, tc.in.Bool.Valid, actual.Bool.Valid)
})
}
}

func TestNullInt64MarshalJSON(t *testing.T) {
type outer struct {
Int64 *NullInt64 `json:"null_int,omitempty"`
Expand Down

0 comments on commit 0cdec1a

Please sign in to comment.