diff --git a/bool.go b/bool.go index 1ec1396..41627a3 100644 --- a/bool.go +++ b/bool.go @@ -1,27 +1,27 @@ package null import ( - "database/sql" + "bytes" + "database/sql/driver" "encoding/json" "errors" - "fmt" - "reflect" + + "github.com/nullbio/null/convert" ) // Bool is a nullable bool. // It does not consider false values to be null. // It will decode to null, not false, if null. type Bool struct { - sql.NullBool + Bool bool + Valid bool } // NewBool creates a new Bool func NewBool(b bool, valid bool) Bool { return Bool{ - NullBool: sql.NullBool{ - Bool: b, - Valid: valid, - }, + Bool: b, + Valid: valid, } } @@ -43,24 +43,18 @@ func BoolFromPtr(b *bool) Bool { // 0 will not be considered a null Bool. // It also supports unmarshalling a sql.NullBool. func (b *Bool) UnmarshalJSON(data []byte) error { - var err error - var v interface{} - if err = json.Unmarshal(data, &v); err != nil { - return err - } - switch x := v.(type) { - case bool: - b.Bool = x - case map[string]interface{}: - err = json.Unmarshal(data, &b.NullBool) - case nil: + if bytes.Equal(data, NullBytes) { + b.Bool = false b.Valid = false return nil - default: - err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Bool", reflect.TypeOf(v).Name()) } - b.Valid = err == nil - return err + + if err := json.Unmarshal(data, &b.Bool); err != nil { + return err + } + + b.Valid = true + return nil } // UnmarshalText implements encoding.TextUnmarshaler. @@ -127,3 +121,21 @@ func (b Bool) Ptr() *bool { func (b Bool) IsZero() bool { return !b.Valid } + +// Scan implements the Scanner interface. +func (b *Bool) Scan(value interface{}) error { + if value == nil { + b.Bool, b.Valid = false, false + return nil + } + b.Valid = true + return convert.ConvertAssign(&b.Bool, value) +} + +// Value implements the driver Valuer interface. +func (b Bool) Value() (driver.Value, error) { + if !b.Valid { + return nil, nil + } + return b.Bool, nil +} diff --git a/bool_test.go b/bool_test.go index 1dc992a..fad9e9d 100644 --- a/bool_test.go +++ b/bool_test.go @@ -39,8 +39,9 @@ func TestUnmarshalBool(t *testing.T) { var nb Bool err = json.Unmarshal(nullBoolJSON, &nb) - maybePanic(err) - assertBool(t, nb, "sq.NullBool json") + if err == nil { + panic("err should not be nil") + } var null Bool err = json.Unmarshal(nullJSON, &null) diff --git a/bytes.go b/bytes.go index f8f4ca7..887d590 100644 --- a/bytes.go +++ b/bytes.go @@ -1,31 +1,27 @@ package null import ( + "bytes" "database/sql/driver" + "encoding/json" "gopkg.in/nullbio/null.v5/convert" ) -// NullBytes is a nullable byte slice. -type NullBytes struct { - Bytes []byte - Valid bool -} +// NullBytes is a global byte slice of JSON null +var NullBytes = []byte("null") // Bytes is a nullable []byte. -// JSON marshals to zero if null. -// Considered null to SQL if zero. type Bytes struct { - NullBytes + Bytes []byte + Valid bool } // NewBytes creates a new Bytes func NewBytes(b []byte, valid bool) Bytes { return Bytes{ - NullBytes: NullBytes{ - Bytes: b, - Valid: valid, - }, + Bytes: b, + Valid: valid, } } @@ -47,14 +43,19 @@ func BytesFromPtr(b *[]byte) Bytes { // If data is len 0 or nil, it will unmarshal to JSON null. // If not, it will copy your data slice into Bytes. func (b *Bytes) UnmarshalJSON(data []byte) error { - if data == nil || len(data) == 0 { - b.Bytes = []byte("null") - } else { - b.Bytes = append(b.Bytes[0:0], data...) + if bytes.Equal(data, NullBytes) { + b.Valid = false + b.Bytes = nil + return nil } - b.Valid = true + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + b.Bytes = []byte(s) + b.Valid = true return nil } @@ -110,19 +111,19 @@ func (b Bytes) IsZero() bool { } // Scan implements the Scanner interface. -func (n *NullBytes) Scan(value interface{}) error { +func (b *Bytes) Scan(value interface{}) error { if value == nil { - n.Bytes, n.Valid = []byte{}, false + b.Bytes, b.Valid = []byte{}, false return nil } - n.Valid = true - return convert.ConvertAssign(&n.Bytes, value) + b.Valid = true + return convert.ConvertAssign(&b.Bytes, value) } // Value implements the driver Valuer interface. -func (n NullBytes) Value() (driver.Value, error) { - if !n.Valid { +func (b Bytes) Value() (driver.Value, error) { + if !b.Valid { return nil, nil } - return n.Bytes, nil + return b.Bytes, nil } diff --git a/bytes_test.go b/bytes_test.go index 98b5b68..40b9cbb 100644 --- a/bytes_test.go +++ b/bytes_test.go @@ -11,7 +11,7 @@ var ( ) func TestBytesFrom(t *testing.T) { - i := BytesFrom([]byte(`"hello"`)) + i := BytesFrom([]byte(`hello`)) assertBytes(t, i, "BytesFrom()") zero := BytesFrom(nil) @@ -26,7 +26,7 @@ func TestBytesFrom(t *testing.T) { } func TestBytesFromPtr(t *testing.T) { - n := []byte(`"hello"`) + n := []byte(`hello`) iptr := &n i := BytesFromPtr(iptr) assertBytes(t, i, "BytesFromPtr()") @@ -43,26 +43,23 @@ func TestUnmarshalBytes(t *testing.T) { var ni Bytes err = ni.UnmarshalJSON([]byte{}) - if ni.Valid == false { - t.Errorf("expected Valid to be true, got false") - } - if !bytes.Equal(ni.Bytes, []byte("null")) { - t.Errorf("Expected Bytes to be nil slice, but was not: %#v %#v", ni.Bytes, []byte(`null`)) + if err == nil { + t.Errorf("Expected error") } var null Bytes - err = null.UnmarshalJSON(nil) - if null.Valid == false { - t.Errorf("expected Valid to be true, got false") + err = null.UnmarshalJSON([]byte("null")) + if null.Valid == true { + t.Errorf("expected Valid to be false, got true") } - if !bytes.Equal(null.Bytes, []byte(`null`)) { - t.Errorf("Expected Bytes to be []byte nil, but was not: %#v %#v", null.Bytes, []byte(`null`)) + if null.Bytes != nil { + t.Errorf("Expected Bytes to be nil, but was not: %#v %#v", null.Bytes, []byte(`null`)) } } func TestTextUnmarshalBytes(t *testing.T) { var i Bytes - err := i.UnmarshalText([]byte(`"hello"`)) + err := i.UnmarshalText([]byte(`hello`)) maybePanic(err) assertBytes(t, i, "UnmarshalText() []byte") @@ -132,15 +129,15 @@ func TestBytesIsZero(t *testing.T) { func TestBytesSetValid(t *testing.T) { change := NewBytes(nil, false) assertNullBytes(t, change, "SetValid()") - change.SetValid([]byte(`"hello"`)) + change.SetValid([]byte(`hello`)) assertBytes(t, change, "SetValid()") } func TestBytesScan(t *testing.T) { var i Bytes - err := i.Scan(`"hello"`) + err := i.Scan(`hello`) maybePanic(err) - assertBytes(t, i, "scanned []byte") + assertBytes(t, i, "Scan() []byte") var null Bytes err = null.Scan(nil) @@ -149,8 +146,8 @@ func TestBytesScan(t *testing.T) { } func assertBytes(t *testing.T, i Bytes, from string) { - if !bytes.Equal(i.Bytes, []byte(`"hello"`)) { - t.Errorf("bad %s []byte: %#v ≠ %#v\n", from, string(i.Bytes), string([]byte(`"hello"`))) + if !bytes.Equal(i.Bytes, []byte("hello")) { + t.Errorf("bad %s []byte: %v ≠ %v\n", from, string(i.Bytes), string([]byte(`hello`))) } if !i.Valid { t.Error(from, "is invalid, but should be valid") diff --git a/json.go b/json.go index 3dd3aa7..0979233 100644 --- a/json.go +++ b/json.go @@ -1,33 +1,26 @@ package null import ( + "bytes" "database/sql/driver" "encoding/json" "errors" + "fmt" "gopkg.in/nullbio/null.v5/convert" ) -// NullJSON is a nullable byte slice. -type NullJSON struct { +// JSON is a nullable []byte. +type JSON struct { JSON []byte Valid bool } -// JSON is a nullable []byte. -// JSON marshals to zero if null. -// Considered null to SQL if zero. -type JSON struct { - NullJSON -} - // NewJSON creates a new JSON func NewJSON(b []byte, valid bool) JSON { return JSON{ - NullJSON: NullJSON{ - JSON: b, - Valid: valid, - }, + JSON: b, + Valid: valid, } } @@ -65,16 +58,21 @@ func (j JSON) Unmarshal(dest interface{}) error { } // UnmarshalJSON implements json.Unmarshaler. -// If data is len 0 or nil, it will unmarshal to JSON null. // If not, it will copy your data slice into JSON. func (j *JSON) UnmarshalJSON(data []byte) error { - if data == nil || len(data) == 0 { + if data == nil { + return fmt.Errorf("json: cannot unmarshal nil into Go value of type null.JSON") + } + + if bytes.Equal(data, NullBytes) { j.JSON = []byte("null") - } else { - j.JSON = append(j.JSON[0:0], data...) + j.Valid = false + return nil } j.Valid = true + j.JSON = make([]byte, len(data)) + copy(j.JSON, data) return nil } @@ -145,19 +143,19 @@ func (j JSON) IsZero() bool { } // Scan implements the Scanner interface. -func (n *NullJSON) Scan(value interface{}) error { +func (j *JSON) Scan(value interface{}) error { if value == nil { - n.JSON, n.Valid = []byte{}, false + j.JSON, j.Valid = []byte{}, false return nil } - n.Valid = true - return convert.ConvertAssign(&n.JSON, value) + j.Valid = true + return convert.ConvertAssign(&j.JSON, value) } // Value implements the driver Valuer interface. -func (n NullJSON) Value() (driver.Value, error) { - if !n.Valid { +func (j JSON) Value() (driver.Value, error) { + if !j.Valid { return nil, nil } - return n.JSON, nil + return j.JSON, nil } diff --git a/json_test.go b/json_test.go index 934ad14..3a8d004 100644 --- a/json_test.go +++ b/json_test.go @@ -65,8 +65,8 @@ func TestMarshal(t *testing.T) { if !bytes.Equal(i.JSON, []byte("null")) { t.Errorf("Expected null, but got %s", string(i.JSON)) } - if i.Valid == false { - t.Error("Expected Valid true, got Valid false") + if i.Valid == true { + t.Error("Expected Valid false, got Valid true") } } @@ -114,8 +114,8 @@ func TestUnmarshalJSON(t *testing.T) { if ni.Valid == false { t.Errorf("expected Valid to be true, got false") } - if !bytes.Equal(ni.JSON, []byte(`null`)) { - t.Errorf("Expected JSON to be null slice, but was not: %#v %#v", ni.JSON, []byte(nil)) + if !bytes.Equal(ni.JSON, nil) { + t.Errorf("Expected JSON to be nil, but was not: %#v %#v", ni.JSON, []byte(nil)) } var null JSON @@ -123,7 +123,7 @@ func TestUnmarshalJSON(t *testing.T) { if ni.Valid == false { t.Errorf("expected Valid to be true, got false") } - if !bytes.Equal(null.JSON, []byte(`null`)) { + if !bytes.Equal(null.JSON, nil) { t.Errorf("Expected JSON to be []byte nil, but was not: %#v %#v", null.JSON, []byte(nil)) } } diff --git a/zero/bool.go b/zero/bool.go deleted file mode 100644 index 22f095d..0000000 --- a/zero/bool.go +++ /dev/null @@ -1,121 +0,0 @@ -package zero - -import ( - "database/sql" - "encoding/json" - "errors" - "fmt" - "reflect" -) - -// Bool is a nullable bool. False input is considered null. -// JSON marshals to false if null. -// Considered null to SQL unmarshaled from a false value. -type Bool struct { - sql.NullBool -} - -// NewBool creates a new Bool -func NewBool(b bool, valid bool) Bool { - return Bool{ - NullBool: sql.NullBool{ - Bool: b, - Valid: valid, - }, - } -} - -// BoolFrom creates a new Bool that will be null if false. -func BoolFrom(b bool) Bool { - return NewBool(b, b) -} - -// BoolFromPtr creates a new Bool that be null if b is nil. -func BoolFromPtr(b *bool) Bool { - if b == nil { - return NewBool(false, false) - } - return NewBool(*b, true) -} - -// UnmarshalJSON implements json.Unmarshaler. -// "false" will be considered a null Bool. -// It also supports unmarshalling a sql.NullBool. -func (b *Bool) UnmarshalJSON(data []byte) error { - var err error - var v interface{} - if err = json.Unmarshal(data, &v); err != nil { - return err - } - switch x := v.(type) { - case bool: - b.Bool = x - case map[string]interface{}: - err = json.Unmarshal(data, &b.NullBool) - case nil: - b.Valid = false - return nil - default: - err = fmt.Errorf("json: cannot unmarshal %v into Go value of type zero.Bool", reflect.TypeOf(v).Name()) - } - b.Valid = (err == nil) && b.Bool - return err -} - -// UnmarshalText implements encoding.TextUnmarshaler. -// It will unmarshal to a null Bool if the input is a false or not a bool. -// It will return an error if the input is not a float, blank, or "null". -func (b *Bool) UnmarshalText(text []byte) error { - str := string(text) - switch str { - case "", "null": - b.Valid = false - return nil - case "true": - b.Bool = true - case "false": - b.Bool = false - default: - b.Valid = false - return errors.New("invalid input:" + str) - } - b.Valid = b.Bool - return nil -} - -// MarshalJSON implements json.Marshaler. -// It will encode null if this Bool is null. -func (b Bool) MarshalJSON() ([]byte, error) { - if !b.Valid || !b.Bool { - return []byte("false"), nil - } - return []byte("true"), nil -} - -// MarshalText implements encoding.TextMarshaler. -// It will encode a zero if this Bool is null. -func (b Bool) MarshalText() ([]byte, error) { - if !b.Valid || !b.Bool { - return []byte("false"), nil - } - return []byte("true"), nil -} - -// SetValid changes this Bool's value and also sets it to be non-null. -func (b *Bool) SetValid(v bool) { - b.Bool = v - b.Valid = true -} - -// Ptr returns a poBooler to this Bool's value, or a nil poBooler if this Bool is null. -func (b Bool) Ptr() *bool { - if !b.Valid { - return nil - } - return &b.Bool -} - -// IsZero returns true for null or zero Bools, for future omitempty support (Go 1.4?) -func (b Bool) IsZero() bool { - return !b.Valid || !b.Bool -} diff --git a/zero/bool_test.go b/zero/bool_test.go deleted file mode 100644 index 1880a11..0000000 --- a/zero/bool_test.go +++ /dev/null @@ -1,187 +0,0 @@ -package zero - -import ( - "encoding/json" - "testing" -) - -var ( - boolJSON = []byte(`true`) - falseJSON = []byte(`false`) - nullBoolJSON = []byte(`{"Bool":true,"Valid":true}`) -) - -func TestBoolFrom(t *testing.T) { - b := BoolFrom(true) - assertBool(t, b, "BoolFrom()") - - zero := BoolFrom(false) - if zero.Valid { - t.Error("BoolFrom(false)", "is valid, but should be invalid") - } -} - -func TestBoolFromPtr(t *testing.T) { - v := true - bptr := &v - b := BoolFromPtr(bptr) - assertBool(t, b, "BoolFromPtr()") - - null := BoolFromPtr(nil) - assertNullBool(t, null, "BoolFromPtr(nil)") -} - -func TestUnmarshalBool(t *testing.T) { - var b Bool - err := json.Unmarshal(boolJSON, &b) - maybePanic(err) - assertBool(t, b, "float json") - - var nb Bool - err = json.Unmarshal(nullBoolJSON, &nb) - maybePanic(err) - assertBool(t, nb, "sql.NullBool json") - - var zero Bool - err = json.Unmarshal(falseJSON, &zero) - maybePanic(err) - assertNullBool(t, zero, "zero json") - - var null Bool - err = json.Unmarshal(nullJSON, &null) - maybePanic(err) - assertNullBool(t, null, "null json") - - var invalid Bool - err = invalid.UnmarshalJSON(invalidJSON) - if _, ok := err.(*json.SyntaxError); !ok { - t.Errorf("expected json.SyntaxError, not %T: %v", err, err) - } - assertNullBool(t, invalid, "invalid json") - - var badType Bool - err = json.Unmarshal(intJSON, &badType) - if err == nil { - panic("err should not be nil") - } - assertNullBool(t, badType, "wrong type json") -} - -func TestTextUnmarshalBool(t *testing.T) { - var b Bool - err := b.UnmarshalText(boolJSON) - maybePanic(err) - assertBool(t, b, "UnmarshalText() bool") - - var zero Bool - err = zero.UnmarshalText(falseJSON) - maybePanic(err) - assertNullBool(t, zero, "UnmarshalText() zero bool") - - var blank Bool - err = blank.UnmarshalText([]byte("")) - maybePanic(err) - assertNullBool(t, blank, "UnmarshalText() empty bool") - - var null Bool - err = null.UnmarshalText(nullJSON) - maybePanic(err) - assertNullBool(t, null, `UnmarshalText() "null"`) - - var invalid Bool - err = invalid.UnmarshalText(invalidJSON) - if err == nil { - panic("err should not be nil") - } -} - -func TestMarshalBool(t *testing.T) { - b := BoolFrom(true) - data, err := json.Marshal(b) - maybePanic(err) - assertJSONEquals(t, data, "true", "non-empty json marshal") - - // invalid values should be encoded as false - null := NewBool(false, false) - data, err = json.Marshal(null) - maybePanic(err) - assertJSONEquals(t, data, "false", "null json marshal") -} - -func TestMarshalBoolText(t *testing.T) { - b := BoolFrom(true) - data, err := b.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "true", "non-empty text marshal") - - // invalid values should be encoded as zero - null := NewBool(false, false) - data, err = null.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "false", "null text marshal") -} - -func TestBoolPointer(t *testing.T) { - b := BoolFrom(true) - ptr := b.Ptr() - if *ptr != true { - t.Errorf("bad %s bool: %#v ≠ %v\n", "pointer", ptr, true) - } - - null := NewBool(false, false) - ptr = null.Ptr() - if ptr != nil { - t.Errorf("bad %s bool: %#v ≠ %s\n", "nil pointer", ptr, "nil") - } -} - -func TestBoolIsZero(t *testing.T) { - b := BoolFrom(true) - if b.IsZero() { - t.Errorf("IsZero() should be false") - } - - null := NewBool(false, false) - if !null.IsZero() { - t.Errorf("IsZero() should be true") - } - - zero := NewBool(false, true) - if !zero.IsZero() { - t.Errorf("IsZero() should be true") - } -} - -func TestBoolSetValid(t *testing.T) { - change := NewBool(false, false) - assertNullBool(t, change, "SetValid()") - change.SetValid(true) - assertBool(t, change, "SetValid()") -} - -func TestBoolScan(t *testing.T) { - var b Bool - err := b.Scan(true) - maybePanic(err) - assertBool(t, b, "scanned bool") - - var null Bool - err = null.Scan(nil) - maybePanic(err) - assertNullBool(t, null, "scanned null") -} - -func assertBool(t *testing.T, b Bool, from string) { - if b.Bool != true { - t.Errorf("bad %s bool: %d ≠ %v\n", from, b.Bool, true) - } - if !b.Valid { - t.Error(from, "is invalid, but should be valid") - } -} - -func assertNullBool(t *testing.T, b Bool, from string) { - if b.Valid { - t.Error(from, "is valid, but should be invalid") - } -} diff --git a/zero/bytes.go b/zero/bytes.go deleted file mode 100644 index 3a6fd10..0000000 --- a/zero/bytes.go +++ /dev/null @@ -1,128 +0,0 @@ -package zero - -import ( - "database/sql/driver" - - "gopkg.in/nullbio/null.v5/convert" -) - -// NullBytes is a nullable byte slice. -type NullBytes struct { - Bytes []byte - Valid bool -} - -// Bytes is a nullable []byte. -// JSON marshals to zero if null. -// Considered null to SQL if zero. -type Bytes struct { - NullBytes -} - -// NewBytes creates a new Bytes -func NewBytes(b []byte, valid bool) Bytes { - return Bytes{ - NullBytes: NullBytes{ - Bytes: b, - Valid: valid, - }, - } -} - -// BytesFrom creates a new Bytes that will be null if len zero. -func BytesFrom(b []byte) Bytes { - return NewBytes(b, len(b) != 0) -} - -// BytesFromPtr creates a new Bytes that be null if len zero. -func BytesFromPtr(b *[]byte) Bytes { - if b == nil || len(*b) == 0 { - return NewBytes(nil, false) - } - n := NewBytes(*b, true) - return n -} - -// UnmarshalJSON implements json.Unmarshaler. -// If data is len 0 or nil, it will unmarshal to JSON null. -// If not, it will copy your data slice into Bytes. -func (b *Bytes) UnmarshalJSON(data []byte) error { - if data == nil || len(data) == 0 { - b.Bytes = []byte("null") - b.Valid = false - } else { - b.Bytes = append(b.Bytes[0:0], data...) - b.Valid = true - } - - return nil -} - -// UnmarshalText implements encoding.TextUnmarshaler. -// It will unmarshal to nil if the text is nil or len 0. -func (b *Bytes) UnmarshalText(text []byte) error { - if text == nil || len(text) == 0 { - b.Bytes = nil - b.Valid = false - } else { - b.Bytes = append(b.Bytes[0:0], text...) - b.Valid = true - } - - return nil -} - -// MarshalJSON implements json.Marshaler. -// It will encode null if the Bytes is nil. -func (b Bytes) MarshalJSON() ([]byte, error) { - if !b.Valid { - return []byte("null"), nil - } - return b.Bytes, nil -} - -// MarshalText implements encoding.TextMarshaler. -// It will encode nil if the Bytes is invalid. -func (b Bytes) MarshalText() ([]byte, error) { - if !b.Valid { - return nil, nil - } - return b.Bytes, nil -} - -// SetValid changes this Bytes's value and also sets it to be non-null. -func (b *Bytes) SetValid(n []byte) { - b.Bytes = n - b.Valid = true -} - -// Ptr returns a pointer to this Bytes's value, or a nil pointer if this Bytes is null. -func (b Bytes) Ptr() *[]byte { - if !b.Valid { - return nil - } - return &b.Bytes -} - -// IsZero returns true for null or zero Bytes's, for future omitempty support (Go 1.4?) -func (b Bytes) IsZero() bool { - return !b.Valid || b.Bytes == nil || len(b.Bytes) == 0 -} - -// Scan implements the Scanner interface. -func (n *NullBytes) Scan(value interface{}) error { - if value == nil { - n.Bytes, n.Valid = []byte{}, false - return nil - } - n.Valid = true - return convert.ConvertAssign(&n.Bytes, value) -} - -// Value implements the driver Valuer interface. -func (n NullBytes) Value() (driver.Value, error) { - if !n.Valid { - return nil, nil - } - return n.Bytes, nil -} diff --git a/zero/bytes_test.go b/zero/bytes_test.go deleted file mode 100644 index 4612e0a..0000000 --- a/zero/bytes_test.go +++ /dev/null @@ -1,169 +0,0 @@ -package zero - -import ( - "bytes" - "encoding/json" - "testing" -) - -var ( - bytesJSON = []byte(`"hello"`) -) - -func TestBytesFrom(t *testing.T) { - i := BytesFrom([]byte(`"hello"`)) - assertBytes(t, i, "BytesFrom()") - - zero := BytesFrom(nil) - if zero.Valid { - t.Error("BytesFrom(nil)", "is valid, but should be invalid") - } - - zero = BytesFrom([]byte{}) - if zero.Valid { - t.Error("BytesFrom([]byte{})", "is valid, but should be invalid") - } -} - -func TestBytesFromPtr(t *testing.T) { - n := []byte(`"hello"`) - iptr := &n - i := BytesFromPtr(iptr) - assertBytes(t, i, "BytesFromPtr()") - - null := BytesFromPtr(nil) - assertNullBytes(t, null, "BytesFromPtr(nil)") -} - -func TestUnmarshalBytes(t *testing.T) { - var i Bytes - err := json.Unmarshal(bytesJSON, &i) - maybePanic(err) - assertBytes(t, i, "[]byte json") - - var ni Bytes - err = ni.UnmarshalJSON([]byte{}) - if ni.Valid == true { - t.Errorf("expected Valid to be false, got true") - } - if !bytes.Equal(ni.Bytes, []byte("null")) { - t.Errorf("Expected Bytes to be nil slice, but was not: %#v %#v", ni.Bytes, []byte(`null`)) - } - - var null Bytes - err = null.UnmarshalJSON(nil) - if null.Valid == true { - t.Errorf("expected Valid to be false, got true") - } - if !bytes.Equal(null.Bytes, []byte(`null`)) { - t.Errorf("Expected Bytes to be []byte nil, but was not: %#v %#v", null.Bytes, []byte(`null`)) - } -} - -func TestTextUnmarshalBytes(t *testing.T) { - var i Bytes - err := i.UnmarshalText([]byte(`"hello"`)) - maybePanic(err) - assertBytes(t, i, "UnmarshalText() []byte") - - var blank Bytes - err = blank.UnmarshalText([]byte("")) - maybePanic(err) - assertNullBytes(t, blank, "UnmarshalText() empty []byte") -} - -func TestMarshalBytes(t *testing.T) { - i := BytesFrom([]byte(`"hello"`)) - data, err := json.Marshal(i) - maybePanic(err) - assertJSONEquals(t, data, `"hello"`, "non-empty json marshal") - - // invalid values should be encoded as null - null := NewBytes(nil, false) - data, err = json.Marshal(null) - maybePanic(err) - assertJSONEquals(t, data, "null", "null json marshal") -} - -func TestMarshalBytesText(t *testing.T) { - i := BytesFrom([]byte(`"hello"`)) - data, err := i.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, `"hello"`, "non-empty text marshal") - - // invalid values should be encoded as null - null := NewBytes(nil, false) - data, err = null.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "", "null text marshal") -} - -func TestBytesPointer(t *testing.T) { - i := BytesFrom([]byte(`"hello"`)) - ptr := i.Ptr() - if !bytes.Equal(*ptr, []byte(`"hello"`)) { - t.Errorf("bad %s []byte: %#v ≠ %s\n", "pointer", ptr, `"hello"`) - } - - null := NewBytes(nil, false) - ptr = null.Ptr() - if ptr != nil { - t.Errorf("bad %s []byte: %#v ≠ %s\n", "nil pointer", ptr, "nil") - } -} - -func TestBytesIsZero(t *testing.T) { - i := BytesFrom([]byte(`"hello"`)) - if i.IsZero() { - t.Errorf("IsZero() should be false") - } - - null := NewBytes(nil, false) - if !null.IsZero() { - t.Errorf("IsZero() should be true") - } - - zero := NewBytes(nil, true) - if !zero.IsZero() { - t.Errorf("IsZero() should be true") - } - - nz := NewBytes([]byte("thing"), true) - if nz.IsZero() { - t.Error("IsZero() should be false") - } -} - -func TestBytesSetValid(t *testing.T) { - change := NewBytes(nil, false) - assertNullBytes(t, change, "SetValid()") - change.SetValid([]byte(`"hello"`)) - assertBytes(t, change, "SetValid()") -} - -func TestBytesScan(t *testing.T) { - var i Bytes - err := i.Scan(`"hello"`) - maybePanic(err) - assertBytes(t, i, "scanned []byte") - - var null Bytes - err = null.Scan(nil) - maybePanic(err) - assertNullBytes(t, null, "scanned null") -} - -func assertBytes(t *testing.T, i Bytes, from string) { - if !bytes.Equal(i.Bytes, []byte(`"hello"`)) { - t.Errorf("bad %s []byte: %#v ≠ %#v\n", from, string(i.Bytes), string([]byte(`"hello"`))) - } - if !i.Valid { - t.Error(from, "is invalid, but should be valid") - } -} - -func assertNullBytes(t *testing.T, i Bytes, from string) { - if i.Valid { - t.Error(from, "is valid, but should be invalid") - } -} diff --git a/zero/float32.go b/zero/float32.go deleted file mode 100644 index 9539d74..0000000 --- a/zero/float32.go +++ /dev/null @@ -1,144 +0,0 @@ -package zero - -import ( - "database/sql/driver" - "encoding/json" - "fmt" - "reflect" - "strconv" - - "gopkg.in/nullbio/null.v5/convert" -) - -type NullFloat32 struct { - Float32 float32 - Valid bool -} - -// Float32 is a nullable float32. Zero input will be considered null. -// JSON marshals to zero if null. -// Considered null to SQL if zero. -type Float32 struct { - NullFloat32 -} - -// NewFloat32 creates a new Float32 -func NewFloat32(f float32, valid bool) Float32 { - return Float32{ - NullFloat32: NullFloat32{ - Float32: f, - Valid: valid, - }, - } -} - -// Float32From creates a new Float32 that will be null if zero. -func Float32From(f float32) Float32 { - return NewFloat32(f, f != 0) -} - -// Float32FromPtr creates a new Float32 that be null if f is nil. -func Float32FromPtr(f *float32) Float32 { - if f == nil { - return NewFloat32(0, false) - } - return NewFloat32(*f, true) -} - -// UnmarshalJSON implements json.Unmarshaler. -// It supports number and null input. -// 0 will be considered a null Float32. -// It also supports unmarshalling a sql.NullFloat32. -func (f *Float32) UnmarshalJSON(data []byte) error { - var err error - var v interface{} - if err = json.Unmarshal(data, &v); err != nil { - return err - } - switch x := v.(type) { - case float64: - f.Float32 = float32(x) - case map[string]interface{}: - err = json.Unmarshal(data, &f.NullFloat32) - case nil: - f.Valid = false - return nil - default: - err = fmt.Errorf("json: cannot unmarshal %v into Go value of type zero.Float32", reflect.TypeOf(v).Name()) - } - f.Valid = (err == nil) && (f.Float32 != 0) - return err -} - -// UnmarshalText implements encoding.TextUnmarshaler. -// It will unmarshal to a null Float32 if the input is a blank, zero, or not a float. -// It will return an error if the input is not a float, blank, or "null". -func (f *Float32) UnmarshalText(text []byte) error { - str := string(text) - if str == "" || str == "null" { - f.Valid = false - return nil - } - var err error - res, err := strconv.ParseFloat(string(text), 32) - f.Float32 = float32(res) - f.Valid = (err == nil) && (f.Float32 != 0) - return err -} - -// MarshalJSON implements json.Marshaler. -// It will encode null if this Float32 is null. -func (f Float32) MarshalJSON() ([]byte, error) { - n := f.Float32 - if !f.Valid { - n = 0 - } - return []byte(strconv.FormatFloat(float64(n), 'f', -1, 32)), nil -} - -// MarshalText implements encoding.TextMarshaler. -// It will encode a zero if this Float32 is null. -func (f Float32) MarshalText() ([]byte, error) { - n := f.Float32 - if !f.Valid { - n = 0 - } - return []byte(strconv.FormatFloat(float64(n), 'f', -1, 32)), nil -} - -// SetValid changes this Float32's value and also sets it to be non-null. -func (f *Float32) SetValid(v float32) { - f.Float32 = v - f.Valid = true -} - -// Ptr returns a poFloater to this Float32's value, or a nil poFloater if this Float32 is null. -func (f Float32) Ptr() *float32 { - if !f.Valid { - return nil - } - return &f.Float32 -} - -// IsZero returns true for null or zero Float32's, for future omitempty support (Go 1.4?) -func (f Float32) IsZero() bool { - return !f.Valid || f.Float32 == 0 -} - -// Scan implements the Scanner interface. -func (n *NullFloat32) Scan(value interface{}) error { - if value == nil { - n.Float32, n.Valid = 0, false - return nil - } - n.Valid = true - return convert.ConvertAssign(&n.Float32, value) -} - -// Value implements the driver Valuer interface. -func (n NullFloat32) Value() (driver.Value, error) { - if !n.Valid { - return nil, nil - } - return float64(n.Float32), nil -} diff --git a/zero/float32_test.go b/zero/float32_test.go deleted file mode 100644 index ce4d7f9..0000000 --- a/zero/float32_test.go +++ /dev/null @@ -1,180 +0,0 @@ -package zero - -import ( - "encoding/json" - "testing" -) - -var ( - float32JSON = []byte(`1.2345`) - nullFloat32JSON = []byte(`{"Float32":1.2345,"Valid":true}`) -) - -func TestFloat32From(t *testing.T) { - f := Float32From(1.2345) - assertFloat32(t, f, "Float32From()") - - zero := Float32From(0) - if zero.Valid { - t.Error("Float32From(0)", "is valid, but should be invalid") - } -} - -func TestFloat32FromPtr(t *testing.T) { - n := float32(1.2345) - iptr := &n - f := Float32FromPtr(iptr) - assertFloat32(t, f, "Float32FromPtr()") - - null := Float32FromPtr(nil) - assertNullFloat32(t, null, "Float32FromPtr(nil)") -} - -func TestUnmarshalFloat32(t *testing.T) { - var f Float32 - err := json.Unmarshal(float32JSON, &f) - maybePanic(err) - assertFloat32(t, f, "float json") - - var nf Float32 - err = json.Unmarshal(nullFloat32JSON, &nf) - maybePanic(err) - assertFloat32(t, nf, "sql.NullFloat32 json") - - var zero Float32 - err = json.Unmarshal(zeroJSON, &zero) - maybePanic(err) - assertNullFloat32(t, zero, "zero json") - - var null Float32 - err = json.Unmarshal(nullJSON, &null) - maybePanic(err) - assertNullFloat32(t, null, "null json") - - var badType Float32 - err = json.Unmarshal(boolJSON, &badType) - if err == nil { - panic("err should not be nil") - } - assertNullFloat32(t, badType, "wrong type json") - - var invalid Float32 - err = invalid.UnmarshalJSON(invalidJSON) - if _, ok := err.(*json.SyntaxError); !ok { - t.Errorf("expected json.SyntaxError, not %T", err) - } - assertNullFloat32(t, invalid, "invalid json") -} - -func TestTextUnmarshalFloat32(t *testing.T) { - var f Float32 - err := f.UnmarshalText([]byte("1.2345")) - maybePanic(err) - assertFloat32(t, f, "UnmarshalText() float") - - var zero Float32 - err = zero.UnmarshalText([]byte("0")) - maybePanic(err) - assertNullFloat32(t, zero, "UnmarshalText() zero float") - - var blank Float32 - err = blank.UnmarshalText([]byte("")) - maybePanic(err) - assertNullFloat32(t, blank, "UnmarshalText() empty float") - - var null Float32 - err = null.UnmarshalText([]byte("null")) - maybePanic(err) - assertNullFloat32(t, null, `UnmarshalText() "null"`) -} - -func TestMarshalFloat32(t *testing.T) { - f := Float32From(1.2345) - data, err := json.Marshal(f) - maybePanic(err) - assertJSONEquals(t, data, "1.2345", "non-empty json marshal") - - // invalid values should be encoded as 0 - null := NewFloat32(0, false) - data, err = json.Marshal(null) - maybePanic(err) - assertJSONEquals(t, data, "0", "null json marshal") -} - -func TestMarshalFloat32Text(t *testing.T) { - f := Float32From(1.2345) - data, err := f.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "1.2345", "non-empty text marshal") - - // invalid values should be encoded as zero - null := NewFloat32(0, false) - data, err = null.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "0", "null text marshal") -} - -func TestFloat32Pointer(t *testing.T) { - f := Float32From(1.2345) - ptr := f.Ptr() - if *ptr != 1.2345 { - t.Errorf("bad %s Float32: %#v ≠ %v\n", "pointer", ptr, 1.2345) - } - - null := NewFloat32(0, false) - ptr = null.Ptr() - if ptr != nil { - t.Errorf("bad %s Float32: %#v ≠ %s\n", "nil pointer", ptr, "nil") - } -} - -func TestFloat32IsZero(t *testing.T) { - f := Float32From(1.2345) - if f.IsZero() { - t.Errorf("IsZero() should be false") - } - - null := NewFloat32(0, false) - if !null.IsZero() { - t.Errorf("IsZero() should be true") - } - - zero := NewFloat32(0, true) - if !zero.IsZero() { - t.Errorf("IsZero() should be true") - } -} - -func TestFloat32SetValid(t *testing.T) { - change := NewFloat32(0, false) - assertNullFloat32(t, change, "SetValid()") - change.SetValid(1.2345) - assertFloat32(t, change, "SetValid()") -} - -func TestFloat32Scan(t *testing.T) { - var f Float32 - err := f.Scan(1.2345) - maybePanic(err) - assertFloat32(t, f, "scanned float") - - var null Float32 - err = null.Scan(nil) - maybePanic(err) - assertNullFloat32(t, null, "scanned null") -} - -func assertFloat32(t *testing.T, f Float32, from string) { - if f.Float32 != 1.2345 { - t.Errorf("bad %s float: %f ≠ %f\n", from, f.Float32, 1.2345) - } - if !f.Valid { - t.Error(from, "is invalid, but should be valid") - } -} - -func assertNullFloat32(t *testing.T, f Float32, from string) { - if f.Valid { - t.Error(from, "is valid, but should be invalid") - } -} diff --git a/zero/float64.go b/zero/float64.go deleted file mode 100644 index fe22488..0000000 --- a/zero/float64.go +++ /dev/null @@ -1,118 +0,0 @@ -package zero - -import ( - "database/sql" - "encoding/json" - "fmt" - "reflect" - "strconv" -) - -// Float64 is a nullable float64. Zero input will be considered null. -// JSON marshals to zero if null. -// Considered null to SQL if zero. -type Float64 struct { - sql.NullFloat64 -} - -// NewFloat64 creates a new Float64 -func NewFloat64(f float64, valid bool) Float64 { - return Float64{ - NullFloat64: sql.NullFloat64{ - Float64: f, - Valid: valid, - }, - } -} - -// Float64From creates a new Float64 that will be null if zero. -func Float64From(f float64) Float64 { - return NewFloat64(f, f != 0) -} - -// Float64FromPtr creates a new Float64 that be null if f is nil. -func Float64FromPtr(f *float64) Float64 { - if f == nil { - return NewFloat64(0, false) - } - return NewFloat64(*f, true) -} - -// UnmarshalJSON implements json.Unmarshaler. -// It supports number and null input. -// 0 will be considered a null Float64. -// It also supports unmarshalling a sql.NullFloat64. -func (f *Float64) UnmarshalJSON(data []byte) error { - var err error - var v interface{} - if err = json.Unmarshal(data, &v); err != nil { - return err - } - switch x := v.(type) { - case float64: - f.Float64 = x - case map[string]interface{}: - err = json.Unmarshal(data, &f.NullFloat64) - case nil: - f.Valid = false - return nil - default: - err = fmt.Errorf("json: cannot unmarshal %v into Go value of type zero.Float64", reflect.TypeOf(v).Name()) - } - f.Valid = (err == nil) && (f.Float64 != 0) - return err -} - -// UnmarshalText implements encoding.TextUnmarshaler. -// It will unmarshal to a null Float64 if the input is a blank, zero, or not a float. -// It will return an error if the input is not a float, blank, or "null". -func (f *Float64) UnmarshalText(text []byte) error { - str := string(text) - if str == "" || str == "null" { - f.Valid = false - return nil - } - var err error - f.Float64, err = strconv.ParseFloat(string(text), 64) - f.Valid = (err == nil) && (f.Float64 != 0) - return err -} - -// MarshalJSON implements json.Marshaler. -// It will encode null if this Float64 is null. -func (f Float64) MarshalJSON() ([]byte, error) { - n := f.Float64 - if !f.Valid { - n = 0 - } - return []byte(strconv.FormatFloat(n, 'f', -1, 64)), nil -} - -// MarshalText implements encoding.TextMarshaler. -// It will encode a zero if this Float64 is null. -func (f Float64) MarshalText() ([]byte, error) { - n := f.Float64 - if !f.Valid { - n = 0 - } - return []byte(strconv.FormatFloat(n, 'f', -1, 64)), nil -} - -// SetValid changes this Float64's value and also sets it to be non-null. -func (f *Float64) SetValid(v float64) { - f.Float64 = v - f.Valid = true -} - -// Ptr returns a poFloater to this Float64's value, or a nil poFloater if this Float64 is null. -func (f Float64) Ptr() *float64 { - if !f.Valid { - return nil - } - return &f.Float64 -} - -// IsZero returns true for null or zero Float64's, for future omitempty support (Go 1.4?) -func (f Float64) IsZero() bool { - return !f.Valid || f.Float64 == 0 -} diff --git a/zero/float64_test.go b/zero/float64_test.go deleted file mode 100644 index fb99e07..0000000 --- a/zero/float64_test.go +++ /dev/null @@ -1,180 +0,0 @@ -package zero - -import ( - "encoding/json" - "testing" -) - -var ( - floatJSON = []byte(`1.2345`) - nullFloat64JSON = []byte(`{"Float64":1.2345,"Valid":true}`) -) - -func TestFloat64From(t *testing.T) { - f := Float64From(1.2345) - assertFloat64(t, f, "Float64From()") - - zero := Float64From(0) - if zero.Valid { - t.Error("Float64From(0)", "is valid, but should be invalid") - } -} - -func TestFloat64FromPtr(t *testing.T) { - n := float64(1.2345) - iptr := &n - f := Float64FromPtr(iptr) - assertFloat64(t, f, "Float64FromPtr()") - - null := Float64FromPtr(nil) - assertNullFloat64(t, null, "Float64FromPtr(nil)") -} - -func TestUnmarshalFloat64(t *testing.T) { - var f Float64 - err := json.Unmarshal(floatJSON, &f) - maybePanic(err) - assertFloat64(t, f, "float json") - - var nf Float64 - err = json.Unmarshal(nullFloat64JSON, &nf) - maybePanic(err) - assertFloat64(t, nf, "sql.NullFloat64 json") - - var zero Float64 - err = json.Unmarshal(zeroJSON, &zero) - maybePanic(err) - assertNullFloat64(t, zero, "zero json") - - var null Float64 - err = json.Unmarshal(nullJSON, &null) - maybePanic(err) - assertNullFloat64(t, null, "null json") - - var badType Float64 - err = json.Unmarshal(boolJSON, &badType) - if err == nil { - panic("err should not be nil") - } - assertNullFloat64(t, badType, "wrong type json") - - var invalid Float64 - err = invalid.UnmarshalJSON(invalidJSON) - if _, ok := err.(*json.SyntaxError); !ok { - t.Errorf("expected json.SyntaxError, not %T", err) - } - assertNullFloat64(t, invalid, "invalid json") -} - -func TestTextUnmarshalFloat64(t *testing.T) { - var f Float64 - err := f.UnmarshalText([]byte("1.2345")) - maybePanic(err) - assertFloat64(t, f, "UnmarshalText() float") - - var zero Float64 - err = zero.UnmarshalText([]byte("0")) - maybePanic(err) - assertNullFloat64(t, zero, "UnmarshalText() zero float") - - var blank Float64 - err = blank.UnmarshalText([]byte("")) - maybePanic(err) - assertNullFloat64(t, blank, "UnmarshalText() empty float") - - var null Float64 - err = null.UnmarshalText([]byte("null")) - maybePanic(err) - assertNullFloat64(t, null, `UnmarshalText() "null"`) -} - -func TestMarshalFloat64(t *testing.T) { - f := Float64From(1.2345) - data, err := json.Marshal(f) - maybePanic(err) - assertJSONEquals(t, data, "1.2345", "non-empty json marshal") - - // invalid values should be encoded as 0 - null := NewFloat64(0, false) - data, err = json.Marshal(null) - maybePanic(err) - assertJSONEquals(t, data, "0", "null json marshal") -} - -func TestMarshalFloat64Text(t *testing.T) { - f := Float64From(1.2345) - data, err := f.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "1.2345", "non-empty text marshal") - - // invalid values should be encoded as zero - null := NewFloat64(0, false) - data, err = null.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "0", "null text marshal") -} - -func TestFloat64Pointer(t *testing.T) { - f := Float64From(1.2345) - ptr := f.Ptr() - if *ptr != 1.2345 { - t.Errorf("bad %s Float64: %#v ≠ %v\n", "pointer", ptr, 1.2345) - } - - null := NewFloat64(0, false) - ptr = null.Ptr() - if ptr != nil { - t.Errorf("bad %s Float64: %#v ≠ %s\n", "nil pointer", ptr, "nil") - } -} - -func TestFloat64IsZero(t *testing.T) { - f := Float64From(1.2345) - if f.IsZero() { - t.Errorf("IsZero() should be false") - } - - null := NewFloat64(0, false) - if !null.IsZero() { - t.Errorf("IsZero() should be true") - } - - zero := NewFloat64(0, true) - if !zero.IsZero() { - t.Errorf("IsZero() should be true") - } -} - -func TestFloat64SetValid(t *testing.T) { - change := NewFloat64(0, false) - assertNullFloat64(t, change, "SetValid()") - change.SetValid(1.2345) - assertFloat64(t, change, "SetValid()") -} - -func TestFloat64Scan(t *testing.T) { - var f Float64 - err := f.Scan(1.2345) - maybePanic(err) - assertFloat64(t, f, "scanned float") - - var null Float64 - err = null.Scan(nil) - maybePanic(err) - assertNullFloat64(t, null, "scanned null") -} - -func assertFloat64(t *testing.T, f Float64, from string) { - if f.Float64 != 1.2345 { - t.Errorf("bad %s float: %f ≠ %f\n", from, f.Float64, 1.2345) - } - if !f.Valid { - t.Error(from, "is invalid, but should be valid") - } -} - -func assertNullFloat64(t *testing.T, f Float64, from string) { - if f.Valid { - t.Error(from, "is valid, but should be invalid") - } -} diff --git a/zero/int.go b/zero/int.go deleted file mode 100644 index c0b6cbc..0000000 --- a/zero/int.go +++ /dev/null @@ -1,146 +0,0 @@ -package zero - -import ( - "database/sql/driver" - "encoding/json" - "fmt" - "reflect" - "strconv" - - "gopkg.in/nullbio/null.v5/convert" -) - -type NullInt struct { - Int int - Valid bool -} - -// Int is a nullable int. -// JSON marshals to zero if null. -// Considered null to SQL if zero. -type Int struct { - NullInt -} - -// NewInt creates a new Int -func NewInt(i int, valid bool) Int { - return Int{ - NullInt: NullInt{ - Int: i, - Valid: valid, - }, - } -} - -// IntFrom creates a new Int that will be null if zero. -func IntFrom(i int) Int { - return NewInt(i, i != 0) -} - -// IntFromPtr creates a new Int that be null if i is nil. -func IntFromPtr(i *int) Int { - if i == nil { - return NewInt(0, false) - } - n := NewInt(*i, true) - return n -} - -// UnmarshalJSON implements json.Unmarshaler. -// It supports number and null input. -// 0 will be considered a null Int. -// It also supports unmarshalling a sql.NullInt. -func (i *Int) UnmarshalJSON(data []byte) error { - var err error - var v interface{} - if err = json.Unmarshal(data, &v); err != nil { - return err - } - switch v.(type) { - case float64: - // Unmarshal again, directly to int, to avoid intermediate float64 - err = json.Unmarshal(data, &i.Int) - case map[string]interface{}: - err = json.Unmarshal(data, &i.NullInt) - case nil: - i.Valid = false - return nil - default: - err = fmt.Errorf("json: cannot unmarshal %v into Go value of type zero.Int", reflect.TypeOf(v).Name()) - } - i.Valid = (err == nil) && (i.Int != 0) - return err -} - -// UnmarshalText implements encoding.TextUnmarshaler. -// It will unmarshal to a null Int if the input is a blank, zero, or not an integer. -// It will return an error if the input is not an integer, blank, or "null". -func (i *Int) UnmarshalText(text []byte) error { - str := string(text) - if str == "" || str == "null" { - i.Valid = false - return nil - } - var err error - res, err := strconv.ParseInt(string(text), 10, 0) - i.Int = int(res) - i.Valid = (err == nil) && (i.Int != 0) - return err -} - -// MarshalJSON implements json.Marshaler. -// It will encode 0 if this Int is null. -func (i Int) MarshalJSON() ([]byte, error) { - n := i.Int - if !i.Valid { - n = 0 - } - return []byte(strconv.FormatInt(int64(n), 10)), nil -} - -// MarshalText implements encoding.TextMarshaler. -// It will encode a zero if this Int is null. -func (i Int) MarshalText() ([]byte, error) { - n := i.Int - if !i.Valid { - n = 0 - } - return []byte(strconv.FormatInt(int64(n), 10)), nil -} - -// SetValid changes this Int's value and also sets it to be non-null. -func (i *Int) SetValid(n int) { - i.Int = n - i.Valid = true -} - -// Ptr returns a pointer to this Int's value, or a nil pointer if this Int is null. -func (i Int) Ptr() *int { - if !i.Valid { - return nil - } - return &i.Int -} - -// IsZero returns true for null or zero Ints, for future omitempty support (Go 1.4?) -func (i Int) IsZero() bool { - return !i.Valid || i.Int == 0 -} - -// Scan implements the Scanner interface. -func (n *NullInt) Scan(value interface{}) error { - if value == nil { - n.Int, n.Valid = 0, false - return nil - } - n.Valid = true - return convert.ConvertAssign(&n.Int, value) -} - -// Value implements the driver Valuer interface. -func (n NullInt) Value() (driver.Value, error) { - if !n.Valid { - return nil, nil - } - return int64(n.Int), nil -} diff --git a/zero/int16.go b/zero/int16.go deleted file mode 100644 index c0dfabf..0000000 --- a/zero/int16.go +++ /dev/null @@ -1,146 +0,0 @@ -package zero - -import ( - "database/sql/driver" - "encoding/json" - "fmt" - "reflect" - "strconv" - - "gopkg.in/nullbio/null.v5/convert" -) - -type NullInt16 struct { - Int16 int16 - Valid bool -} - -// Int16 is a nullable int16. -// JSON marshals to zero if null. -// Considered null to SQL if zero. -type Int16 struct { - NullInt16 -} - -// NewInt16 creates a new Int16 -func NewInt16(i int16, valid bool) Int16 { - return Int16{ - NullInt16: NullInt16{ - Int16: i, - Valid: valid, - }, - } -} - -// Int16From creates a new Int16 that will be null if zero. -func Int16From(i int16) Int16 { - return NewInt16(i, i != 0) -} - -// Int16FromPtr creates a new Int16 that be null if i is nil. -func Int16FromPtr(i *int16) Int16 { - if i == nil { - return NewInt16(0, false) - } - n := NewInt16(*i, true) - return n -} - -// UnmarshalJSON implements json.Unmarshaler. -// It supports number and null input. -// 0 will be considered a null Int16. -// It also supports unmarshalling a sql.NullInt16. -func (i *Int16) UnmarshalJSON(data []byte) error { - var err error - var v interface{} - if err = json.Unmarshal(data, &v); err != nil { - return err - } - switch v.(type) { - case float64: - // Unmarshal again, directly to int16, to avoid intermediate float64 - err = json.Unmarshal(data, &i.Int16) - case map[string]interface{}: - err = json.Unmarshal(data, &i.NullInt16) - case nil: - i.Valid = false - return nil - default: - err = fmt.Errorf("json: cannot unmarshal %v into Go value of type zero.Int16", reflect.TypeOf(v).Name()) - } - i.Valid = (err == nil) && (i.Int16 != 0) - return err -} - -// UnmarshalText implements encoding.TextUnmarshaler. -// It will unmarshal to a null Int16 if the input is a blank, zero, or not an integer. -// It will return an error if the input is not an integer, blank, or "null". -func (i *Int16) UnmarshalText(text []byte) error { - str := string(text) - if str == "" || str == "null" { - i.Valid = false - return nil - } - var err error - res, err := strconv.ParseInt(string(text), 10, 16) - i.Int16 = int16(res) - i.Valid = (err == nil) && (i.Int16 != 0) - return err -} - -// MarshalJSON implements json.Marshaler. -// It will encode 0 if this Int16 is null. -func (i Int16) MarshalJSON() ([]byte, error) { - n := i.Int16 - if !i.Valid { - n = 0 - } - return []byte(strconv.FormatInt(int64(n), 10)), nil -} - -// MarshalText implements encoding.TextMarshaler. -// It will encode a zero if this Int16 is null. -func (i Int16) MarshalText() ([]byte, error) { - n := i.Int16 - if !i.Valid { - n = 0 - } - return []byte(strconv.FormatInt(int64(n), 10)), nil -} - -// SetValid changes this Int16's value and also sets it to be non-null. -func (i *Int16) SetValid(n int16) { - i.Int16 = n - i.Valid = true -} - -// Ptr returns a pointer to this Int16's value, or a nil pointer if this Int16 is null. -func (i Int16) Ptr() *int16 { - if !i.Valid { - return nil - } - return &i.Int16 -} - -// IsZero returns true for null or zero Int16s, for future omitempty support (Go 1.4?) -func (i Int16) IsZero() bool { - return !i.Valid || i.Int16 == 0 -} - -// Scan implements the Scanner interface. -func (n *NullInt16) Scan(value interface{}) error { - if value == nil { - n.Int16, n.Valid = 0, false - return nil - } - n.Valid = true - return convert.ConvertAssign(&n.Int16, value) -} - -// Value implements the driver Valuer interface. -func (n NullInt16) Value() (driver.Value, error) { - if !n.Valid { - return nil, nil - } - return int64(n.Int16), nil -} diff --git a/zero/int16_test.go b/zero/int16_test.go deleted file mode 100644 index a615fe3..0000000 --- a/zero/int16_test.go +++ /dev/null @@ -1,207 +0,0 @@ -package zero - -import ( - "encoding/json" - "math" - "strconv" - "testing" -) - -var ( - int16JSON = []byte(`32766`) - nullInt16JSON = []byte(`{"Int16":32766,"Valid":true}`) - zero16JSON = []byte(`0`) -) - -func TestInt16From(t *testing.T) { - i := Int16From(32766) - assertInt16(t, i, "Int16From()") - - zero := Int16From(0) - if zero.Valid { - t.Error("Int16From(0)", "is valid, but should be invalid") - } -} - -func TestInt16FromPtr(t *testing.T) { - n := int16(32766) - iptr := &n - i := Int16FromPtr(iptr) - assertInt16(t, i, "Int16FromPtr()") - - null := Int16FromPtr(nil) - assertNullInt16(t, null, "Int16FromPtr(nil)") -} - -func TestUnmarshalInt16(t *testing.T) { - var i Int16 - err := json.Unmarshal(int16JSON, &i) - maybePanic(err) - assertInt16(t, i, "int json") - - var ni Int16 - err = json.Unmarshal(nullInt16JSON, &ni) - maybePanic(err) - assertInt16(t, ni, "sql.NullInt16 json") - - var zero Int16 - err = json.Unmarshal(zero16JSON, &zero) - maybePanic(err) - assertNullInt16(t, zero, "zero json") - - var null Int16 - err = json.Unmarshal(nullJSON, &null) - maybePanic(err) - assertNullInt16(t, null, "null json") - - var badType Int16 - err = json.Unmarshal(boolJSON, &badType) - if err == nil { - panic("err should not be nil") - } - assertNullInt16(t, badType, "wrong type json") - - var invalid Int16 - err = invalid.UnmarshalJSON(invalidJSON) - if _, ok := err.(*json.SyntaxError); !ok { - t.Errorf("expected json.SyntaxError, not %T", err) - } - assertNullInt16(t, invalid, "invalid json") -} - -func TestUnmarshalNonIntegerNumber16(t *testing.T) { - var i Int16 - err := json.Unmarshal(floatJSON, &i) - if err == nil { - panic("err should be present; non-integer number coerced to int") - } -} - -func TestUnmarshalInt16Overflow(t *testing.T) { - int64Overflow := uint64(math.MaxInt16) - - // Max int64 should decode successfully - var i Int16 - err := json.Unmarshal([]byte(strconv.FormatUint(int64Overflow, 10)), &i) - maybePanic(err) - - // Attempt to overflow - int64Overflow++ - err = json.Unmarshal([]byte(strconv.FormatUint(int64Overflow, 10)), &i) - if err == nil { - panic("err should be present; decoded value overflows int64") - } -} - -func TestTextUnmarshalInt16(t *testing.T) { - var i Int16 - err := i.UnmarshalText([]byte("32766")) - maybePanic(err) - assertInt16(t, i, "UnmarshalText() int") - - var zero Int16 - err = zero.UnmarshalText([]byte("0")) - maybePanic(err) - assertNullInt16(t, zero, "UnmarshalText() zero int") - - var blank Int16 - err = blank.UnmarshalText([]byte("")) - maybePanic(err) - assertNullInt16(t, blank, "UnmarshalText() empty int") - - var null Int16 - err = null.UnmarshalText([]byte("null")) - maybePanic(err) - assertNullInt16(t, null, `UnmarshalText() "null"`) -} - -func TestMarshalInt16(t *testing.T) { - i := Int16From(32766) - data, err := json.Marshal(i) - maybePanic(err) - assertJSONEquals(t, data, "32766", "non-empty json marshal") - - // invalid values should be encoded as 0 - null := NewInt16(0, false) - data, err = json.Marshal(null) - maybePanic(err) - assertJSONEquals(t, data, "0", "null json marshal") -} - -func TestMarshalInt16Text(t *testing.T) { - i := Int16From(32766) - data, err := i.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "32766", "non-empty text marshal") - - // invalid values should be encoded as zero - null := NewInt16(0, false) - data, err = null.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "0", "null text marshal") -} - -func TestInt16Pointer(t *testing.T) { - i := Int16From(32766) - ptr := i.Ptr() - if *ptr != 32766 { - t.Errorf("bad %s int: %#v ≠ %d\n", "pointer", ptr, 32766) - } - - null := NewInt16(0, false) - ptr = null.Ptr() - if ptr != nil { - t.Errorf("bad %s int: %#v ≠ %s\n", "nil pointer", ptr, "nil") - } -} - -func TestInt16IsZero(t *testing.T) { - i := Int16From(32766) - if i.IsZero() { - t.Errorf("IsZero() should be false") - } - - null := NewInt16(0, false) - if !null.IsZero() { - t.Errorf("IsZero() should be true") - } - - zero := NewInt16(0, true) - if !zero.IsZero() { - t.Errorf("IsZero() should be true") - } -} - -func TestInt16Scan(t *testing.T) { - var i Int16 - err := i.Scan(32766) - maybePanic(err) - assertInt16(t, i, "scanned int") - - var null Int16 - err = null.Scan(nil) - maybePanic(err) - assertNullInt16(t, null, "scanned null") -} - -func TestInt16SetValid(t *testing.T) { - change := NewInt16(0, false) - assertNullInt16(t, change, "SetValid()") - change.SetValid(32766) - assertInt16(t, change, "SetValid()") -} - -func assertInt16(t *testing.T, i Int16, from string) { - if i.Int16 != 32766 { - t.Errorf("bad %s int: %d ≠ %d\n", from, i.Int16, 32766) - } - if !i.Valid { - t.Error(from, "is invalid, but should be valid") - } -} - -func assertNullInt16(t *testing.T, i Int16, from string) { - if i.Valid { - t.Error(from, "is valid, but should be invalid") - } -} diff --git a/zero/int32.go b/zero/int32.go deleted file mode 100644 index 15c9590..0000000 --- a/zero/int32.go +++ /dev/null @@ -1,146 +0,0 @@ -package zero - -import ( - "database/sql/driver" - "encoding/json" - "fmt" - "reflect" - "strconv" - - "gopkg.in/nullbio/null.v5/convert" -) - -type NullInt32 struct { - Int32 int32 - Valid bool -} - -// Int32 is a nullable int32. -// JSON marshals to zero if null. -// Considered null to SQL if zero. -type Int32 struct { - NullInt32 -} - -// NewInt32 creates a new Int32 -func NewInt32(i int32, valid bool) Int32 { - return Int32{ - NullInt32: NullInt32{ - Int32: i, - Valid: valid, - }, - } -} - -// Int32From creates a new Int32 that will be null if zero. -func Int32From(i int32) Int32 { - return NewInt32(i, i != 0) -} - -// Int32FromPtr creates a new Int32 that be null if i is nil. -func Int32FromPtr(i *int32) Int32 { - if i == nil { - return NewInt32(0, false) - } - n := NewInt32(*i, true) - return n -} - -// UnmarshalJSON implements json.Unmarshaler. -// It supports number and null input. -// 0 will be considered a null Int32. -// It also supports unmarshalling a sql.NullInt32. -func (i *Int32) UnmarshalJSON(data []byte) error { - var err error - var v interface{} - if err = json.Unmarshal(data, &v); err != nil { - return err - } - switch v.(type) { - case float64: - // Unmarshal again, directly to int32, to avoid intermediate float64 - err = json.Unmarshal(data, &i.Int32) - case map[string]interface{}: - err = json.Unmarshal(data, &i.NullInt32) - case nil: - i.Valid = false - return nil - default: - err = fmt.Errorf("json: cannot unmarshal %v into Go value of type zero.Int32", reflect.TypeOf(v).Name()) - } - i.Valid = (err == nil) && (i.Int32 != 0) - return err -} - -// UnmarshalText implements encoding.TextUnmarshaler. -// It will unmarshal to a null Int32 if the input is a blank, zero, or not an integer. -// It will return an error if the input is not an integer, blank, or "null". -func (i *Int32) UnmarshalText(text []byte) error { - str := string(text) - if str == "" || str == "null" { - i.Valid = false - return nil - } - var err error - res, err := strconv.ParseInt(string(text), 10, 32) - i.Int32 = int32(res) - i.Valid = (err == nil) && (i.Int32 != 0) - return err -} - -// MarshalJSON implements json.Marshaler. -// It will encode 0 if this Int32 is null. -func (i Int32) MarshalJSON() ([]byte, error) { - n := i.Int32 - if !i.Valid { - n = 0 - } - return []byte(strconv.FormatInt(int64(n), 10)), nil -} - -// MarshalText implements encoding.TextMarshaler. -// It will encode a zero if this Int32 is null. -func (i Int32) MarshalText() ([]byte, error) { - n := i.Int32 - if !i.Valid { - n = 0 - } - return []byte(strconv.FormatInt(int64(n), 10)), nil -} - -// SetValid changes this Int32's value and also sets it to be non-null. -func (i *Int32) SetValid(n int32) { - i.Int32 = n - i.Valid = true -} - -// Ptr returns a pointer to this Int32's value, or a nil pointer if this Int32 is null. -func (i Int32) Ptr() *int32 { - if !i.Valid { - return nil - } - return &i.Int32 -} - -// IsZero returns true for null or zero Int32s, for future omitempty support (Go 1.4?) -func (i Int32) IsZero() bool { - return !i.Valid || i.Int32 == 0 -} - -// Scan implements the Scanner interface. -func (n *NullInt32) Scan(value interface{}) error { - if value == nil { - n.Int32, n.Valid = 0, false - return nil - } - n.Valid = true - return convert.ConvertAssign(&n.Int32, value) -} - -// Value implements the driver Valuer interface. -func (n NullInt32) Value() (driver.Value, error) { - if !n.Valid { - return nil, nil - } - return int64(n.Int32), nil -} diff --git a/zero/int32_test.go b/zero/int32_test.go deleted file mode 100644 index 0f16665..0000000 --- a/zero/int32_test.go +++ /dev/null @@ -1,207 +0,0 @@ -package zero - -import ( - "encoding/json" - "math" - "strconv" - "testing" -) - -var ( - int32JSON = []byte(`2147483646`) - nullInt32JSON = []byte(`{"Int32":2147483646,"Valid":true}`) - zero32JSON = []byte(`0`) -) - -func TestInt32From(t *testing.T) { - i := Int32From(2147483646) - assertInt32(t, i, "Int32From()") - - zero := Int32From(0) - if zero.Valid { - t.Error("Int32From(0)", "is valid, but should be invalid") - } -} - -func TestInt32FromPtr(t *testing.T) { - n := int32(2147483646) - iptr := &n - i := Int32FromPtr(iptr) - assertInt32(t, i, "Int32FromPtr()") - - null := Int32FromPtr(nil) - assertNullInt32(t, null, "Int32FromPtr(nil)") -} - -func TestUnmarshalInt32(t *testing.T) { - var i Int32 - err := json.Unmarshal(int32JSON, &i) - maybePanic(err) - assertInt32(t, i, "int json") - - var ni Int32 - err = json.Unmarshal(nullInt32JSON, &ni) - maybePanic(err) - assertInt32(t, ni, "sql.NullInt32 json") - - var zero Int32 - err = json.Unmarshal(zero32JSON, &zero) - maybePanic(err) - assertNullInt32(t, zero, "zero json") - - var null Int32 - err = json.Unmarshal(nullJSON, &null) - maybePanic(err) - assertNullInt32(t, null, "null json") - - var badType Int32 - err = json.Unmarshal(boolJSON, &badType) - if err == nil { - panic("err should not be nil") - } - assertNullInt32(t, badType, "wrong type json") - - var invalid Int32 - err = invalid.UnmarshalJSON(invalidJSON) - if _, ok := err.(*json.SyntaxError); !ok { - t.Errorf("expected json.SyntaxError, not %T", err) - } - assertNullInt32(t, invalid, "invalid json") -} - -func TestUnmarshalNonIntegerNumber32(t *testing.T) { - var i Int32 - err := json.Unmarshal(floatJSON, &i) - if err == nil { - panic("err should be present; non-integer number coerced to int") - } -} - -func TestUnmarshalInt32Overflow(t *testing.T) { - int64Overflow := uint64(math.MaxInt32) - - // Max int64 should decode successfully - var i Int32 - err := json.Unmarshal([]byte(strconv.FormatUint(int64Overflow, 10)), &i) - maybePanic(err) - - // Attempt to overflow - int64Overflow++ - err = json.Unmarshal([]byte(strconv.FormatUint(int64Overflow, 10)), &i) - if err == nil { - panic("err should be present; decoded value overflows int64") - } -} - -func TestTextUnmarshalInt32(t *testing.T) { - var i Int32 - err := i.UnmarshalText([]byte("2147483646")) - maybePanic(err) - assertInt32(t, i, "UnmarshalText() int") - - var zero Int32 - err = zero.UnmarshalText([]byte("0")) - maybePanic(err) - assertNullInt32(t, zero, "UnmarshalText() zero int") - - var blank Int32 - err = blank.UnmarshalText([]byte("")) - maybePanic(err) - assertNullInt32(t, blank, "UnmarshalText() empty int") - - var null Int32 - err = null.UnmarshalText([]byte("null")) - maybePanic(err) - assertNullInt32(t, null, `UnmarshalText() "null"`) -} - -func TestMarshalInt32(t *testing.T) { - i := Int32From(2147483646) - data, err := json.Marshal(i) - maybePanic(err) - assertJSONEquals(t, data, "2147483646", "non-empty json marshal") - - // invalid values should be encoded as 0 - null := NewInt32(0, false) - data, err = json.Marshal(null) - maybePanic(err) - assertJSONEquals(t, data, "0", "null json marshal") -} - -func TestMarshalInt32Text(t *testing.T) { - i := Int32From(2147483646) - data, err := i.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "2147483646", "non-empty text marshal") - - // invalid values should be encoded as zero - null := NewInt32(0, false) - data, err = null.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "0", "null text marshal") -} - -func TestInt32Pointer(t *testing.T) { - i := Int32From(2147483646) - ptr := i.Ptr() - if *ptr != 2147483646 { - t.Errorf("bad %s int: %#v ≠ %d\n", "pointer", ptr, 2147483646) - } - - null := NewInt32(0, false) - ptr = null.Ptr() - if ptr != nil { - t.Errorf("bad %s int: %#v ≠ %s\n", "nil pointer", ptr, "nil") - } -} - -func TestInt32IsZero(t *testing.T) { - i := Int32From(2147483646) - if i.IsZero() { - t.Errorf("IsZero() should be false") - } - - null := NewInt32(0, false) - if !null.IsZero() { - t.Errorf("IsZero() should be true") - } - - zero := NewInt32(0, true) - if !zero.IsZero() { - t.Errorf("IsZero() should be true") - } -} - -func TestInt32Scan(t *testing.T) { - var i Int32 - err := i.Scan(2147483646) - maybePanic(err) - assertInt32(t, i, "scanned int") - - var null Int32 - err = null.Scan(nil) - maybePanic(err) - assertNullInt32(t, null, "scanned null") -} - -func TestInt32SetValid(t *testing.T) { - change := NewInt32(0, false) - assertNullInt32(t, change, "SetValid()") - change.SetValid(2147483646) - assertInt32(t, change, "SetValid()") -} - -func assertInt32(t *testing.T, i Int32, from string) { - if i.Int32 != 2147483646 { - t.Errorf("bad %s int: %d ≠ %d\n", from, i.Int32, 2147483646) - } - if !i.Valid { - t.Error(from, "is invalid, but should be valid") - } -} - -func assertNullInt32(t *testing.T, i Int32, from string) { - if i.Valid { - t.Error(from, "is valid, but should be invalid") - } -} diff --git a/zero/int64.go b/zero/int64.go deleted file mode 100644 index bff86ac..0000000 --- a/zero/int64.go +++ /dev/null @@ -1,120 +0,0 @@ -package zero - -import ( - "database/sql" - "encoding/json" - "fmt" - "reflect" - "strconv" -) - -// Int64 is a nullable int64. -// JSON marshals to zero if null. -// Considered null to SQL if zero. -type Int64 struct { - sql.NullInt64 -} - -// NewInt64 creates a new Int64 -func NewInt64(i int64, valid bool) Int64 { - return Int64{ - NullInt64: sql.NullInt64{ - Int64: i, - Valid: valid, - }, - } -} - -// Int64From creates a new Int64 that will be null if zero. -func Int64From(i int64) Int64 { - return NewInt64(i, i != 0) -} - -// Int64FromPtr creates a new Int64 that be null if i is nil. -func Int64FromPtr(i *int64) Int64 { - if i == nil { - return NewInt64(0, false) - } - n := NewInt64(*i, true) - return n -} - -// UnmarshalJSON implements json.Unmarshaler. -// It supports number and null input. -// 0 will be considered a null Int64. -// It also supports unmarshalling a sql.NullInt64. -func (i *Int64) UnmarshalJSON(data []byte) error { - var err error - var v interface{} - if err = json.Unmarshal(data, &v); err != nil { - return err - } - switch v.(type) { - case float64: - // Unmarshal again, directly to int64, to avoid intermediate float64 - err = json.Unmarshal(data, &i.Int64) - case map[string]interface{}: - err = json.Unmarshal(data, &i.NullInt64) - case nil: - i.Valid = false - return nil - default: - err = fmt.Errorf("json: cannot unmarshal %v into Go value of type zero.Int64", reflect.TypeOf(v).Name()) - } - i.Valid = (err == nil) && (i.Int64 != 0) - return err -} - -// UnmarshalText implements encoding.TextUnmarshaler. -// It will unmarshal to a null Int64 if the input is a blank, zero, or not an integer. -// It will return an error if the input is not an integer, blank, or "null". -func (i *Int64) UnmarshalText(text []byte) error { - str := string(text) - if str == "" || str == "null" { - i.Valid = false - return nil - } - var err error - i.Int64, err = strconv.ParseInt(string(text), 10, 64) - i.Valid = (err == nil) && (i.Int64 != 0) - return err -} - -// MarshalJSON implements json.Marshaler. -// It will encode 0 if this Int64 is null. -func (i Int64) MarshalJSON() ([]byte, error) { - n := i.Int64 - if !i.Valid { - n = 0 - } - return []byte(strconv.FormatInt(n, 10)), nil -} - -// MarshalText implements encoding.TextMarshaler. -// It will encode a zero if this Int64 is null. -func (i Int64) MarshalText() ([]byte, error) { - n := i.Int64 - if !i.Valid { - n = 0 - } - return []byte(strconv.FormatInt(n, 10)), nil -} - -// SetValid changes this Int64's value and also sets it to be non-null. -func (i *Int64) SetValid(n int64) { - i.Int64 = n - i.Valid = true -} - -// Ptr returns a pointer to this Int64's value, or a nil pointer if this Int64 is null. -func (i Int64) Ptr() *int64 { - if !i.Valid { - return nil - } - return &i.Int64 -} - -// IsZero returns true for null or zero Int64s, for future omitempty support (Go 1.4?) -func (i Int64) IsZero() bool { - return !i.Valid || i.Int64 == 0 -} diff --git a/zero/int64_test.go b/zero/int64_test.go deleted file mode 100644 index 1cd0b26..0000000 --- a/zero/int64_test.go +++ /dev/null @@ -1,207 +0,0 @@ -package zero - -import ( - "encoding/json" - "math" - "strconv" - "testing" -) - -var ( - int64JSON = []byte(`9223372036854775806`) - nullInt64JSON = []byte(`{"Int64":9223372036854775806,"Valid":true}`) - zero64JSON = []byte(`0`) -) - -func TestInt64From(t *testing.T) { - i := Int64From(9223372036854775806) - assertInt64(t, i, "Int64From()") - - zero := Int64From(0) - if zero.Valid { - t.Error("Int64From(0)", "is valid, but should be invalid") - } -} - -func TestInt64FromPtr(t *testing.T) { - n := int64(9223372036854775806) - iptr := &n - i := Int64FromPtr(iptr) - assertInt64(t, i, "Int64FromPtr()") - - null := Int64FromPtr(nil) - assertNullInt64(t, null, "Int64FromPtr(nil)") -} - -func TestUnmarshalInt64(t *testing.T) { - var i Int64 - err := json.Unmarshal(int64JSON, &i) - maybePanic(err) - assertInt64(t, i, "int json") - - var ni Int64 - err = json.Unmarshal(nullInt64JSON, &ni) - maybePanic(err) - assertInt64(t, ni, "sql.NullInt64 json") - - var zero Int64 - err = json.Unmarshal(zero64JSON, &zero) - maybePanic(err) - assertNullInt64(t, zero, "zero json") - - var null Int64 - err = json.Unmarshal(nullJSON, &null) - maybePanic(err) - assertNullInt64(t, null, "null json") - - var badType Int64 - err = json.Unmarshal(boolJSON, &badType) - if err == nil { - panic("err should not be nil") - } - assertNullInt64(t, badType, "wrong type json") - - var invalid Int64 - err = invalid.UnmarshalJSON(invalidJSON) - if _, ok := err.(*json.SyntaxError); !ok { - t.Errorf("expected json.SyntaxError, not %T", err) - } - assertNullInt64(t, invalid, "invalid json") -} - -func TestUnmarshalNonIntegerNumber64(t *testing.T) { - var i Int64 - err := json.Unmarshal(floatJSON, &i) - if err == nil { - panic("err should be present; non-integer number coerced to int") - } -} - -func TestUnmarshalInt64Overflow(t *testing.T) { - int64Overflow := uint64(math.MaxInt64) - - // Max int64 should decode successfully - var i Int64 - err := json.Unmarshal([]byte(strconv.FormatUint(int64Overflow, 10)), &i) - maybePanic(err) - - // Attempt to overflow - int64Overflow++ - err = json.Unmarshal([]byte(strconv.FormatUint(int64Overflow, 10)), &i) - if err == nil { - panic("err should be present; decoded value overflows int64") - } -} - -func TestTextUnmarshalInt64(t *testing.T) { - var i Int64 - err := i.UnmarshalText([]byte("9223372036854775806")) - maybePanic(err) - assertInt64(t, i, "UnmarshalText() int") - - var zero Int64 - err = zero.UnmarshalText([]byte("0")) - maybePanic(err) - assertNullInt64(t, zero, "UnmarshalText() zero int") - - var blank Int64 - err = blank.UnmarshalText([]byte("")) - maybePanic(err) - assertNullInt64(t, blank, "UnmarshalText() empty int") - - var null Int64 - err = null.UnmarshalText([]byte("null")) - maybePanic(err) - assertNullInt64(t, null, `UnmarshalText() "null"`) -} - -func TestMarshalInt64(t *testing.T) { - i := Int64From(9223372036854775806) - data, err := json.Marshal(i) - maybePanic(err) - assertJSONEquals(t, data, "9223372036854775806", "non-empty json marshal") - - // invalid values should be encoded as 0 - null := NewInt64(0, false) - data, err = json.Marshal(null) - maybePanic(err) - assertJSONEquals(t, data, "0", "null json marshal") -} - -func TestMarshalInt64Text(t *testing.T) { - i := Int64From(9223372036854775806) - data, err := i.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "9223372036854775806", "non-empty text marshal") - - // invalid values should be encoded as zero - null := NewInt64(0, false) - data, err = null.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "0", "null text marshal") -} - -func TestInt64Pointer(t *testing.T) { - i := Int64From(9223372036854775806) - ptr := i.Ptr() - if *ptr != 9223372036854775806 { - t.Errorf("bad %s int: %#v ≠ %d\n", "pointer", ptr, 9223372036854775806) - } - - null := NewInt64(0, false) - ptr = null.Ptr() - if ptr != nil { - t.Errorf("bad %s int: %#v ≠ %s\n", "nil pointer", ptr, "nil") - } -} - -func TestInt64IsZero(t *testing.T) { - i := Int64From(9223372036854775806) - if i.IsZero() { - t.Errorf("IsZero() should be false") - } - - null := NewInt64(0, false) - if !null.IsZero() { - t.Errorf("IsZero() should be true") - } - - zero := NewInt64(0, true) - if !zero.IsZero() { - t.Errorf("IsZero() should be true") - } -} - -func TestInt64Scan(t *testing.T) { - var i Int64 - err := i.Scan(9223372036854775806) - maybePanic(err) - assertInt64(t, i, "scanned int") - - var null Int64 - err = null.Scan(nil) - maybePanic(err) - assertNullInt64(t, null, "scanned null") -} - -func TestInt64SetValid(t *testing.T) { - change := NewInt64(0, false) - assertNullInt64(t, change, "SetValid()") - change.SetValid(9223372036854775806) - assertInt64(t, change, "SetValid()") -} - -func assertInt64(t *testing.T, i Int64, from string) { - if i.Int64 != 9223372036854775806 { - t.Errorf("bad %s int: %d ≠ %d\n", from, i.Int64, 9223372036854775806) - } - if !i.Valid { - t.Error(from, "is invalid, but should be valid") - } -} - -func assertNullInt64(t *testing.T, i Int64, from string) { - if i.Valid { - t.Error(from, "is valid, but should be invalid") - } -} diff --git a/zero/int8.go b/zero/int8.go deleted file mode 100644 index 4ccb826..0000000 --- a/zero/int8.go +++ /dev/null @@ -1,146 +0,0 @@ -package zero - -import ( - "database/sql/driver" - "encoding/json" - "fmt" - "reflect" - "strconv" - - "gopkg.in/nullbio/null.v5/convert" -) - -type NullInt8 struct { - Int8 int8 - Valid bool -} - -// Int8 is a nullable int8. -// JSON marshals to zero if null. -// Considered null to SQL if zero. -type Int8 struct { - NullInt8 -} - -// NewInt8 creates a new Int8 -func NewInt8(i int8, valid bool) Int8 { - return Int8{ - NullInt8: NullInt8{ - Int8: i, - Valid: valid, - }, - } -} - -// Int8From creates a new Int8 that will be null if zero. -func Int8From(i int8) Int8 { - return NewInt8(i, i != 0) -} - -// Int8FromPtr creates a new Int8 that be null if i is nil. -func Int8FromPtr(i *int8) Int8 { - if i == nil { - return NewInt8(0, false) - } - n := NewInt8(*i, true) - return n -} - -// UnmarshalJSON implements json.Unmarshaler. -// It supports number and null input. -// 0 will be considered a null Int8. -// It also supports unmarshalling a sql.NullInt8. -func (i *Int8) UnmarshalJSON(data []byte) error { - var err error - var v interface{} - if err = json.Unmarshal(data, &v); err != nil { - return err - } - switch v.(type) { - case float64: - // Unmarshal again, directly to int8, to avoid intermediate float64 - err = json.Unmarshal(data, &i.Int8) - case map[string]interface{}: - err = json.Unmarshal(data, &i.NullInt8) - case nil: - i.Valid = false - return nil - default: - err = fmt.Errorf("json: cannot unmarshal %v into Go value of type zero.Int8", reflect.TypeOf(v).Name()) - } - i.Valid = (err == nil) && (i.Int8 != 0) - return err -} - -// UnmarshalText implements encoding.TextUnmarshaler. -// It will unmarshal to a null Int8 if the input is a blank, zero, or not an integer. -// It will return an error if the input is not an integer, blank, or "null". -func (i *Int8) UnmarshalText(text []byte) error { - str := string(text) - if str == "" || str == "null" { - i.Valid = false - return nil - } - var err error - res, err := strconv.ParseInt(string(text), 10, 8) - i.Int8 = int8(res) - i.Valid = (err == nil) && (i.Int8 != 0) - return err -} - -// MarshalJSON implements json.Marshaler. -// It will encode 0 if this Int8 is null. -func (i Int8) MarshalJSON() ([]byte, error) { - n := i.Int8 - if !i.Valid { - n = 0 - } - return []byte(strconv.FormatInt(int64(n), 10)), nil -} - -// MarshalText implements encoding.TextMarshaler. -// It will encode a zero if this Int8 is null. -func (i Int8) MarshalText() ([]byte, error) { - n := i.Int8 - if !i.Valid { - n = 0 - } - return []byte(strconv.FormatInt(int64(n), 10)), nil -} - -// SetValid changes this Int8's value and also sets it to be non-null. -func (i *Int8) SetValid(n int8) { - i.Int8 = n - i.Valid = true -} - -// Ptr returns a pointer to this Int8's value, or a nil pointer if this Int8 is null. -func (i Int8) Ptr() *int8 { - if !i.Valid { - return nil - } - return &i.Int8 -} - -// IsZero returns true for null or zero Int8s, for future omitempty support (Go 1.4?) -func (i Int8) IsZero() bool { - return !i.Valid || i.Int8 == 0 -} - -// Scan implements the Scanner interface. -func (n *NullInt8) Scan(value interface{}) error { - if value == nil { - n.Int8, n.Valid = 0, false - return nil - } - n.Valid = true - return convert.ConvertAssign(&n.Int8, value) -} - -// Value implements the driver Valuer interface. -func (n NullInt8) Value() (driver.Value, error) { - if !n.Valid { - return nil, nil - } - return int64(n.Int8), nil -} diff --git a/zero/int8_test.go b/zero/int8_test.go deleted file mode 100644 index 76429f9..0000000 --- a/zero/int8_test.go +++ /dev/null @@ -1,207 +0,0 @@ -package zero - -import ( - "encoding/json" - "math" - "strconv" - "testing" -) - -var ( - int8JSON = []byte(`126`) - nullInt8JSON = []byte(`{"Int8":126,"Valid":true}`) - zero8JSON = []byte(`0`) -) - -func TestInt8From(t *testing.T) { - i := Int8From(126) - assertInt8(t, i, "Int8From()") - - zero := Int8From(0) - if zero.Valid { - t.Error("Int8From(0)", "is valid, but should be invalid") - } -} - -func TestInt8FromPtr(t *testing.T) { - n := int8(126) - iptr := &n - i := Int8FromPtr(iptr) - assertInt8(t, i, "Int8FromPtr()") - - null := Int8FromPtr(nil) - assertNullInt8(t, null, "Int8FromPtr(nil)") -} - -func TestUnmarshalInt8(t *testing.T) { - var i Int8 - err := json.Unmarshal(int8JSON, &i) - maybePanic(err) - assertInt8(t, i, "int json") - - var ni Int8 - err = json.Unmarshal(nullInt8JSON, &ni) - maybePanic(err) - assertInt8(t, ni, "sql.NullInt8 json") - - var zero Int8 - err = json.Unmarshal(zero8JSON, &zero) - maybePanic(err) - assertNullInt8(t, zero, "zero json") - - var null Int8 - err = json.Unmarshal(nullJSON, &null) - maybePanic(err) - assertNullInt8(t, null, "null json") - - var badType Int8 - err = json.Unmarshal(boolJSON, &badType) - if err == nil { - panic("err should not be nil") - } - assertNullInt8(t, badType, "wrong type json") - - var invalid Int8 - err = invalid.UnmarshalJSON(invalidJSON) - if _, ok := err.(*json.SyntaxError); !ok { - t.Errorf("expected json.SyntaxError, not %T", err) - } - assertNullInt8(t, invalid, "invalid json") -} - -func TestUnmarshalNonIntegerNumber8(t *testing.T) { - var i Int8 - err := json.Unmarshal(floatJSON, &i) - if err == nil { - panic("err should be present; non-integer number coerced to int") - } -} - -func TestUnmarshalInt8Overflow(t *testing.T) { - int64Overflow := uint64(math.MaxInt8) - - // Max int64 should decode successfully - var i Int8 - err := json.Unmarshal([]byte(strconv.FormatUint(int64Overflow, 10)), &i) - maybePanic(err) - - // Attempt to overflow - int64Overflow++ - err = json.Unmarshal([]byte(strconv.FormatUint(int64Overflow, 10)), &i) - if err == nil { - panic("err should be present; decoded value overflows int64") - } -} - -func TestTextUnmarshalInt8(t *testing.T) { - var i Int8 - err := i.UnmarshalText([]byte("126")) - maybePanic(err) - assertInt8(t, i, "UnmarshalText() int") - - var zero Int8 - err = zero.UnmarshalText([]byte("0")) - maybePanic(err) - assertNullInt8(t, zero, "UnmarshalText() zero int") - - var blank Int8 - err = blank.UnmarshalText([]byte("")) - maybePanic(err) - assertNullInt8(t, blank, "UnmarshalText() empty int") - - var null Int8 - err = null.UnmarshalText([]byte("null")) - maybePanic(err) - assertNullInt8(t, null, `UnmarshalText() "null"`) -} - -func TestMarshalInt8(t *testing.T) { - i := Int8From(126) - data, err := json.Marshal(i) - maybePanic(err) - assertJSONEquals(t, data, "126", "non-empty json marshal") - - // invalid values should be encoded as 0 - null := NewInt8(0, false) - data, err = json.Marshal(null) - maybePanic(err) - assertJSONEquals(t, data, "0", "null json marshal") -} - -func TestMarshalInt8Text(t *testing.T) { - i := Int8From(126) - data, err := i.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "126", "non-empty text marshal") - - // invalid values should be encoded as zero - null := NewInt8(0, false) - data, err = null.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "0", "null text marshal") -} - -func TestInt8Pointer(t *testing.T) { - i := Int8From(126) - ptr := i.Ptr() - if *ptr != 126 { - t.Errorf("bad %s int: %#v ≠ %d\n", "pointer", ptr, 126) - } - - null := NewInt8(0, false) - ptr = null.Ptr() - if ptr != nil { - t.Errorf("bad %s int: %#v ≠ %s\n", "nil pointer", ptr, "nil") - } -} - -func TestInt8IsZero(t *testing.T) { - i := Int8From(126) - if i.IsZero() { - t.Errorf("IsZero() should be false") - } - - null := NewInt8(0, false) - if !null.IsZero() { - t.Errorf("IsZero() should be true") - } - - zero := NewInt8(0, true) - if !zero.IsZero() { - t.Errorf("IsZero() should be true") - } -} - -func TestInt8Scan(t *testing.T) { - var i Int8 - err := i.Scan(126) - maybePanic(err) - assertInt8(t, i, "scanned int") - - var null Int8 - err = null.Scan(nil) - maybePanic(err) - assertNullInt8(t, null, "scanned null") -} - -func TestInt8SetValid(t *testing.T) { - change := NewInt8(0, false) - assertNullInt8(t, change, "SetValid()") - change.SetValid(126) - assertInt8(t, change, "SetValid()") -} - -func assertInt8(t *testing.T, i Int8, from string) { - if i.Int8 != 126 { - t.Errorf("bad %s int: %d ≠ %d\n", from, i.Int8, 126) - } - if !i.Valid { - t.Error(from, "is invalid, but should be valid") - } -} - -func assertNullInt8(t *testing.T, i Int8, from string) { - if i.Valid { - t.Error(from, "is valid, but should be invalid") - } -} diff --git a/zero/int_test.go b/zero/int_test.go deleted file mode 100644 index 353f98a..0000000 --- a/zero/int_test.go +++ /dev/null @@ -1,189 +0,0 @@ -package zero - -import ( - "encoding/json" - "testing" -) - -var ( - intJSON = []byte(`12345`) - nullIntJSON = []byte(`{"Int":12345,"Valid":true}`) - zeroJSON = []byte(`0`) -) - -func TestIntFrom(t *testing.T) { - i := IntFrom(12345) - assertInt(t, i, "IntFrom()") - - zero := IntFrom(0) - if zero.Valid { - t.Error("IntFrom(0)", "is valid, but should be invalid") - } -} - -func TestIntFromPtr(t *testing.T) { - n := int(12345) - iptr := &n - i := IntFromPtr(iptr) - assertInt(t, i, "IntFromPtr()") - - null := IntFromPtr(nil) - assertNullInt(t, null, "IntFromPtr(nil)") -} - -func TestUnmarshalInt(t *testing.T) { - var i Int - err := json.Unmarshal(intJSON, &i) - maybePanic(err) - assertInt(t, i, "int json") - - var ni Int - err = json.Unmarshal(nullIntJSON, &ni) - maybePanic(err) - assertInt(t, ni, "sql.NullInt json") - - var zero Int - err = json.Unmarshal(zeroJSON, &zero) - maybePanic(err) - assertNullInt(t, zero, "zero json") - - var null Int - err = json.Unmarshal(nullJSON, &null) - maybePanic(err) - assertNullInt(t, null, "null json") - - var badType Int - err = json.Unmarshal(boolJSON, &badType) - if err == nil { - panic("err should not be nil") - } - assertNullInt(t, badType, "wrong type json") - - var invalid Int - err = invalid.UnmarshalJSON(invalidJSON) - if _, ok := err.(*json.SyntaxError); !ok { - t.Errorf("expected json.SyntaxError, not %T", err) - } - assertNullInt(t, invalid, "invalid json") -} - -func TestUnmarshalNonIntegerNumber(t *testing.T) { - var i Int - err := json.Unmarshal(floatJSON, &i) - if err == nil { - panic("err should be present; non-integer number coerced to int") - } -} - -func TestTextUnmarshalInt(t *testing.T) { - var i Int - err := i.UnmarshalText([]byte("12345")) - maybePanic(err) - assertInt(t, i, "UnmarshalText() int") - - var zero Int - err = zero.UnmarshalText([]byte("0")) - maybePanic(err) - assertNullInt(t, zero, "UnmarshalText() zero int") - - var blank Int - err = blank.UnmarshalText([]byte("")) - maybePanic(err) - assertNullInt(t, blank, "UnmarshalText() empty int") - - var null Int - err = null.UnmarshalText([]byte("null")) - maybePanic(err) - assertNullInt(t, null, `UnmarshalText() "null"`) -} - -func TestMarshalInt(t *testing.T) { - i := IntFrom(12345) - data, err := json.Marshal(i) - maybePanic(err) - assertJSONEquals(t, data, "12345", "non-empty json marshal") - - // invalid values should be encoded as 0 - null := NewInt(0, false) - data, err = json.Marshal(null) - maybePanic(err) - assertJSONEquals(t, data, "0", "null json marshal") -} - -func TestMarshalIntText(t *testing.T) { - i := IntFrom(12345) - data, err := i.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "12345", "non-empty text marshal") - - // invalid values should be encoded as zero - null := NewInt(0, false) - data, err = null.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "0", "null text marshal") -} - -func TestIntPointer(t *testing.T) { - i := IntFrom(12345) - ptr := i.Ptr() - if *ptr != 12345 { - t.Errorf("bad %s int: %#v ≠ %d\n", "pointer", ptr, 12345) - } - - null := NewInt(0, false) - ptr = null.Ptr() - if ptr != nil { - t.Errorf("bad %s int: %#v ≠ %s\n", "nil pointer", ptr, "nil") - } -} - -func TestIntIsZero(t *testing.T) { - i := IntFrom(12345) - if i.IsZero() { - t.Errorf("IsZero() should be false") - } - - null := NewInt(0, false) - if !null.IsZero() { - t.Errorf("IsZero() should be true") - } - - zero := NewInt(0, true) - if !zero.IsZero() { - t.Errorf("IsZero() should be true") - } -} - -func TestIntScan(t *testing.T) { - var i Int - err := i.Scan(12345) - maybePanic(err) - assertInt(t, i, "scanned int") - - var null Int - err = null.Scan(nil) - maybePanic(err) - assertNullInt(t, null, "scanned null") -} - -func TestIntSetValid(t *testing.T) { - change := NewInt(0, false) - assertNullInt(t, change, "SetValid()") - change.SetValid(12345) - assertInt(t, change, "SetValid()") -} - -func assertInt(t *testing.T, i Int, from string) { - if i.Int != 12345 { - t.Errorf("bad %s int: %d ≠ %d\n", from, i.Int, 12345) - } - if !i.Valid { - t.Error(from, "is invalid, but should be valid") - } -} - -func assertNullInt(t *testing.T, i Int, from string) { - if i.Valid { - t.Error(from, "is valid, but should be invalid") - } -} diff --git a/zero/json.go b/zero/json.go deleted file mode 100644 index 75680c2..0000000 --- a/zero/json.go +++ /dev/null @@ -1,128 +0,0 @@ -package zero - -import ( - "database/sql/driver" - - "gopkg.in/nullbio/null.v5/convert" -) - -// NullJSON is a nullable byte slice. -type NullJSON struct { - JSON []byte - Valid bool -} - -// JSON is a nullable []byte. -// JSON marshals to zero if null. -// Considered null to SQL if zero. -type JSON struct { - NullJSON -} - -// NewJSON creates a new JSON -func NewJSON(b []byte, valid bool) JSON { - return JSON{ - NullJSON: NullJSON{ - JSON: b, - Valid: valid, - }, - } -} - -// JSONFrom creates a new JSON that will be null if len zero. -func JSONFrom(b []byte) JSON { - return NewJSON(b, len(b) != 0) -} - -// JSONFromPtr creates a new JSON that be null if len zero. -func JSONFromPtr(b *[]byte) JSON { - if b == nil || len(*b) == 0 { - return NewJSON(nil, false) - } - n := NewJSON(*b, true) - return n -} - -// UnmarshalJSON implements json.Unmarshaler. -// If data is len 0 or nil, it will unmarshal to JSON null. -// If not, it will copy your data slice into JSON. -func (j *JSON) UnmarshalJSON(data []byte) error { - if data == nil || len(data) == 0 { - j.JSON = []byte("null") - j.Valid = false - } else { - j.JSON = append(j.JSON[0:0], data...) - j.Valid = true - } - - return nil -} - -// UnmarshalText implements encoding.TextUnmarshaler. -// It will unmarshal to nil if the text is nil or len 0. -func (j *JSON) UnmarshalText(text []byte) error { - if text == nil || len(text) == 0 { - j.JSON = nil - j.Valid = false - } else { - j.JSON = append(j.JSON[0:0], text...) - j.Valid = true - } - - return nil -} - -// MarshalJSON implements json.Marshaler. -// It will encode null if the JSON is nil. -func (j JSON) MarshalJSON() ([]byte, error) { - if !j.Valid { - return []byte("null"), nil - } - return j.JSON, nil -} - -// MarshalText implements encoding.TextMarshaler. -// It will encode nil if the JSON is invalid. -func (j JSON) MarshalText() ([]byte, error) { - if !j.Valid { - return nil, nil - } - return j.JSON, nil -} - -// SetValid changes this JSON's value and also sets it to be non-null. -func (j *JSON) SetValid(n []byte) { - j.JSON = n - j.Valid = true -} - -// Ptr returns a pointer to this JSON's value, or a nil pointer if this JSON is null. -func (j JSON) Ptr() *[]byte { - if !j.Valid { - return nil - } - return &j.JSON -} - -// IsZero returns true for null or zero JSON's, for future omitempty support (Go 1.4?) -func (j JSON) IsZero() bool { - return !j.Valid || j.JSON == nil || len(j.JSON) == 0 -} - -// Scan implements the Scanner interface. -func (n *NullJSON) Scan(value interface{}) error { - if value == nil { - n.JSON, n.Valid = []byte{}, false - return nil - } - n.Valid = true - return convert.ConvertAssign(&n.JSON, value) -} - -// Value implements the driver Valuer interface. -func (n NullJSON) Value() (driver.Value, error) { - if !n.Valid { - return nil, nil - } - return n.JSON, nil -} diff --git a/zero/json_test.go b/zero/json_test.go deleted file mode 100644 index 789ac01..0000000 --- a/zero/json_test.go +++ /dev/null @@ -1,169 +0,0 @@ -package zero - -import ( - "bytes" - "encoding/json" - "testing" -) - -var ( - jsonJSON = []byte(`"hello"`) -) - -func TestJSONFrom(t *testing.T) { - i := JSONFrom([]byte(`"hello"`)) - assertJSON(t, i, "JSONFrom()") - - zero := JSONFrom(nil) - if zero.Valid { - t.Error("JSONFrom(nil)", "is valid, but should be invalid") - } - - zero = JSONFrom([]byte{}) - if zero.Valid { - t.Error("JSONFrom([]byte{})", "is valid, but should be invalid") - } -} - -func TestJSONFromPtr(t *testing.T) { - n := []byte(`"hello"`) - iptr := &n - i := JSONFromPtr(iptr) - assertJSON(t, i, "JSONFromPtr()") - - null := JSONFromPtr(nil) - assertNullJSON(t, null, "JSONFromPtr(nil)") -} - -func TestUnmarshalJSON(t *testing.T) { - var i JSON - err := json.Unmarshal(bytesJSON, &i) - maybePanic(err) - assertJSON(t, i, "[]byte json") - - var ni JSON - err = ni.UnmarshalJSON([]byte{}) - if ni.Valid == true { - t.Errorf("expected Valid to be false, got true") - } - if !bytes.Equal(ni.JSON, []byte("null")) { - t.Errorf("Expected JSON to be nil slice, but was not: %#v %#v", ni.JSON, []byte(`null`)) - } - - var null JSON - err = null.UnmarshalJSON(nil) - if null.Valid == true { - t.Errorf("expected Valid to be false, got true") - } - if !bytes.Equal(null.JSON, []byte(`null`)) { - t.Errorf("Expected JSON to be []byte nil, but was not: %#v %#v", null.JSON, []byte(`null`)) - } -} - -func TestTextUnmarshalJSON(t *testing.T) { - var i JSON - err := i.UnmarshalText([]byte(`"hello"`)) - maybePanic(err) - assertJSON(t, i, "UnmarshalText() []byte") - - var blank JSON - err = blank.UnmarshalText([]byte("")) - maybePanic(err) - assertNullJSON(t, blank, "UnmarshalText() empty []byte") -} - -func TestMarshalJSON(t *testing.T) { - i := JSONFrom([]byte(`"hello"`)) - data, err := json.Marshal(i) - maybePanic(err) - assertJSONEquals(t, data, `"hello"`, "non-empty json marshal") - - // invalid values should be encoded as null - null := NewJSON(nil, false) - data, err = json.Marshal(null) - maybePanic(err) - assertJSONEquals(t, data, "null", "null json marshal") -} - -func TestMarshalJSONText(t *testing.T) { - i := JSONFrom([]byte(`"hello"`)) - data, err := i.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, `"hello"`, "non-empty text marshal") - - // invalid values should be encoded as null - null := NewJSON(nil, false) - data, err = null.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "", "null text marshal") -} - -func TestJSONPointer(t *testing.T) { - i := JSONFrom([]byte(`"hello"`)) - ptr := i.Ptr() - if !bytes.Equal(*ptr, []byte(`"hello"`)) { - t.Errorf("bad %s []byte: %#v ≠ %s\n", "pointer", ptr, `"hello"`) - } - - null := NewJSON(nil, false) - ptr = null.Ptr() - if ptr != nil { - t.Errorf("bad %s []byte: %#v ≠ %s\n", "nil pointer", ptr, "nil") - } -} - -func TestJSONIsZero(t *testing.T) { - i := JSONFrom([]byte(`"hello"`)) - if i.IsZero() { - t.Errorf("IsZero() should be false") - } - - null := NewJSON(nil, false) - if !null.IsZero() { - t.Errorf("IsZero() should be true") - } - - zero := NewJSON(nil, true) - if !zero.IsZero() { - t.Errorf("IsZero() should be true") - } - - nz := NewJSON([]byte("thing"), true) - if nz.IsZero() { - t.Error("IsZero() should be false") - } -} - -func TestJSONSetValid(t *testing.T) { - change := NewJSON(nil, false) - assertNullJSON(t, change, "SetValid()") - change.SetValid([]byte(`"hello"`)) - assertJSON(t, change, "SetValid()") -} - -func TestJSONScan(t *testing.T) { - var i JSON - err := i.Scan(`"hello"`) - maybePanic(err) - assertJSON(t, i, "scanned []byte") - - var null JSON - err = null.Scan(nil) - maybePanic(err) - assertNullJSON(t, null, "scanned null") -} - -func assertJSON(t *testing.T, i JSON, from string) { - if !bytes.Equal(i.JSON, []byte(`"hello"`)) { - t.Errorf("bad %s []byte: %#v ≠ %#v\n", from, string(i.JSON), string([]byte(`"hello"`))) - } - if !i.Valid { - t.Error(from, "is invalid, but should be valid") - } -} - -func assertNullJSON(t *testing.T, i JSON, from string) { - if i.Valid { - t.Error(from, "is valid, but should be invalid") - } -} diff --git a/zero/string.go b/zero/string.go deleted file mode 100644 index 82e7fcb..0000000 --- a/zero/string.go +++ /dev/null @@ -1,103 +0,0 @@ -// Package zero contains SQL types that consider zero input and null input to be equivalent -// with convenient support for JSON and text marshaling. -// Types in this package will JSON marshal to their zero value, even if null. -// Use the null parent package if you don't want this. -package zero - -import ( - "database/sql" - "encoding/json" - "fmt" - "reflect" -) - -// String is a nullable string. -// JSON marshals to a blank string if null. -// Considered null to SQL if zero. -type String struct { - sql.NullString -} - -// NewString creates a new String -func NewString(s string, valid bool) String { - return String{ - NullString: sql.NullString{ - String: s, - Valid: valid, - }, - } -} - -// StringFrom creates a new String that will be null if s is blank. -func StringFrom(s string) String { - return NewString(s, s != "") -} - -// StringFromPtr creates a new String that be null if s is nil or blank. -// It will make s point to the String's value. -func StringFromPtr(s *string) String { - if s == nil { - return NewString("", false) - } - return NewString(*s, *s != "") -} - -// UnmarshalJSON implements json.Unmarshaler. -// It supports string and null input. Blank string input produces a null String. -// It also supports unmarshalling a sql.NullString. -func (s *String) UnmarshalJSON(data []byte) error { - var err error - var v interface{} - if err = json.Unmarshal(data, &v); err != nil { - return err - } - switch x := v.(type) { - case string: - s.String = x - case map[string]interface{}: - err = json.Unmarshal(data, &s.NullString) - case nil: - s.Valid = false - return nil - default: - err = fmt.Errorf("json: cannot unmarshal %v into Go value of type zero.String", reflect.TypeOf(v).Name()) - } - s.Valid = (err == nil) && (s.String != "") - return err -} - -// MarshalText implements encoding.TextMarshaler. -// It will encode a blank string when this String is null. -func (s String) MarshalText() ([]byte, error) { - if !s.Valid { - return []byte{}, nil - } - return []byte(s.String), nil -} - -// UnmarshalText implements encoding.TextUnmarshaler. -// It will unmarshal to a null String if the input is a blank string. -func (s *String) UnmarshalText(text []byte) error { - s.String = string(text) - s.Valid = s.String != "" - return nil -} - -// SetValid changes this String's value and also sets it to be non-null. -func (s *String) SetValid(v string) { - s.String = v - s.Valid = true -} - -// Ptr returns a pointer to this String's value, or a nil pointer if this String is null. -func (s String) Ptr() *string { - if !s.Valid { - return nil - } - return &s.String -} - -// IsZero returns true for null or empty strings, for potential future omitempty support. -func (s String) IsZero() bool { - return !s.Valid || s.String == "" -} diff --git a/zero/string_test.go b/zero/string_test.go deleted file mode 100644 index 416f415..0000000 --- a/zero/string_test.go +++ /dev/null @@ -1,189 +0,0 @@ -package zero - -import ( - "encoding/json" - "testing" -) - -var ( - stringJSON = []byte(`"test"`) - blankStringJSON = []byte(`""`) - nullStringJSON = []byte(`{"String":"test","Valid":true}`) - - nullJSON = []byte(`null`) - invalidJSON = []byte(`:)`) -) - -type stringInStruct struct { - Test String `json:"test,omitempty"` -} - -func TestStringFrom(t *testing.T) { - str := StringFrom("test") - assertStr(t, str, "StringFrom() string") - - null := StringFrom("") - assertNullStr(t, null, "StringFrom() empty string") -} - -func TestUnmarshalString(t *testing.T) { - var str String - err := json.Unmarshal(stringJSON, &str) - maybePanic(err) - assertStr(t, str, "string json") - - var ns String - err = json.Unmarshal(nullStringJSON, &ns) - maybePanic(err) - assertStr(t, ns, "sql.NullString json") - - var blank String - err = json.Unmarshal(blankStringJSON, &blank) - maybePanic(err) - assertNullStr(t, blank, "blank string json") - - var null String - err = json.Unmarshal(nullJSON, &null) - maybePanic(err) - assertNullStr(t, null, "null json") - - var badType String - err = json.Unmarshal(boolJSON, &badType) - if err == nil { - panic("err should not be nil") - } - assertNullStr(t, badType, "wrong type json") - - var invalid String - err = invalid.UnmarshalJSON(invalidJSON) - if _, ok := err.(*json.SyntaxError); !ok { - t.Errorf("expected json.SyntaxError, not %T", err) - } - assertNullStr(t, invalid, "invalid json") -} - -func TestTextUnmarshalString(t *testing.T) { - var str String - err := str.UnmarshalText([]byte("test")) - maybePanic(err) - assertStr(t, str, "UnmarshalText() string") - - var null String - err = null.UnmarshalText([]byte("")) - maybePanic(err) - assertNullStr(t, null, "UnmarshalText() empty string") -} - -func TestMarshalString(t *testing.T) { - str := StringFrom("test") - data, err := json.Marshal(str) - maybePanic(err) - assertJSONEquals(t, data, `"test"`, "non-empty json marshal") - - // invalid values should be encoded as an empty string - null := StringFrom("") - data, err = json.Marshal(null) - maybePanic(err) - assertJSONEquals(t, data, `""`, "empty json marshal") -} - -// Tests omitempty... broken until Go 1.4 -// func TestMarshalStringInStruct(t *testing.T) { -// obj := stringInStruct{Test: StringFrom("")} -// data, err := json.Marshal(obj) -// maybePanic(err) -// assertJSONEquals(t, data, `{}`, "null string in struct") -// } - -func TestStringPointer(t *testing.T) { - str := StringFrom("test") - ptr := str.Ptr() - if *ptr != "test" { - t.Errorf("bad %s string: %#v ≠ %s\n", "pointer", ptr, "test") - } - - null := StringFrom("") - ptr = null.Ptr() - if ptr != nil { - t.Errorf("bad %s string: %#v ≠ %s\n", "nil pointer", ptr, "nil") - } -} - -func TestStringFromPointer(t *testing.T) { - test := "test" - testptr := &test - str := StringFromPtr(testptr) - assertStr(t, str, "StringFromPtr()") - - testptr = nil - null := StringFromPtr(testptr) - assertNullStr(t, null, "StringFromPtr()") - - ptr := null.Ptr() - if ptr != nil { - t.Errorf("bad %s string: %#v ≠ %s\n", "nil pointer", ptr, "nil") - } -} - -func TestStringIsZero(t *testing.T) { - str := StringFrom("test") - if str.IsZero() { - t.Errorf("IsZero() should be false") - } - - null := StringFrom("") - if !null.IsZero() { - t.Errorf("IsZero() should be true") - } - - empty := NewString("", true) - if !empty.IsZero() { - t.Errorf("IsZero() should be true") - } -} - -func TestStringScan(t *testing.T) { - var str String - err := str.Scan("test") - maybePanic(err) - assertStr(t, str, "scanned string") - - var null String - err = null.Scan(nil) - maybePanic(err) - assertNullStr(t, null, "scanned null") -} - -func TestStringSetValid(t *testing.T) { - change := NewString("", false) - assertNullStr(t, change, "SetValid()") - change.SetValid("test") - assertStr(t, change, "SetValid()") -} - -func maybePanic(err error) { - if err != nil { - panic(err) - } -} - -func assertStr(t *testing.T, s String, from string) { - if s.String != "test" { - t.Errorf("bad %s string: %s ≠ %s\n", from, s.String, "test") - } - if !s.Valid { - t.Error(from, "is invalid, but should be valid") - } -} - -func assertNullStr(t *testing.T, s String, from string) { - if s.Valid { - t.Error(from, "is valid, but should be invalid") - } -} - -func assertJSONEquals(t *testing.T, data []byte, cmp string, from string) { - if string(data) != cmp { - t.Errorf("bad %s data: %s ≠ %s\n", from, data, cmp) - } -} diff --git a/zero/time.go b/zero/time.go deleted file mode 100644 index 0e842ba..0000000 --- a/zero/time.go +++ /dev/null @@ -1,145 +0,0 @@ -package zero - -import ( - "database/sql/driver" - "encoding/json" - "fmt" - "reflect" - "time" -) - -// Time is a nullable time.Time. -// JSON marshals to the zero value for time.Time if null. -// Considered to be null to SQL if zero. -type Time struct { - Time time.Time - Valid bool -} - -// Scan implements Scanner interface. -func (t *Time) Scan(value interface{}) error { - var err error - switch x := value.(type) { - case time.Time: - t.Time = x - case nil: - t.Valid = false - return nil - default: - err = fmt.Errorf("null: cannot scan type %T into null.Time: %v", value, value) - } - t.Valid = err == nil - return err -} - -// Value implements the driver Valuer interface. -func (t Time) Value() (driver.Value, error) { - if !t.Valid { - return nil, nil - } - return t.Time, nil -} - -// NewTime creates a new Time. -func NewTime(t time.Time, valid bool) Time { - return Time{ - Time: t, - Valid: valid, - } -} - -// TimeFrom creates a new Time that will -// be null if t is the zero value. -func TimeFrom(t time.Time) Time { - return NewTime(t, !t.IsZero()) -} - -// TimeFromPtr creates a new Time that will -// be null if t is nil or *t is the zero value. -func TimeFromPtr(t *time.Time) Time { - if t == nil { - return NewTime(time.Time{}, false) - } - return TimeFrom(*t) -} - -// MarshalJSON implements json.Marshaler. -// It will encode the zero value of time.Time -// if this time is invalid. -func (t Time) MarshalJSON() ([]byte, error) { - if !t.Valid { - return (time.Time{}).MarshalJSON() - } - return t.Time.MarshalJSON() -} - -// UnmarshalJSON implements json.Unmarshaler. -// It supports string, object (e.g. pq.NullTime and friends) -// and null input. -func (t *Time) UnmarshalJSON(data []byte) error { - var err error - var v interface{} - if err = json.Unmarshal(data, &v); err != nil { - return err - } - switch x := v.(type) { - case string: - var ti time.Time - if err = ti.UnmarshalJSON(data); err != nil { - return err - } - *t = TimeFrom(ti) - return nil - case map[string]interface{}: - ti, tiOK := x["Time"].(string) - valid, validOK := x["Valid"].(bool) - if !tiOK || !validOK { - return fmt.Errorf(`json: unmarshalling object into Go value of type null.Time requires key "Time" to be of type string and key "Valid" to be of type bool; found %T and %T, respectively`, x["Time"], x["Valid"]) - } - err = t.Time.UnmarshalText([]byte(ti)) - t.Valid = valid - return err - case nil: - t.Valid = false - return nil - default: - return fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Time", reflect.TypeOf(v).Name()) - } -} - -func (t Time) MarshalText() ([]byte, error) { - ti := t.Time - if !t.Valid { - ti = time.Time{} - } - return ti.MarshalText() -} - -func (t *Time) UnmarshalText(text []byte) error { - str := string(text) - if str == "" || str == "null" { - t.Valid = false - return nil - } - if err := t.Time.UnmarshalText(text); err != nil { - return err - } - t.Valid = true - return nil -} - -// SetValid changes this Time's value and -// sets it to be non-null. -func (t *Time) SetValid(v time.Time) { - t.Time = v - t.Valid = true -} - -// Ptr returns a pointer to this Time's value, -// or a nil pointer if this Time is zero. -func (t Time) Ptr() *time.Time { - if !t.Valid { - return nil - } - return &t.Time -} diff --git a/zero/time_test.go b/zero/time_test.go deleted file mode 100644 index fa1dd9a..0000000 --- a/zero/time_test.go +++ /dev/null @@ -1,209 +0,0 @@ -package zero - -import ( - "encoding/json" - "testing" - "time" -) - -var ( - timeString = "2012-12-21T21:21:21Z" - timeJSON = []byte(`"` + timeString + `"`) - zeroTimeStr = "0001-01-01T00:00:00Z" - zeroTimeJSON = []byte(`"0001-01-01T00:00:00Z"`) - blankTimeJSON = []byte(`null`) - timeValue, _ = time.Parse(time.RFC3339, timeString) - timeObject = []byte(`{"Time":"2012-12-21T21:21:21Z","Valid":true}`) - nullObject = []byte(`{"Time":"0001-01-01T00:00:00Z","Valid":false}`) - badObject = []byte(`{"hello": "world"}`) -) - -func TestUnmarshalTimeJSON(t *testing.T) { - var ti Time - err := json.Unmarshal(timeObject, &ti) - maybePanic(err) - assertTime(t, ti, "UnmarshalJSON() json") - - var blank Time - err = json.Unmarshal(blankTimeJSON, &blank) - maybePanic(err) - assertNullTime(t, blank, "blank time json") - - var zero Time - err = json.Unmarshal(zeroTimeJSON, &zero) - maybePanic(err) - assertNullTime(t, zero, "zero time json") - - var fromObject Time - err = json.Unmarshal(timeObject, &fromObject) - maybePanic(err) - assertTime(t, fromObject, "map time json") - - var null Time - err = json.Unmarshal(nullObject, &null) - maybePanic(err) - assertNullTime(t, null, "map null time json") - - var nullFromObj Time - err = json.Unmarshal(nullObject, &nullFromObj) - maybePanic(err) - assertNullTime(t, nullFromObj, "null from object json") - - var invalid Time - err = invalid.UnmarshalJSON(invalidJSON) - if _, ok := err.(*json.SyntaxError); !ok { - t.Errorf("expected json.SyntaxError, not %T", err) - } - assertNullTime(t, invalid, "invalid from object json") - - var bad Time - err = json.Unmarshal(badObject, &bad) - if err == nil { - t.Errorf("expected error: bad object") - } - assertNullTime(t, bad, "bad from object json") - - var wrongType Time - err = json.Unmarshal(intJSON, &wrongType) - if err == nil { - t.Errorf("expected error: wrong type JSON") - } - assertNullTime(t, wrongType, "wrong type object json") - - var wrongString Time - err = json.Unmarshal(stringJSON, &wrongString) - if err == nil { - t.Errorf("expected error: wrong string JSON") - } - assertNullTime(t, wrongString, "wrong string object json") -} - -func TestMarshalTime(t *testing.T) { - ti := TimeFrom(timeValue) - data, err := json.Marshal(ti) - maybePanic(err) - assertJSONEquals(t, data, string(timeJSON), "non-empty json marshal") - - null := TimeFromPtr(nil) - data, err = json.Marshal(null) - maybePanic(err) - assertJSONEquals(t, data, string(zeroTimeJSON), "empty json marshal") -} - -func TestUnmarshalTimeText(t *testing.T) { - ti := TimeFrom(timeValue) - txt, err := ti.MarshalText() - maybePanic(err) - assertJSONEquals(t, txt, timeString, "marshal text") - - var unmarshal Time - err = unmarshal.UnmarshalText(txt) - maybePanic(err) - assertTime(t, unmarshal, "unmarshal text") - - var null Time - err = null.UnmarshalText(nullJSON) - maybePanic(err) - assertNullTime(t, null, "unmarshal null text") - txt, err = null.MarshalText() - maybePanic(err) - assertJSONEquals(t, txt, zeroTimeStr, "marshal null text") - - var invalid Time - err = invalid.UnmarshalText([]byte("hello world")) - if err == nil { - t.Error("expected error") - } - assertNullTime(t, invalid, "bad string") -} - -func TestTimeFrom(t *testing.T) { - ti := TimeFrom(timeValue) - assertTime(t, ti, "TimeFrom() time.Time") - - var nt time.Time - null := TimeFrom(nt) - assertNullTime(t, null, "TimeFrom() empty time.Time") -} - -func TestTimeFromPtr(t *testing.T) { - ti := TimeFromPtr(&timeValue) - assertTime(t, ti, "TimeFromPtr() time") - - null := TimeFromPtr(nil) - assertNullTime(t, null, "TimeFromPtr(nil)") -} - -func TestTimeSetValid(t *testing.T) { - var ti time.Time - change := TimeFrom(ti) - assertNullTime(t, change, "SetValid()") - change.SetValid(timeValue) - assertTime(t, change, "SetValid()") -} - -func TestTimePointer(t *testing.T) { - ti := TimeFrom(timeValue) - ptr := ti.Ptr() - if *ptr != timeValue { - t.Errorf("bad %s time: %#v ≠ %v\n", "pointer", ptr, timeValue) - } - - var nt time.Time - null := TimeFrom(nt) - ptr = null.Ptr() - if ptr != nil { - t.Errorf("bad %s time: %#v ≠ %s\n", "nil pointer", ptr, "nil") - } -} - -func TestTimeScan(t *testing.T) { - var ti Time - err := ti.Scan(timeValue) - maybePanic(err) - assertTime(t, ti, "scanned time") - - var null Time - err = null.Scan(nil) - maybePanic(err) - assertNullTime(t, null, "scanned null") - - var wrong Time - err = wrong.Scan(int64(42)) - if err == nil { - t.Error("expected error") - } - assertNullTime(t, wrong, "scanned wrong") -} - -func TestTimeValue(t *testing.T) { - ti := TimeFrom(timeValue) - v, err := ti.Value() - maybePanic(err) - if ti.Time != timeValue { - t.Errorf("bad time.Time value: %v ≠ %v", ti.Time, timeValue) - } - - var nt time.Time - zero := TimeFrom(nt) - v, err = zero.Value() - maybePanic(err) - if v != nil { - t.Errorf("bad %s time.Time value: %v ≠ %v", "zero", v, nil) - } -} - -func assertTime(t *testing.T, ti Time, from string) { - if ti.Time != timeValue { - t.Errorf("bad %v time: %v ≠ %v\n", from, ti.Time, timeValue) - } - if !ti.Valid { - t.Error(from, "is invalid, but should be valid") - } -} - -func assertNullTime(t *testing.T, ti Time, from string) { - if ti.Valid { - t.Error(from, "is valid, but should be invalid") - } -} diff --git a/zero/uint.go b/zero/uint.go deleted file mode 100644 index 75c793c..0000000 --- a/zero/uint.go +++ /dev/null @@ -1,146 +0,0 @@ -package zero - -import ( - "database/sql/driver" - "encoding/json" - "fmt" - "reflect" - "strconv" - - "gopkg.in/nullbio/null.v5/convert" -) - -type NullUint struct { - Uint uint - Valid bool -} - -// Uint is a nullable uint. -// JSON marshals to zero if null. -// Considered null to SQL if zero. -type Uint struct { - NullUint -} - -// NewUint creates a new Uint -func NewUint(i uint, valid bool) Uint { - return Uint{ - NullUint: NullUint{ - Uint: i, - Valid: valid, - }, - } -} - -// UintFrom creates a new Uint that will be null if zero. -func UintFrom(i uint) Uint { - return NewUint(i, i != 0) -} - -// UintFromPtr creates a new Uint that be null if i is nil. -func UintFromPtr(i *uint) Uint { - if i == nil { - return NewUint(0, false) - } - n := NewUint(*i, true) - return n -} - -// UnmarshalJSON implements json.Unmarshaler. -// It supports number and null input. -// 0 will be considered a null Uint. -// It also supports unmarshalling a sql.NullUint. -func (i *Uint) UnmarshalJSON(data []byte) error { - var err error - var v interface{} - if err = json.Unmarshal(data, &v); err != nil { - return err - } - switch v.(type) { - case float64: - // Unmarshal again, directly to uint, to avoid intermediate float64 - err = json.Unmarshal(data, &i.Uint) - case map[string]interface{}: - err = json.Unmarshal(data, &i.NullUint) - case nil: - i.Valid = false - return nil - default: - err = fmt.Errorf("json: cannot unmarshal %v into Go value of type zero.Uint", reflect.TypeOf(v).Name()) - } - i.Valid = (err == nil) && (i.Uint != 0) - return err -} - -// UnmarshalText implements encoding.TextUnmarshaler. -// It will unmarshal to a null Uint if the input is a blank, zero, or not an integer. -// It will return an error if the input is not an integer, blank, or "null". -func (i *Uint) UnmarshalText(text []byte) error { - str := string(text) - if str == "" || str == "null" { - i.Valid = false - return nil - } - var err error - res, err := strconv.ParseUint(string(text), 10, 0) - i.Uint = uint(res) - i.Valid = (err == nil) && (i.Uint != 0) - return err -} - -// MarshalJSON implements json.Marshaler. -// It will encode 0 if this Uint is null. -func (i Uint) MarshalJSON() ([]byte, error) { - n := i.Uint - if !i.Valid { - n = 0 - } - return []byte(strconv.FormatUint(uint64(n), 10)), nil -} - -// MarshalText implements encoding.TextMarshaler. -// It will encode a zero if this Uint is null. -func (i Uint) MarshalText() ([]byte, error) { - n := i.Uint - if !i.Valid { - n = 0 - } - return []byte(strconv.FormatUint(uint64(n), 10)), nil -} - -// SetValid changes this Uint's value and also sets it to be non-null. -func (i *Uint) SetValid(n uint) { - i.Uint = n - i.Valid = true -} - -// Ptr returns a pointer to this Uint's value, or a nil pointer if this Uint is null. -func (i Uint) Ptr() *uint { - if !i.Valid { - return nil - } - return &i.Uint -} - -// IsZero returns true for null or zero Uints, for future omitempty support (Go 1.4?) -func (i Uint) IsZero() bool { - return !i.Valid || i.Uint == 0 -} - -// Scan implements the Scanner interface. -func (n *NullUint) Scan(value interface{}) error { - if value == nil { - n.Uint, n.Valid = 0, false - return nil - } - n.Valid = true - return convert.ConvertAssign(&n.Uint, value) -} - -// Value implements the driver Valuer interface. -func (n NullUint) Value() (driver.Value, error) { - if !n.Valid { - return nil, nil - } - return int64(n.Uint), nil -} diff --git a/zero/uint16.go b/zero/uint16.go deleted file mode 100644 index a2481be..0000000 --- a/zero/uint16.go +++ /dev/null @@ -1,146 +0,0 @@ -package zero - -import ( - "database/sql/driver" - "encoding/json" - "fmt" - "reflect" - "strconv" - - "gopkg.in/nullbio/null.v5/convert" -) - -type NullUint16 struct { - Uint16 uint16 - Valid bool -} - -// Uint16 is a nullable uint16. -// JSON marshals to zero if null. -// Considered null to SQL if zero. -type Uint16 struct { - NullUint16 -} - -// NewUint16 creates a new Uint16 -func NewUint16(i uint16, valid bool) Uint16 { - return Uint16{ - NullUint16: NullUint16{ - Uint16: i, - Valid: valid, - }, - } -} - -// Uint16From creates a new Uint16 that will be null if zero. -func Uint16From(i uint16) Uint16 { - return NewUint16(i, i != 0) -} - -// Uint16FromPtr creates a new Uint16 that be null if i is nil. -func Uint16FromPtr(i *uint16) Uint16 { - if i == nil { - return NewUint16(0, false) - } - n := NewUint16(*i, true) - return n -} - -// UnmarshalJSON implements json.Unmarshaler. -// It supports number and null input. -// 0 will be considered a null Uint16. -// It also supports unmarshalling a sql.NullUint16. -func (i *Uint16) UnmarshalJSON(data []byte) error { - var err error - var v interface{} - if err = json.Unmarshal(data, &v); err != nil { - return err - } - switch v.(type) { - case float64: - // Unmarshal again, directly to uint16, to avoid intermediate float64 - err = json.Unmarshal(data, &i.Uint16) - case map[string]interface{}: - err = json.Unmarshal(data, &i.NullUint16) - case nil: - i.Valid = false - return nil - default: - err = fmt.Errorf("json: cannot unmarshal %v into Go value of type zero.Uint16", reflect.TypeOf(v).Name()) - } - i.Valid = (err == nil) && (i.Uint16 != 0) - return err -} - -// UnmarshalText implements encoding.TextUnmarshaler. -// It will unmarshal to a null Uint16 if the input is a blank, zero, or not an integer. -// It will return an error if the input is not an integer, blank, or "null". -func (i *Uint16) UnmarshalText(text []byte) error { - str := string(text) - if str == "" || str == "null" { - i.Valid = false - return nil - } - var err error - res, err := strconv.ParseUint(string(text), 10, 16) - i.Uint16 = uint16(res) - i.Valid = (err == nil) && (i.Uint16 != 0) - return err -} - -// MarshalJSON implements json.Marshaler. -// It will encode 0 if this Uint16 is null. -func (i Uint16) MarshalJSON() ([]byte, error) { - n := i.Uint16 - if !i.Valid { - n = 0 - } - return []byte(strconv.FormatUint(uint64(n), 10)), nil -} - -// MarshalText implements encoding.TextMarshaler. -// It will encode a zero if this Uint16 is null. -func (i Uint16) MarshalText() ([]byte, error) { - n := i.Uint16 - if !i.Valid { - n = 0 - } - return []byte(strconv.FormatUint(uint64(n), 10)), nil -} - -// SetValid changes this Uint16's value and also sets it to be non-null. -func (i *Uint16) SetValid(n uint16) { - i.Uint16 = n - i.Valid = true -} - -// Ptr returns a pointer to this Uint16's value, or a nil pointer if this Uint16 is null. -func (i Uint16) Ptr() *uint16 { - if !i.Valid { - return nil - } - return &i.Uint16 -} - -// IsZero returns true for null or zero Uint16s, for future omitempty support (Go 1.4?) -func (i Uint16) IsZero() bool { - return !i.Valid || i.Uint16 == 0 -} - -// Scan implements the Scanner interface. -func (n *NullUint16) Scan(value interface{}) error { - if value == nil { - n.Uint16, n.Valid = 0, false - return nil - } - n.Valid = true - return convert.ConvertAssign(&n.Uint16, value) -} - -// Value implements the driver Valuer interface. -func (n NullUint16) Value() (driver.Value, error) { - if !n.Valid { - return nil, nil - } - return int64(n.Uint16), nil -} diff --git a/zero/uint16_test.go b/zero/uint16_test.go deleted file mode 100644 index 49d3300..0000000 --- a/zero/uint16_test.go +++ /dev/null @@ -1,207 +0,0 @@ -package zero - -import ( - "encoding/json" - "math" - "strconv" - "testing" -) - -var ( - uint16JSON = []byte(`65534`) - nullUint16JSON = []byte(`{"Uint16":65534,"Valid":true}`) - zeroU16JSON = []byte(`0`) -) - -func TestUint16From(t *testing.T) { - i := Uint16From(65534) - assertUint16(t, i, "Uint16From()") - - zero := Uint16From(0) - if zero.Valid { - t.Error("Uint16From(0)", "is valid, but should be invalid") - } -} - -func TestUint16FromPtr(t *testing.T) { - n := uint16(65534) - iptr := &n - i := Uint16FromPtr(iptr) - assertUint16(t, i, "Uint16FromPtr()") - - null := Uint16FromPtr(nil) - assertNullUint16(t, null, "Uint16FromPtr(nil)") -} - -func TestUnmarshalUint16(t *testing.T) { - var i Uint16 - err := json.Unmarshal(uint16JSON, &i) - maybePanic(err) - assertUint16(t, i, "int json") - - var ni Uint16 - err = json.Unmarshal(nullUint16JSON, &ni) - maybePanic(err) - assertUint16(t, ni, "sql.NullUint16 json") - - var zero Uint16 - err = json.Unmarshal(zeroU16JSON, &zero) - maybePanic(err) - assertNullUint16(t, zero, "zero json") - - var null Uint16 - err = json.Unmarshal(nullJSON, &null) - maybePanic(err) - assertNullUint16(t, null, "null json") - - var badType Uint16 - err = json.Unmarshal(boolJSON, &badType) - if err == nil { - panic("err should not be nil") - } - assertNullUint16(t, badType, "wrong type json") - - var invalid Uint16 - err = invalid.UnmarshalJSON(invalidJSON) - if _, ok := err.(*json.SyntaxError); !ok { - t.Errorf("expected json.SyntaxError, not %T", err) - } - assertNullUint16(t, invalid, "invalid json") -} - -func TestUnmarshalNonIntegerNumberU16(t *testing.T) { - var i Uint16 - err := json.Unmarshal(floatJSON, &i) - if err == nil { - panic("err should be present; non-integer number coerced to int") - } -} - -func TestUnmarshalUint16Overflow(t *testing.T) { - int64Overflow := uint64(math.MaxUint16) - - // Max int64 should decode successfully - var i Uint16 - err := json.Unmarshal([]byte(strconv.FormatUint(int64Overflow, 10)), &i) - maybePanic(err) - - // Attempt to overflow - int64Overflow++ - err = json.Unmarshal([]byte(strconv.FormatUint(int64Overflow, 10)), &i) - if err == nil { - panic("err should be present; decoded value overflows int64") - } -} - -func TestTextUnmarshalUint16(t *testing.T) { - var i Uint16 - err := i.UnmarshalText([]byte("65534")) - maybePanic(err) - assertUint16(t, i, "UnmarshalText() int") - - var zero Uint16 - err = zero.UnmarshalText([]byte("0")) - maybePanic(err) - assertNullUint16(t, zero, "UnmarshalText() zero int") - - var blank Uint16 - err = blank.UnmarshalText([]byte("")) - maybePanic(err) - assertNullUint16(t, blank, "UnmarshalText() empty int") - - var null Uint16 - err = null.UnmarshalText([]byte("null")) - maybePanic(err) - assertNullUint16(t, null, `UnmarshalText() "null"`) -} - -func TestMarshalUint16(t *testing.T) { - i := Uint16From(65534) - data, err := json.Marshal(i) - maybePanic(err) - assertJSONEquals(t, data, "65534", "non-empty json marshal") - - // invalid values should be encoded as 0 - null := NewUint16(0, false) - data, err = json.Marshal(null) - maybePanic(err) - assertJSONEquals(t, data, "0", "null json marshal") -} - -func TestMarshalUint16Text(t *testing.T) { - i := Uint16From(65534) - data, err := i.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "65534", "non-empty text marshal") - - // invalid values should be encoded as zero - null := NewUint16(0, false) - data, err = null.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "0", "null text marshal") -} - -func TestUint16Pointer(t *testing.T) { - i := Uint16From(65534) - ptr := i.Ptr() - if *ptr != 65534 { - t.Errorf("bad %s int: %#v ≠ %d\n", "pointer", ptr, 65534) - } - - null := NewUint16(0, false) - ptr = null.Ptr() - if ptr != nil { - t.Errorf("bad %s int: %#v ≠ %s\n", "nil pointer", ptr, "nil") - } -} - -func TestUint16IsZero(t *testing.T) { - i := Uint16From(65534) - if i.IsZero() { - t.Errorf("IsZero() should be false") - } - - null := NewUint16(0, false) - if !null.IsZero() { - t.Errorf("IsZero() should be true") - } - - zero := NewUint16(0, true) - if !zero.IsZero() { - t.Errorf("IsZero() should be true") - } -} - -func TestUint16Scan(t *testing.T) { - var i Uint16 - err := i.Scan(65534) - maybePanic(err) - assertUint16(t, i, "scanned int") - - var null Uint16 - err = null.Scan(nil) - maybePanic(err) - assertNullUint16(t, null, "scanned null") -} - -func TestUint16SetValid(t *testing.T) { - change := NewUint16(0, false) - assertNullUint16(t, change, "SetValid()") - change.SetValid(65534) - assertUint16(t, change, "SetValid()") -} - -func assertUint16(t *testing.T, i Uint16, from string) { - if i.Uint16 != 65534 { - t.Errorf("bad %s int: %d ≠ %d\n", from, i.Uint16, 65534) - } - if !i.Valid { - t.Error(from, "is invalid, but should be valid") - } -} - -func assertNullUint16(t *testing.T, i Uint16, from string) { - if i.Valid { - t.Error(from, "is valid, but should be invalid") - } -} diff --git a/zero/uint32.go b/zero/uint32.go deleted file mode 100644 index aa3ac5d..0000000 --- a/zero/uint32.go +++ /dev/null @@ -1,146 +0,0 @@ -package zero - -import ( - "database/sql/driver" - "encoding/json" - "fmt" - "reflect" - "strconv" - - "gopkg.in/nullbio/null.v5/convert" -) - -type NullUint32 struct { - Uint32 uint32 - Valid bool -} - -// Uint32 is a nullable uint32. -// JSON marshals to zero if null. -// Considered null to SQL if zero. -type Uint32 struct { - NullUint32 -} - -// NewUint32 creates a new Uint32 -func NewUint32(i uint32, valid bool) Uint32 { - return Uint32{ - NullUint32: NullUint32{ - Uint32: i, - Valid: valid, - }, - } -} - -// Uint32From creates a new Uint32 that will be null if zero. -func Uint32From(i uint32) Uint32 { - return NewUint32(i, i != 0) -} - -// Uint32FromPtr creates a new Uint32 that be null if i is nil. -func Uint32FromPtr(i *uint32) Uint32 { - if i == nil { - return NewUint32(0, false) - } - n := NewUint32(*i, true) - return n -} - -// UnmarshalJSON implements json.Unmarshaler. -// It supports number and null input. -// 0 will be considered a null Uint32. -// It also supports unmarshalling a sql.NullUint32. -func (i *Uint32) UnmarshalJSON(data []byte) error { - var err error - var v interface{} - if err = json.Unmarshal(data, &v); err != nil { - return err - } - switch v.(type) { - case float64: - // Unmarshal again, directly to uint32, to avoid intermediate float64 - err = json.Unmarshal(data, &i.Uint32) - case map[string]interface{}: - err = json.Unmarshal(data, &i.NullUint32) - case nil: - i.Valid = false - return nil - default: - err = fmt.Errorf("json: cannot unmarshal %v into Go value of type zero.Uint32", reflect.TypeOf(v).Name()) - } - i.Valid = (err == nil) && (i.Uint32 != 0) - return err -} - -// UnmarshalText implements encoding.TextUnmarshaler. -// It will unmarshal to a null Uint32 if the input is a blank, zero, or not an integer. -// It will return an error if the input is not an integer, blank, or "null". -func (i *Uint32) UnmarshalText(text []byte) error { - str := string(text) - if str == "" || str == "null" { - i.Valid = false - return nil - } - var err error - res, err := strconv.ParseUint(string(text), 10, 32) - i.Uint32 = uint32(res) - i.Valid = (err == nil) && (i.Uint32 != 0) - return err -} - -// MarshalJSON implements json.Marshaler. -// It will encode 0 if this Uint32 is null. -func (i Uint32) MarshalJSON() ([]byte, error) { - n := i.Uint32 - if !i.Valid { - n = 0 - } - return []byte(strconv.FormatUint(uint64(n), 10)), nil -} - -// MarshalText implements encoding.TextMarshaler. -// It will encode a zero if this Uint32 is null. -func (i Uint32) MarshalText() ([]byte, error) { - n := i.Uint32 - if !i.Valid { - n = 0 - } - return []byte(strconv.FormatUint(uint64(n), 10)), nil -} - -// SetValid changes this Uint32's value and also sets it to be non-null. -func (i *Uint32) SetValid(n uint32) { - i.Uint32 = n - i.Valid = true -} - -// Ptr returns a pointer to this Uint32's value, or a nil pointer if this Uint32 is null. -func (i Uint32) Ptr() *uint32 { - if !i.Valid { - return nil - } - return &i.Uint32 -} - -// IsZero returns true for null or zero Uint32s, for future omitempty support (Go 1.4?) -func (i Uint32) IsZero() bool { - return !i.Valid || i.Uint32 == 0 -} - -// Scan implements the Scanner interface. -func (n *NullUint32) Scan(value interface{}) error { - if value == nil { - n.Uint32, n.Valid = 0, false - return nil - } - n.Valid = true - return convert.ConvertAssign(&n.Uint32, value) -} - -// Value implements the driver Valuer interface. -func (n NullUint32) Value() (driver.Value, error) { - if !n.Valid { - return nil, nil - } - return int64(n.Uint32), nil -} diff --git a/zero/uint32_test.go b/zero/uint32_test.go deleted file mode 100644 index 29e07a0..0000000 --- a/zero/uint32_test.go +++ /dev/null @@ -1,207 +0,0 @@ -package zero - -import ( - "encoding/json" - "math" - "strconv" - "testing" -) - -var ( - uint32JSON = []byte(`4294967294`) - nullUint32JSON = []byte(`{"Uint32":4294967294,"Valid":true}`) - zeroU32JSON = []byte(`0`) -) - -func TestUint32From(t *testing.T) { - i := Uint32From(4294967294) - assertUint32(t, i, "Uint32From()") - - zero := Uint32From(0) - if zero.Valid { - t.Error("Uint32From(0)", "is valid, but should be invalid") - } -} - -func TestUint32FromPtr(t *testing.T) { - n := uint32(4294967294) - iptr := &n - i := Uint32FromPtr(iptr) - assertUint32(t, i, "Uint32FromPtr()") - - null := Uint32FromPtr(nil) - assertNullUint32(t, null, "Uint32FromPtr(nil)") -} - -func TestUnmarshalUint32(t *testing.T) { - var i Uint32 - err := json.Unmarshal(uint32JSON, &i) - maybePanic(err) - assertUint32(t, i, "int json") - - var ni Uint32 - err = json.Unmarshal(nullUint32JSON, &ni) - maybePanic(err) - assertUint32(t, ni, "sql.NullUint32 json") - - var zero Uint32 - err = json.Unmarshal(zeroU32JSON, &zero) - maybePanic(err) - assertNullUint32(t, zero, "zero json") - - var null Uint32 - err = json.Unmarshal(nullJSON, &null) - maybePanic(err) - assertNullUint32(t, null, "null json") - - var badType Uint32 - err = json.Unmarshal(boolJSON, &badType) - if err == nil { - panic("err should not be nil") - } - assertNullUint32(t, badType, "wrong type json") - - var invalid Uint32 - err = invalid.UnmarshalJSON(invalidJSON) - if _, ok := err.(*json.SyntaxError); !ok { - t.Errorf("expected json.SyntaxError, not %T", err) - } - assertNullUint32(t, invalid, "invalid json") -} - -func TestUnmarshalNonIntegerNumberU32(t *testing.T) { - var i Uint32 - err := json.Unmarshal(floatJSON, &i) - if err == nil { - panic("err should be present; non-integer number coerced to int") - } -} - -func TestUnmarshalUint32Overflow(t *testing.T) { - int64Overflow := uint64(math.MaxUint32) - - // Max int64 should decode successfully - var i Uint32 - err := json.Unmarshal([]byte(strconv.FormatUint(int64Overflow, 10)), &i) - maybePanic(err) - - // Attempt to overflow - int64Overflow++ - err = json.Unmarshal([]byte(strconv.FormatUint(int64Overflow, 10)), &i) - if err == nil { - panic("err should be present; decoded value overflows int64") - } -} - -func TestTextUnmarshalUint32(t *testing.T) { - var i Uint32 - err := i.UnmarshalText([]byte("4294967294")) - maybePanic(err) - assertUint32(t, i, "UnmarshalText() int") - - var zero Uint32 - err = zero.UnmarshalText([]byte("0")) - maybePanic(err) - assertNullUint32(t, zero, "UnmarshalText() zero int") - - var blank Uint32 - err = blank.UnmarshalText([]byte("")) - maybePanic(err) - assertNullUint32(t, blank, "UnmarshalText() empty int") - - var null Uint32 - err = null.UnmarshalText([]byte("null")) - maybePanic(err) - assertNullUint32(t, null, `UnmarshalText() "null"`) -} - -func TestMarshalUint32(t *testing.T) { - i := Uint32From(4294967294) - data, err := json.Marshal(i) - maybePanic(err) - assertJSONEquals(t, data, "4294967294", "non-empty json marshal") - - // invalid values should be encoded as 0 - null := NewUint32(0, false) - data, err = json.Marshal(null) - maybePanic(err) - assertJSONEquals(t, data, "0", "null json marshal") -} - -func TestMarshalUint32Text(t *testing.T) { - i := Uint32From(4294967294) - data, err := i.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "4294967294", "non-empty text marshal") - - // invalid values should be encoded as zero - null := NewUint32(0, false) - data, err = null.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "0", "null text marshal") -} - -func TestUint32Pointer(t *testing.T) { - i := Uint32From(4294967294) - ptr := i.Ptr() - if *ptr != 4294967294 { - t.Errorf("bad %s int: %#v ≠ %d\n", "pointer", ptr, 4294967294) - } - - null := NewUint32(0, false) - ptr = null.Ptr() - if ptr != nil { - t.Errorf("bad %s int: %#v ≠ %s\n", "nil pointer", ptr, "nil") - } -} - -func TestUint32IsZero(t *testing.T) { - i := Uint32From(4294967294) - if i.IsZero() { - t.Errorf("IsZero() should be false") - } - - null := NewUint32(0, false) - if !null.IsZero() { - t.Errorf("IsZero() should be true") - } - - zero := NewUint32(0, true) - if !zero.IsZero() { - t.Errorf("IsZero() should be true") - } -} - -func TestUint32Scan(t *testing.T) { - var i Uint32 - err := i.Scan(4294967294) - maybePanic(err) - assertUint32(t, i, "scanned int") - - var null Uint32 - err = null.Scan(nil) - maybePanic(err) - assertNullUint32(t, null, "scanned null") -} - -func TestUint32SetValid(t *testing.T) { - change := NewUint32(0, false) - assertNullUint32(t, change, "SetValid()") - change.SetValid(4294967294) - assertUint32(t, change, "SetValid()") -} - -func assertUint32(t *testing.T, i Uint32, from string) { - if i.Uint32 != 4294967294 { - t.Errorf("bad %s int: %d ≠ %d\n", from, i.Uint32, 4294967294) - } - if !i.Valid { - t.Error(from, "is invalid, but should be valid") - } -} - -func assertNullUint32(t *testing.T, i Uint32, from string) { - if i.Valid { - t.Error(from, "is valid, but should be invalid") - } -} diff --git a/zero/uint64.go b/zero/uint64.go deleted file mode 100644 index f4c6900..0000000 --- a/zero/uint64.go +++ /dev/null @@ -1,146 +0,0 @@ -package zero - -import ( - "database/sql/driver" - "encoding/json" - "fmt" - "reflect" - "strconv" - - "gopkg.in/nullbio/null.v5/convert" -) - -// NullUint64 is a replica of sql.NullInt64 for uint64 types. -type NullUint64 struct { - Uint64 uint64 - Valid bool -} - -// Uint64 is an nullable uint64. -// It does not consider zero values to be null. -// It will decode to null, not zero, if null. -type Uint64 struct { - NullUint64 -} - -// NewUint64 creates a new Uint64 -func NewUint64(i uint64, valid bool) Uint64 { - return Uint64{ - NullUint64: NullUint64{ - Uint64: i, - Valid: valid, - }, - } -} - -// Uint64From creates a new Uint64 that will be null if zero. -func Uint64From(i uint64) Uint64 { - return NewUint64(i, i != 0) -} - -// Uint64FromPtr creates a new Uint64 that be null if i is nil. -func Uint64FromPtr(i *uint64) Uint64 { - if i == nil { - return NewUint64(0, false) - } - n := NewUint64(*i, true) - return n -} - -// UnmarshalJSON implements json.Unmarshaler. -// It supports number and null input. -// 0 will be considered a null Uint64. -// It also supports unmarshalling a sql.NullUint64. -func (i *Uint64) UnmarshalJSON(data []byte) error { - var err error - var v interface{} - if err = json.Unmarshal(data, &v); err != nil { - return err - } - switch v.(type) { - case float64: - // Unmarshal again, directly to uint64, to avoid intermediate float64 - err = json.Unmarshal(data, &i.Uint64) - case map[string]interface{}: - err = json.Unmarshal(data, &i.NullUint64) - case nil: - i.Valid = false - return nil - default: - err = fmt.Errorf("json: cannot unmarshal %v into Go value of type zero.Uint64", reflect.TypeOf(v).Name()) - } - i.Valid = (err == nil) && (i.Uint64 != 0) - return err -} - -// UnmarshalText implements encoding.TextUnmarshaler. -// It will unmarshal to a null Uint64 if the input is a blank, zero, or not an integer. -// It will return an error if the input is not an integer, blank, or "null". -func (i *Uint64) UnmarshalText(text []byte) error { - str := string(text) - if str == "" || str == "null" { - i.Valid = false - return nil - } - var err error - i.Uint64, err = strconv.ParseUint(string(text), 10, 64) - i.Valid = (err == nil) && (i.Uint64 != 0) - return err -} - -// MarshalJSON implements json.Marshaler. -// It will encode 0 if this Uint64 is null. -func (i Uint64) MarshalJSON() ([]byte, error) { - n := i.Uint64 - if !i.Valid { - n = 0 - } - return []byte(strconv.FormatUint(n, 10)), nil -} - -// MarshalText implements encoding.TextMarshaler. -// It will encode a zero if this Uint64 is null. -func (i Uint64) MarshalText() ([]byte, error) { - n := i.Uint64 - if !i.Valid { - n = 0 - } - return []byte(strconv.FormatUint(n, 10)), nil -} - -// SetValid changes this Uint64's value and also sets it to be non-null. -func (i *Uint64) SetValid(n uint64) { - i.Uint64 = n - i.Valid = true -} - -// Ptr returns a pointer to this Uint64's value, or a nil pointer if this Uint64 is null. -func (i Uint64) Ptr() *uint64 { - if !i.Valid { - return nil - } - return &i.Uint64 -} - -// IsZero returns true for null or zero Uint64s, for future omitempty support (Go 1.4?) -func (i Uint64) IsZero() bool { - return !i.Valid || i.Uint64 == 0 -} - -// Scan implements the Scanner interface. -func (n *NullUint64) Scan(value interface{}) error { - if value == nil { - n.Uint64, n.Valid = 0, false - return nil - } - n.Valid = true - return convert.ConvertAssign(&n.Uint64, value) -} - -// Value implements the driver Valuer interface. -func (n NullUint64) Value() (driver.Value, error) { - if !n.Valid { - return nil, nil - } - return int64(n.Uint64), nil -} diff --git a/zero/uint64_test.go b/zero/uint64_test.go deleted file mode 100644 index bdf261f..0000000 --- a/zero/uint64_test.go +++ /dev/null @@ -1,189 +0,0 @@ -package zero - -import ( - "encoding/json" - "testing" -) - -var ( - uint64JSON = []byte(`18446744073709551614`) - nullUint64JSON = []byte(`{"Uint64":18446744073709551614,"Valid":true}`) - zeroU64JSON = []byte(`0`) -) - -func TestUint64From(t *testing.T) { - i := Uint64From(18446744073709551614) - assertUint64(t, i, "Uint64From()") - - zero := Uint64From(0) - if zero.Valid { - t.Error("Uint64From(0)", "is valid, but should be invalid") - } -} - -func TestUint64FromPtr(t *testing.T) { - n := uint64(18446744073709551614) - iptr := &n - i := Uint64FromPtr(iptr) - assertUint64(t, i, "Uint64FromPtr()") - - null := Uint64FromPtr(nil) - assertNullUint64(t, null, "Uint64FromPtr(nil)") -} - -func TestUnmarshalUint64(t *testing.T) { - var i Uint64 - err := json.Unmarshal(uint64JSON, &i) - maybePanic(err) - assertUint64(t, i, "int json") - - var ni Uint64 - err = json.Unmarshal(nullUint64JSON, &ni) - maybePanic(err) - assertUint64(t, ni, "sql.NullUint64 json") - - var zero Uint64 - err = json.Unmarshal(zeroU64JSON, &zero) - maybePanic(err) - assertNullUint64(t, zero, "zero json") - - var null Uint64 - err = json.Unmarshal(nullJSON, &null) - maybePanic(err) - assertNullUint64(t, null, "null json") - - var badType Uint64 - err = json.Unmarshal(boolJSON, &badType) - if err == nil { - panic("err should not be nil") - } - assertNullUint64(t, badType, "wrong type json") - - var invalid Uint64 - err = invalid.UnmarshalJSON(invalidJSON) - if _, ok := err.(*json.SyntaxError); !ok { - t.Errorf("expected json.SyntaxError, not %T", err) - } - assertNullUint64(t, invalid, "invalid json") -} - -func TestUnmarshalNonIntegerNumberU64(t *testing.T) { - var i Uint64 - err := json.Unmarshal(floatJSON, &i) - if err == nil { - panic("err should be present; non-integer number coerced to int") - } -} - -func TestTextUnmarshalUint64(t *testing.T) { - var i Uint64 - err := i.UnmarshalText([]byte("18446744073709551614")) - maybePanic(err) - assertUint64(t, i, "UnmarshalText() int") - - var zero Uint64 - err = zero.UnmarshalText([]byte("0")) - maybePanic(err) - assertNullUint64(t, zero, "UnmarshalText() zero int") - - var blank Uint64 - err = blank.UnmarshalText([]byte("")) - maybePanic(err) - assertNullUint64(t, blank, "UnmarshalText() empty int") - - var null Uint64 - err = null.UnmarshalText([]byte("null")) - maybePanic(err) - assertNullUint64(t, null, `UnmarshalText() "null"`) -} - -func TestMarshalUint64(t *testing.T) { - i := Uint64From(18446744073709551614) - data, err := json.Marshal(i) - maybePanic(err) - assertJSONEquals(t, data, "18446744073709551614", "non-empty json marshal") - - // invalid values should be encoded as 0 - null := NewUint64(0, false) - data, err = json.Marshal(null) - maybePanic(err) - assertJSONEquals(t, data, "0", "null json marshal") -} - -func TestMarshalUint64Text(t *testing.T) { - i := Uint64From(18446744073709551614) - data, err := i.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "18446744073709551614", "non-empty text marshal") - - // invalid values should be encoded as zero - null := NewUint64(0, false) - data, err = null.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "0", "null text marshal") -} - -func TestUint64Pointer(t *testing.T) { - i := Uint64From(18446744073709551614) - ptr := i.Ptr() - if *ptr != 18446744073709551614 { - t.Errorf("bad %s int: %#v ≠ %d\n", "pointer", ptr, uint64(18446744073709551614)) - } - - null := NewUint64(0, false) - ptr = null.Ptr() - if ptr != nil { - t.Errorf("bad %s int: %#v ≠ %s\n", "nil pointer", ptr, "nil") - } -} - -func TestUint64IsZero(t *testing.T) { - i := Uint64From(18446744073709551614) - if i.IsZero() { - t.Errorf("IsZero() should be false") - } - - null := NewUint64(0, false) - if !null.IsZero() { - t.Errorf("IsZero() should be true") - } - - zero := NewUint64(0, true) - if !zero.IsZero() { - t.Errorf("IsZero() should be true") - } -} - -func TestUint64Scan(t *testing.T) { - var i Uint64 - err := i.Scan(uint64(18446744073709551614)) - maybePanic(err) - assertUint64(t, i, "scanned int") - - var null Uint64 - err = null.Scan(nil) - maybePanic(err) - assertNullUint64(t, null, "scanned null") -} - -func TestUint64SetValid(t *testing.T) { - change := NewUint64(0, false) - assertNullUint64(t, change, "SetValid()") - change.SetValid(18446744073709551614) - assertUint64(t, change, "SetValid()") -} - -func assertUint64(t *testing.T, i Uint64, from string) { - if i.Uint64 != 18446744073709551614 { - t.Errorf("bad %s int: %d ≠ %d\n", from, i.Uint64, uint64(18446744073709551614)) - } - if !i.Valid { - t.Error(from, "is invalid, but should be valid") - } -} - -func assertNullUint64(t *testing.T, i Uint64, from string) { - if i.Valid { - t.Error(from, "is valid, but should be invalid") - } -} diff --git a/zero/uint8.go b/zero/uint8.go deleted file mode 100644 index f49b3d7..0000000 --- a/zero/uint8.go +++ /dev/null @@ -1,146 +0,0 @@ -package zero - -import ( - "database/sql/driver" - "encoding/json" - "fmt" - "reflect" - "strconv" - - "gopkg.in/nullbio/null.v5/convert" -) - -type NullUint8 struct { - Uint8 uint8 - Valid bool -} - -// Uint8 is a nullable uint8. -// JSON marshals to zero if null. -// Considered null to SQL if zero. -type Uint8 struct { - NullUint8 -} - -// NewUint8 creates a new Uint8 -func NewUint8(i uint8, valid bool) Uint8 { - return Uint8{ - NullUint8: NullUint8{ - Uint8: i, - Valid: valid, - }, - } -} - -// Uint8From creates a new Uint8 that will be null if zero. -func Uint8From(i uint8) Uint8 { - return NewUint8(i, i != 0) -} - -// Uint8FromPtr creates a new Uint8 that be null if i is nil. -func Uint8FromPtr(i *uint8) Uint8 { - if i == nil { - return NewUint8(0, false) - } - n := NewUint8(*i, true) - return n -} - -// UnmarshalJSON implements json.Unmarshaler. -// It supports number and null input. -// 0 will be considered a null Uint8. -// It also supports unmarshalling a sql.NullUint8. -func (i *Uint8) UnmarshalJSON(data []byte) error { - var err error - var v interface{} - if err = json.Unmarshal(data, &v); err != nil { - return err - } - switch v.(type) { - case float64: - // Unmarshal again, directly to uint8, to avoid intermediate float64 - err = json.Unmarshal(data, &i.Uint8) - case map[string]interface{}: - err = json.Unmarshal(data, &i.NullUint8) - case nil: - i.Valid = false - return nil - default: - err = fmt.Errorf("json: cannot unmarshal %v into Go value of type zero.Uint8", reflect.TypeOf(v).Name()) - } - i.Valid = (err == nil) && (i.Uint8 != 0) - return err -} - -// UnmarshalText implements encoding.TextUnmarshaler. -// It will unmarshal to a null Uint8 if the input is a blank, zero, or not an integer. -// It will return an error if the input is not an integer, blank, or "null". -func (i *Uint8) UnmarshalText(text []byte) error { - str := string(text) - if str == "" || str == "null" { - i.Valid = false - return nil - } - var err error - res, err := strconv.ParseUint(string(text), 10, 8) - i.Uint8 = uint8(res) - i.Valid = (err == nil) && (i.Uint8 != 0) - return err -} - -// MarshalJSON implements json.Marshaler. -// It will encode 0 if this Uint8 is null. -func (i Uint8) MarshalJSON() ([]byte, error) { - n := i.Uint8 - if !i.Valid { - n = 0 - } - return []byte(strconv.FormatUint(uint64(n), 10)), nil -} - -// MarshalText implements encoding.TextMarshaler. -// It will encode a zero if this Uint8 is null. -func (i Uint8) MarshalText() ([]byte, error) { - n := i.Uint8 - if !i.Valid { - n = 0 - } - return []byte(strconv.FormatUint(uint64(n), 10)), nil -} - -// SetValid changes this Uint8's value and also sets it to be non-null. -func (i *Uint8) SetValid(n uint8) { - i.Uint8 = n - i.Valid = true -} - -// Ptr returns a pointer to this Uint8's value, or a nil pointer if this Uint8 is null. -func (i Uint8) Ptr() *uint8 { - if !i.Valid { - return nil - } - return &i.Uint8 -} - -// IsZero returns true for null or zero Uint8s, for future omitempty support (Go 1.4?) -func (i Uint8) IsZero() bool { - return !i.Valid || i.Uint8 == 0 -} - -// Scan implements the Scanner interface. -func (n *NullUint8) Scan(value interface{}) error { - if value == nil { - n.Uint8, n.Valid = 0, false - return nil - } - n.Valid = true - return convert.ConvertAssign(&n.Uint8, value) -} - -// Value implements the driver Valuer interface. -func (n NullUint8) Value() (driver.Value, error) { - if !n.Valid { - return nil, nil - } - return int64(n.Uint8), nil -} diff --git a/zero/uint8_test.go b/zero/uint8_test.go deleted file mode 100644 index 6e3cd82..0000000 --- a/zero/uint8_test.go +++ /dev/null @@ -1,207 +0,0 @@ -package zero - -import ( - "encoding/json" - "math" - "strconv" - "testing" -) - -var ( - uint8JSON = []byte(`254`) - nullUint8JSON = []byte(`{"Uint8":254,"Valid":true}`) - zeroU8JSON = []byte(`0`) -) - -func TestUint8From(t *testing.T) { - i := Uint8From(254) - assertUint8(t, i, "Uint8From()") - - zero := Uint8From(0) - if zero.Valid { - t.Error("Uint8From(0)", "is valid, but should be invalid") - } -} - -func TestUint8FromPtr(t *testing.T) { - n := uint8(254) - iptr := &n - i := Uint8FromPtr(iptr) - assertUint8(t, i, "Uint8FromPtr()") - - null := Uint8FromPtr(nil) - assertNullUint8(t, null, "Uint8FromPtr(nil)") -} - -func TestUnmarshalUint8(t *testing.T) { - var i Uint8 - err := json.Unmarshal(uint8JSON, &i) - maybePanic(err) - assertUint8(t, i, "int json") - - var ni Uint8 - err = json.Unmarshal(nullUint8JSON, &ni) - maybePanic(err) - assertUint8(t, ni, "sql.NullUint8 json") - - var zero Uint8 - err = json.Unmarshal(zeroU8JSON, &zero) - maybePanic(err) - assertNullUint8(t, zero, "zero json") - - var null Uint8 - err = json.Unmarshal(nullJSON, &null) - maybePanic(err) - assertNullUint8(t, null, "null json") - - var badType Uint8 - err = json.Unmarshal(boolJSON, &badType) - if err == nil { - panic("err should not be nil") - } - assertNullUint8(t, badType, "wrong type json") - - var invalid Uint8 - err = invalid.UnmarshalJSON(invalidJSON) - if _, ok := err.(*json.SyntaxError); !ok { - t.Errorf("expected json.SyntaxError, not %T", err) - } - assertNullUint8(t, invalid, "invalid json") -} - -func TestUnmarshalNonIntegerNumberU8(t *testing.T) { - var i Uint8 - err := json.Unmarshal(floatJSON, &i) - if err == nil { - panic("err should be present; non-integer number coerced to int") - } -} - -func TestUnmarshalUint8Overflow(t *testing.T) { - int64Overflow := uint64(math.MaxUint8) - - // Max int64 should decode successfully - var i Uint8 - err := json.Unmarshal([]byte(strconv.FormatUint(int64Overflow, 10)), &i) - maybePanic(err) - - // Attempt to overflow - int64Overflow++ - err = json.Unmarshal([]byte(strconv.FormatUint(int64Overflow, 10)), &i) - if err == nil { - panic("err should be present; decoded value overflows int64") - } -} - -func TestTextUnmarshalUint8(t *testing.T) { - var i Uint8 - err := i.UnmarshalText([]byte("254")) - maybePanic(err) - assertUint8(t, i, "UnmarshalText() int") - - var zero Uint8 - err = zero.UnmarshalText([]byte("0")) - maybePanic(err) - assertNullUint8(t, zero, "UnmarshalText() zero int") - - var blank Uint8 - err = blank.UnmarshalText([]byte("")) - maybePanic(err) - assertNullUint8(t, blank, "UnmarshalText() empty int") - - var null Uint8 - err = null.UnmarshalText([]byte("null")) - maybePanic(err) - assertNullUint8(t, null, `UnmarshalText() "null"`) -} - -func TestMarshalUint8(t *testing.T) { - i := Uint8From(254) - data, err := json.Marshal(i) - maybePanic(err) - assertJSONEquals(t, data, "254", "non-empty json marshal") - - // invalid values should be encoded as 0 - null := NewUint8(0, false) - data, err = json.Marshal(null) - maybePanic(err) - assertJSONEquals(t, data, "0", "null json marshal") -} - -func TestMarshalUint8Text(t *testing.T) { - i := Uint8From(254) - data, err := i.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "254", "non-empty text marshal") - - // invalid values should be encoded as zero - null := NewUint8(0, false) - data, err = null.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "0", "null text marshal") -} - -func TestUint8Pointer(t *testing.T) { - i := Uint8From(254) - ptr := i.Ptr() - if *ptr != 254 { - t.Errorf("bad %s int: %#v ≠ %d\n", "pointer", ptr, 254) - } - - null := NewUint8(0, false) - ptr = null.Ptr() - if ptr != nil { - t.Errorf("bad %s int: %#v ≠ %s\n", "nil pointer", ptr, "nil") - } -} - -func TestUint8IsZero(t *testing.T) { - i := Uint8From(254) - if i.IsZero() { - t.Errorf("IsZero() should be false") - } - - null := NewUint8(0, false) - if !null.IsZero() { - t.Errorf("IsZero() should be true") - } - - zero := NewUint8(0, true) - if !zero.IsZero() { - t.Errorf("IsZero() should be true") - } -} - -func TestUint8Scan(t *testing.T) { - var i Uint8 - err := i.Scan(254) - maybePanic(err) - assertUint8(t, i, "scanned int") - - var null Uint8 - err = null.Scan(nil) - maybePanic(err) - assertNullUint8(t, null, "scanned null") -} - -func TestUint8SetValid(t *testing.T) { - change := NewUint8(0, false) - assertNullUint8(t, change, "SetValid()") - change.SetValid(254) - assertUint8(t, change, "SetValid()") -} - -func assertUint8(t *testing.T, i Uint8, from string) { - if i.Uint8 != 254 { - t.Errorf("bad %s int: %d ≠ %d\n", from, i.Uint8, 254) - } - if !i.Valid { - t.Error(from, "is invalid, but should be valid") - } -} - -func assertNullUint8(t *testing.T, i Uint8, from string) { - if i.Valid { - t.Error(from, "is valid, but should be invalid") - } -} diff --git a/zero/uint_test.go b/zero/uint_test.go deleted file mode 100644 index 2f9dcfa..0000000 --- a/zero/uint_test.go +++ /dev/null @@ -1,189 +0,0 @@ -package zero - -import ( - "encoding/json" - "testing" -) - -var ( - uintJSON = []byte(`12345`) - nullUintJSON = []byte(`{"Uint":12345,"Valid":true}`) - zeroUJSON = []byte(`0`) -) - -func TestUintFrom(t *testing.T) { - i := UintFrom(12345) - assertUint(t, i, "UintFrom()") - - zero := UintFrom(0) - if zero.Valid { - t.Error("UintFrom(0)", "is valid, but should be invalid") - } -} - -func TestUintFromPtr(t *testing.T) { - n := uint(12345) - iptr := &n - i := UintFromPtr(iptr) - assertUint(t, i, "UintFromPtr()") - - null := UintFromPtr(nil) - assertNullUint(t, null, "UintFromPtr(nil)") -} - -func TestUnmarshalUint(t *testing.T) { - var i Uint - err := json.Unmarshal(uintJSON, &i) - maybePanic(err) - assertUint(t, i, "uint json") - - var ni Uint - err = json.Unmarshal(nullUintJSON, &ni) - maybePanic(err) - assertUint(t, ni, "sql.NullUint json") - - var zero Uint - err = json.Unmarshal(zeroUJSON, &zero) - maybePanic(err) - assertNullUint(t, zero, "zero json") - - var null Uint - err = json.Unmarshal(nullJSON, &null) - maybePanic(err) - assertNullUint(t, null, "null json") - - var badType Uint - err = json.Unmarshal(boolJSON, &badType) - if err == nil { - panic("err should not be nil") - } - assertNullUint(t, badType, "wrong type json") - - var invalid Uint - err = invalid.UnmarshalJSON(invalidJSON) - if _, ok := err.(*json.SyntaxError); !ok { - t.Errorf("expected json.SyntaxError, not %T", err) - } - assertNullUint(t, invalid, "invalid json") -} - -func TestUnmarshalNonUintegerNumber(t *testing.T) { - var i Uint - err := json.Unmarshal(floatJSON, &i) - if err == nil { - panic("err should be present; non-uinteger number coerced to uint") - } -} - -func TestTextUnmarshalUint(t *testing.T) { - var i Uint - err := i.UnmarshalText([]byte("12345")) - maybePanic(err) - assertUint(t, i, "UnmarshalText() uint") - - var zero Uint - err = zero.UnmarshalText([]byte("0")) - maybePanic(err) - assertNullUint(t, zero, "UnmarshalText() zero uint") - - var blank Uint - err = blank.UnmarshalText([]byte("")) - maybePanic(err) - assertNullUint(t, blank, "UnmarshalText() empty uint") - - var null Uint - err = null.UnmarshalText([]byte("null")) - maybePanic(err) - assertNullUint(t, null, `UnmarshalText() "null"`) -} - -func TestMarshalUint(t *testing.T) { - i := UintFrom(12345) - data, err := json.Marshal(i) - maybePanic(err) - assertJSONEquals(t, data, "12345", "non-empty json marshal") - - // invalid values should be encoded as 0 - null := NewUint(0, false) - data, err = json.Marshal(null) - maybePanic(err) - assertJSONEquals(t, data, "0", "null json marshal") -} - -func TestMarshalUintText(t *testing.T) { - i := UintFrom(12345) - data, err := i.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "12345", "non-empty text marshal") - - // invalid values should be encoded as zero - null := NewUint(0, false) - data, err = null.MarshalText() - maybePanic(err) - assertJSONEquals(t, data, "0", "null text marshal") -} - -func TestUintPouinter(t *testing.T) { - i := UintFrom(12345) - ptr := i.Ptr() - if *ptr != 12345 { - t.Errorf("bad %s uint: %#v ≠ %d\n", "pouinter", ptr, 12345) - } - - null := NewUint(0, false) - ptr = null.Ptr() - if ptr != nil { - t.Errorf("bad %s uint: %#v ≠ %s\n", "nil pouinter", ptr, "nil") - } -} - -func TestUintIsZero(t *testing.T) { - i := UintFrom(12345) - if i.IsZero() { - t.Errorf("IsZero() should be false") - } - - null := NewUint(0, false) - if !null.IsZero() { - t.Errorf("IsZero() should be true") - } - - zero := NewUint(0, true) - if !zero.IsZero() { - t.Errorf("IsZero() should be true") - } -} - -func TestUintScan(t *testing.T) { - var i Uint - err := i.Scan(12345) - maybePanic(err) - assertUint(t, i, "scanned uint") - - var null Uint - err = null.Scan(nil) - maybePanic(err) - assertNullUint(t, null, "scanned null") -} - -func TestUintSetValid(t *testing.T) { - change := NewUint(0, false) - assertNullUint(t, change, "SetValid()") - change.SetValid(12345) - assertUint(t, change, "SetValid()") -} - -func assertUint(t *testing.T, i Uint, from string) { - if i.Uint != 12345 { - t.Errorf("bad %s uint: %d ≠ %d\n", from, i.Uint, 12345) - } - if !i.Valid { - t.Error(from, "is invalid, but should be valid") - } -} - -func assertNullUint(t *testing.T, i Uint, from string) { - if i.Valid { - t.Error(from, "is valid, but should be invalid") - } -}