├── go.sum ├── .gitignore ├── go.mod ├── value.go ├── CHANGELOG.md ├── LICENSE ├── README.md ├── string.go ├── int64.go ├── int.go ├── uint.go ├── bytes.go ├── time.go ├── float64.go ├── bool.go ├── int8.go ├── int16.go ├── int32.go ├── byte.go ├── float32.go ├── uint8.go ├── uint16.go ├── uint32.go ├── uint64.go ├── byte_test.go ├── float32_test.go ├── float64_test.go ├── int_test.go ├── uint_test.go ├── bytes_test.go ├── time_test.go ├── uint64_test.go ├── int8_test.go ├── bool_test.go ├── uint8_test.go ├── int16_test.go ├── int32_test.go ├── uint16_test.go ├── uint32_test.go ├── json.go ├── int64_test.go ├── string_test.go ├── convert ├── convert.go └── convert_test.go └── json_test.go /go.sum: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage.out -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/aarondl/null/v9 2 | 3 | go 1.14 4 | -------------------------------------------------------------------------------- /value.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | // Value specifies methods that allow introspection into the 4 | // state of a value. 5 | type Value interface { 6 | // IsValid returns true iff the value is set and non-null 7 | IsValid() bool 8 | // IsSet returns true iff the value is set (null inclusive) 9 | IsSet() bool 10 | } 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 4 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 5 | 6 | ## [v9.0.0] 7 | 8 | ### Added 9 | 10 | - Add a new `.Set` that can be introspected to see if things are set. 11 | (thanks @razor-1) 12 | 13 | ### Fixed 14 | 15 | - Fix []byte to be base64 encoded in json to match how []byte is handled 16 | (thanks @razor-1) 17 | 18 | ## [v8.1.2] 19 | 20 | ### Fixed 21 | 22 | - Scan negative int64 values into uint64 without breaking 23 | 24 | ## [v8.1.1] 25 | 26 | ### Fixed 27 | 28 | - Fix an overflow issue in uint64 29 | 30 | ## [v8.1.0] 31 | 32 | ### Changed 33 | 34 | - Version the code with go modules 35 | 36 | ## [v8.0.0] 37 | 38 | ### Changed 39 | 40 | - Update code for new randomizer interface 41 | - Start versioning with semantic versions 42 | - Add tags for older versions (back to 7.1.0) 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright for portions of project null-extended are held by *Greg Roseberry, 2014* as part of project null. 2 | All other copyright for project null-extended are held by *Patrick O'Brien, 2016*. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## null [![GoDoc](https://godoc.org/github.com/aarondl/null?status.svg)](https://godoc.org/github.com/aarondl/null) [![Coverage](http://gocover.io/_badge/github.com/aarondl/null)](http://gocover.io/github.com/aarondl/null) 2 | 3 | `null` is a library with reasonable options for dealing with nullable SQL and 4 | JSON values. 5 | 6 | Types in `null` will only be considered null on null input, and will JSON 7 | encode to `null`. 8 | 9 | All types implement `sql.Scanner` and `driver.Valuer`, so you can use this 10 | library in place of `sql.NullXXX`. All types also implement: 11 | `encoding.TextMarshaler`, `encoding.TextUnmarshaler`, `json.Marshaler`, 12 | `json.Unmarshaler` and `sql.Scanner`. 13 | 14 | --- 15 | 16 | ### Installation 17 | 18 | Null used to be versioned with gopkg.in, so once you upgrade to v8 and beyond 19 | please stop using gopkg.in and ensure you're using go modules. 20 | 21 | ``` 22 | go get github.com/aarondl/null/v9 23 | ``` 24 | 25 | ### Usage 26 | 27 | The following are all types supported in this package. All types will marshal 28 | to JSON null if Invalid or SQL source data is null. 29 | 30 | | Type | Description | Notes | 31 | |------|-------------|-------| 32 | | `null.JSON` | Nullable `[]byte` | Will marshal to JSON null if Invalid. `[]byte{}` input will not produce an Invalid JSON, but `[]byte(nil)` will. This should be used for storing raw JSON in the database. Also has `null.JSON.Marshal` and `null.JSON.Unmarshal` helpers to marshal and unmarshal foreign objects. | 33 | | `null.Bytes` | Nullable `[]byte` | `[]byte{}` input will not produce an Invalid Bytes, but `[]byte(nil)` will. This should be used for storing binary data (bytes in PSQL for example) in the database. | 34 | | `null.String` | Nullable `string` | | 35 | | `null.Byte` | Nullable `byte` | | 36 | | `null.Bool` | Nullable `bool` | | 37 | | `null.Time` | Nullable `time.Time | Marshals to JSON null if SQL source data is null. Uses `time.Time`'s marshaler. | 38 | | `null.Float32` | Nullable `float32` | | 39 | | `null.Float64` | Nullable `float64` | | 40 | | `null.Int` | Nullable `int` | | 41 | | `null.Int8` | Nullable `int8` | | 42 | | `null.Int16` | Nullable `int16` | | 43 | | `null.Int32` | Nullable `int32` | | 44 | | `null.Int64` | Nullable `int64` | | 45 | | `null.Uint` | Nullable `uint` | | 46 | | `null.Uint8` | Nullable `uint8` | | 47 | | `null.Uint16` | Nullable `uint16` | | 48 | | `null.Uint32` | Nullable `uint32` | | 49 | | `null.Uint64` | Nullable `uint64` | | | 50 | 51 | ### Bugs 52 | 53 | `json`'s `",omitempty"` struct tag does not work correctly right now. It will 54 | never omit a null or empty String. This might be [fixed 55 | eventually](https://github.com/golang/go/issues/4357). 56 | 57 | 58 | ### License 59 | 60 | BSD-3 (See License file) 61 | -------------------------------------------------------------------------------- /string.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "bytes" 5 | "database/sql/driver" 6 | "encoding/json" 7 | 8 | "github.com/aarondl/null/v9/convert" 9 | ) 10 | 11 | // String is a nullable string. It supports SQL and JSON serialization. 12 | type String struct { 13 | String string 14 | Valid bool 15 | Set bool 16 | } 17 | 18 | // StringFrom creates a new String that will never be blank. 19 | func StringFrom(s string) String { 20 | return NewString(s, true) 21 | } 22 | 23 | // StringFromPtr creates a new String that be null if s is nil. 24 | func StringFromPtr(s *string) String { 25 | if s == nil { 26 | return NewString("", false) 27 | } 28 | return NewString(*s, true) 29 | } 30 | 31 | // NewString creates a new String 32 | func NewString(s string, valid bool) String { 33 | return String{ 34 | String: s, 35 | Valid: valid, 36 | Set: true, 37 | } 38 | } 39 | 40 | // IsValid returns true if this carries and explicit value and 41 | // is not null. 42 | func (s String) IsValid() bool { 43 | return s.Set && s.Valid 44 | } 45 | 46 | // IsSet returns true if this carries an explicit value (null inclusive) 47 | func (s String) IsSet() bool { 48 | return s.Set 49 | } 50 | 51 | // UnmarshalJSON implements json.Unmarshaler. 52 | func (s *String) UnmarshalJSON(data []byte) error { 53 | s.Set = true 54 | if bytes.Equal(data, NullBytes) { 55 | s.String = "" 56 | s.Valid = false 57 | return nil 58 | } 59 | 60 | if err := json.Unmarshal(data, &s.String); err != nil { 61 | return err 62 | } 63 | 64 | s.Valid = true 65 | return nil 66 | } 67 | 68 | // MarshalJSON implements json.Marshaler. 69 | func (s String) MarshalJSON() ([]byte, error) { 70 | if !s.Valid { 71 | return NullBytes, nil 72 | } 73 | return json.Marshal(s.String) 74 | } 75 | 76 | // MarshalText implements encoding.TextMarshaler. 77 | func (s String) MarshalText() ([]byte, error) { 78 | if !s.Valid { 79 | return []byte{}, nil 80 | } 81 | return []byte(s.String), nil 82 | } 83 | 84 | // UnmarshalText implements encoding.TextUnmarshaler. 85 | func (s *String) UnmarshalText(text []byte) error { 86 | s.Set = true 87 | if len(text) == 0 { 88 | s.Valid = false 89 | return nil 90 | } 91 | 92 | s.String = string(text) 93 | s.Valid = true 94 | return nil 95 | } 96 | 97 | // SetValid changes this String's value and also sets it to be non-null. 98 | func (s *String) SetValid(v string) { 99 | s.String = v 100 | s.Valid = true 101 | s.Set = true 102 | } 103 | 104 | // Ptr returns a pointer to this String's value, or a nil pointer if this String is null. 105 | func (s String) Ptr() *string { 106 | if !s.Valid { 107 | return nil 108 | } 109 | return &s.String 110 | } 111 | 112 | // IsZero returns true for null strings, for potential future omitempty support. 113 | func (s String) IsZero() bool { 114 | return !s.Valid 115 | } 116 | 117 | // Scan implements the Scanner interface. 118 | func (s *String) Scan(value interface{}) error { 119 | if value == nil { 120 | s.String, s.Valid, s.Set = "", false, false 121 | return nil 122 | } 123 | s.Valid, s.Set = true, true 124 | return convert.ConvertAssign(&s.String, value) 125 | } 126 | 127 | // Value implements the driver Valuer interface. 128 | func (s String) Value() (driver.Value, error) { 129 | if !s.Valid { 130 | return nil, nil 131 | } 132 | return s.String, nil 133 | } 134 | -------------------------------------------------------------------------------- /int64.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "bytes" 5 | "database/sql/driver" 6 | "encoding/json" 7 | "strconv" 8 | 9 | "github.com/aarondl/null/v9/convert" 10 | ) 11 | 12 | // Int64 is an nullable int64. 13 | type Int64 struct { 14 | Int64 int64 15 | Valid bool 16 | Set bool 17 | } 18 | 19 | // NewInt64 creates a new Int64 20 | func NewInt64(i int64, valid bool) Int64 { 21 | return Int64{ 22 | Int64: i, 23 | Valid: valid, 24 | Set: true, 25 | } 26 | } 27 | 28 | // Int64From creates a new Int64 that will always be valid. 29 | func Int64From(i int64) Int64 { 30 | return NewInt64(i, true) 31 | } 32 | 33 | // Int64FromPtr creates a new Int64 that be null if i is nil. 34 | func Int64FromPtr(i *int64) Int64 { 35 | if i == nil { 36 | return NewInt64(0, false) 37 | } 38 | return NewInt64(*i, true) 39 | } 40 | 41 | // IsValid returns true if this carries and explicit value and 42 | // is not null. 43 | func (i Int64) IsValid() bool { 44 | return i.Set && i.Valid 45 | } 46 | 47 | // IsSet returns true if this carries an explicit value (null inclusive) 48 | func (i Int64) IsSet() bool { 49 | return i.Set 50 | } 51 | 52 | // UnmarshalJSON implements json.Unmarshaler. 53 | func (i *Int64) UnmarshalJSON(data []byte) error { 54 | i.Set = true 55 | if bytes.Equal(data, NullBytes) { 56 | i.Valid = false 57 | i.Int64 = 0 58 | return nil 59 | } 60 | 61 | if err := json.Unmarshal(data, &i.Int64); err != nil { 62 | return err 63 | } 64 | 65 | i.Valid = true 66 | return nil 67 | } 68 | 69 | // UnmarshalText implements encoding.TextUnmarshaler. 70 | func (i *Int64) UnmarshalText(text []byte) error { 71 | i.Set = true 72 | if len(text) == 0 { 73 | i.Valid = false 74 | return nil 75 | } 76 | var err error 77 | i.Int64, err = strconv.ParseInt(string(text), 10, 64) 78 | i.Valid = err == nil 79 | return err 80 | } 81 | 82 | // MarshalJSON implements json.Marshaler. 83 | func (i Int64) MarshalJSON() ([]byte, error) { 84 | if !i.Valid { 85 | return NullBytes, nil 86 | } 87 | return []byte(strconv.FormatInt(i.Int64, 10)), nil 88 | } 89 | 90 | // MarshalText implements encoding.TextMarshaler. 91 | func (i Int64) MarshalText() ([]byte, error) { 92 | if !i.Valid { 93 | return []byte{}, nil 94 | } 95 | return []byte(strconv.FormatInt(i.Int64, 10)), nil 96 | } 97 | 98 | // SetValid changes this Int64's value and also sets it to be non-null. 99 | func (i *Int64) SetValid(n int64) { 100 | i.Int64 = n 101 | i.Valid = true 102 | i.Set = true 103 | } 104 | 105 | // Ptr returns a pointer to this Int64's value, or a nil pointer if this Int64 is null. 106 | func (i Int64) Ptr() *int64 { 107 | if !i.Valid { 108 | return nil 109 | } 110 | return &i.Int64 111 | } 112 | 113 | // IsZero returns true for invalid Int64's, for future omitempty support (Go 1.4?) 114 | func (i Int64) IsZero() bool { 115 | return !i.Valid 116 | } 117 | 118 | // Scan implements the Scanner interface. 119 | func (i *Int64) Scan(value interface{}) error { 120 | if value == nil { 121 | i.Int64, i.Valid, i.Set = 0, false, false 122 | return nil 123 | } 124 | i.Valid, i.Set = true, true 125 | return convert.ConvertAssign(&i.Int64, value) 126 | } 127 | 128 | // Value implements the driver Valuer interface. 129 | func (i Int64) Value() (driver.Value, error) { 130 | if !i.Valid { 131 | return nil, nil 132 | } 133 | return i.Int64, nil 134 | } 135 | -------------------------------------------------------------------------------- /int.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "bytes" 5 | "database/sql/driver" 6 | "encoding/json" 7 | "strconv" 8 | 9 | "github.com/aarondl/null/v9/convert" 10 | ) 11 | 12 | // Int is an nullable int. 13 | type Int struct { 14 | Int int 15 | Valid bool 16 | Set bool 17 | } 18 | 19 | // NewInt creates a new Int 20 | func NewInt(i int, valid bool) Int { 21 | return Int{ 22 | Int: i, 23 | Valid: valid, 24 | Set: true, 25 | } 26 | } 27 | 28 | // IntFrom creates a new Int that will always be valid. 29 | func IntFrom(i int) Int { 30 | return NewInt(i, true) 31 | } 32 | 33 | // IntFromPtr creates a new Int that be null if i is nil. 34 | func IntFromPtr(i *int) Int { 35 | if i == nil { 36 | return NewInt(0, false) 37 | } 38 | return NewInt(*i, true) 39 | } 40 | 41 | // IsValid returns true if this carries and explicit value and 42 | // is not null. 43 | func (i Int) IsValid() bool { 44 | return i.Set && i.Valid 45 | } 46 | 47 | // IsSet returns true if this carries an explicit value (null inclusive) 48 | func (i Int) IsSet() bool { 49 | return i.Set 50 | } 51 | 52 | // UnmarshalJSON implements json.Unmarshaler. 53 | func (i *Int) UnmarshalJSON(data []byte) error { 54 | i.Set = true 55 | 56 | if bytes.Equal(data, NullBytes) { 57 | i.Valid = false 58 | i.Int = 0 59 | return nil 60 | } 61 | 62 | var x int64 63 | if err := json.Unmarshal(data, &x); err != nil { 64 | return err 65 | } 66 | 67 | i.Int = int(x) 68 | i.Valid = true 69 | return nil 70 | } 71 | 72 | // UnmarshalText implements encoding.TextUnmarshaler. 73 | func (i *Int) UnmarshalText(text []byte) error { 74 | i.Set = true 75 | if len(text) == 0 { 76 | i.Valid = false 77 | return nil 78 | } 79 | var err error 80 | res, err := strconv.ParseInt(string(text), 10, 0) 81 | i.Valid = err == nil 82 | if i.Valid { 83 | i.Int = int(res) 84 | } 85 | return err 86 | } 87 | 88 | // MarshalJSON implements json.Marshaler. 89 | func (i Int) MarshalJSON() ([]byte, error) { 90 | if !i.Valid { 91 | return NullBytes, nil 92 | } 93 | return []byte(strconv.FormatInt(int64(i.Int), 10)), nil 94 | } 95 | 96 | // MarshalText implements encoding.TextMarshaler. 97 | func (i Int) MarshalText() ([]byte, error) { 98 | if !i.Valid { 99 | return []byte{}, nil 100 | } 101 | return []byte(strconv.FormatInt(int64(i.Int), 10)), nil 102 | } 103 | 104 | // SetValid changes this Int's value and also sets it to be non-null. 105 | func (i *Int) SetValid(n int) { 106 | i.Int = n 107 | i.Valid = true 108 | i.Set = true 109 | } 110 | 111 | // Ptr returns a pointer to this Int's value, or a nil pointer if this Int is null. 112 | func (i Int) Ptr() *int { 113 | if !i.Valid { 114 | return nil 115 | } 116 | return &i.Int 117 | } 118 | 119 | // IsZero returns true for invalid Ints, for future omitempty support (Go 1.4?) 120 | func (i Int) IsZero() bool { 121 | return !i.Valid 122 | } 123 | 124 | // Scan implements the Scanner interface. 125 | func (i *Int) Scan(value interface{}) error { 126 | if value == nil { 127 | i.Int, i.Valid, i.Set = 0, false, false 128 | return nil 129 | } 130 | i.Valid, i.Set = true, true 131 | return convert.ConvertAssign(&i.Int, value) 132 | } 133 | 134 | // Value implements the driver Valuer interface. 135 | func (i Int) Value() (driver.Value, error) { 136 | if !i.Valid { 137 | return nil, nil 138 | } 139 | return int64(i.Int), nil 140 | } 141 | -------------------------------------------------------------------------------- /uint.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "bytes" 5 | "database/sql/driver" 6 | "encoding/json" 7 | "strconv" 8 | 9 | "github.com/aarondl/null/v9/convert" 10 | ) 11 | 12 | // Uint is an nullable uint. 13 | type Uint struct { 14 | Uint uint 15 | Valid bool 16 | Set bool 17 | } 18 | 19 | // NewUint creates a new Uint 20 | func NewUint(i uint, valid bool) Uint { 21 | return Uint{ 22 | Uint: i, 23 | Valid: valid, 24 | Set: true, 25 | } 26 | } 27 | 28 | // UintFrom creates a new Uint that will always be valid. 29 | func UintFrom(i uint) Uint { 30 | return NewUint(i, true) 31 | } 32 | 33 | // UintFromPtr creates a new Uint that be null if i is nil. 34 | func UintFromPtr(i *uint) Uint { 35 | if i == nil { 36 | return NewUint(0, false) 37 | } 38 | return NewUint(*i, true) 39 | } 40 | 41 | // IsValid returns true if this carries and explicit value and 42 | // is not null. 43 | func (u Uint) IsValid() bool { 44 | return u.Set && u.Valid 45 | } 46 | 47 | // IsSet returns true if this carries an explicit value (null inclusive) 48 | func (u Uint) IsSet() bool { 49 | return u.Set 50 | } 51 | 52 | // UnmarshalJSON implements json.Unmarshaler. 53 | func (u *Uint) UnmarshalJSON(data []byte) error { 54 | u.Set = true 55 | if bytes.Equal(data, NullBytes) { 56 | u.Valid = false 57 | u.Uint = 0 58 | return nil 59 | } 60 | 61 | var x uint64 62 | if err := json.Unmarshal(data, &x); err != nil { 63 | return err 64 | } 65 | 66 | u.Uint = uint(x) 67 | u.Valid = true 68 | return nil 69 | } 70 | 71 | // UnmarshalText implements encoding.TextUnmarshaler. 72 | func (u *Uint) UnmarshalText(text []byte) error { 73 | u.Set = true 74 | if len(text) == 0 { 75 | u.Valid = false 76 | return nil 77 | } 78 | var err error 79 | res, err := strconv.ParseUint(string(text), 10, 0) 80 | u.Valid = err == nil 81 | if u.Valid { 82 | u.Uint = uint(res) 83 | } 84 | return err 85 | } 86 | 87 | // MarshalJSON implements json.Marshaler. 88 | func (u Uint) MarshalJSON() ([]byte, error) { 89 | if !u.Valid { 90 | return NullBytes, nil 91 | } 92 | return []byte(strconv.FormatUint(uint64(u.Uint), 10)), nil 93 | } 94 | 95 | // MarshalText implements encoding.TextMarshaler. 96 | func (u Uint) MarshalText() ([]byte, error) { 97 | if !u.Valid { 98 | return []byte{}, nil 99 | } 100 | return []byte(strconv.FormatUint(uint64(u.Uint), 10)), nil 101 | } 102 | 103 | // SetValid changes this Uint's value and also sets it to be non-null. 104 | func (u *Uint) SetValid(n uint) { 105 | u.Uint = n 106 | u.Valid = true 107 | u.Set = true 108 | } 109 | 110 | // Ptr returns a pointer to this Uint's value, or a nil pointer if this Uint is null. 111 | func (u Uint) Ptr() *uint { 112 | if !u.Valid { 113 | return nil 114 | } 115 | return &u.Uint 116 | } 117 | 118 | // IsZero returns true for invalid Uints, for future omitempty support (Go 1.4?) 119 | func (u Uint) IsZero() bool { 120 | return !u.Valid 121 | } 122 | 123 | // Scan implements the Scanner interface. 124 | func (u *Uint) Scan(value interface{}) error { 125 | if value == nil { 126 | u.Uint, u.Valid, u.Set = 0, false, false 127 | return nil 128 | } 129 | u.Valid, u.Set = true, true 130 | return convert.ConvertAssign(&u.Uint, value) 131 | } 132 | 133 | // Value implements the driver Valuer interface. 134 | func (u Uint) Value() (driver.Value, error) { 135 | if !u.Valid { 136 | return nil, nil 137 | } 138 | return int64(u.Uint), nil 139 | } 140 | -------------------------------------------------------------------------------- /bytes.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "bytes" 5 | "database/sql/driver" 6 | "encoding/json" 7 | 8 | "github.com/aarondl/null/v9/convert" 9 | ) 10 | 11 | // NullBytes is a global byte slice of JSON null 12 | var NullBytes = []byte("null") 13 | 14 | // Bytes is a nullable []byte. 15 | type Bytes struct { 16 | Bytes []byte 17 | Valid bool 18 | Set bool 19 | } 20 | 21 | // NewBytes creates a new Bytes 22 | func NewBytes(b []byte, valid bool) Bytes { 23 | return Bytes{ 24 | Bytes: b, 25 | Valid: valid, 26 | Set: true, 27 | } 28 | } 29 | 30 | // BytesFrom creates a new Bytes that will be invalid if nil. 31 | func BytesFrom(b []byte) Bytes { 32 | return NewBytes(b, b != nil) 33 | } 34 | 35 | // BytesFromPtr creates a new Bytes that will be invalid if nil. 36 | func BytesFromPtr(b *[]byte) Bytes { 37 | if b == nil { 38 | return NewBytes(nil, false) 39 | } 40 | n := NewBytes(*b, true) 41 | return n 42 | } 43 | 44 | // IsValid returns true if this carries and explicit value and 45 | // is not null. 46 | func (b Bytes) IsValid() bool { 47 | return b.Set && b.Valid 48 | } 49 | 50 | // IsSet returns true if this carries an explicit value (null inclusive) 51 | func (b Bytes) IsSet() bool { 52 | return b.Set 53 | } 54 | 55 | // UnmarshalJSON implements json.Unmarshaler. 56 | func (b *Bytes) UnmarshalJSON(data []byte) error { 57 | b.Set = true 58 | 59 | if bytes.Equal(data, NullBytes) { 60 | b.Valid = false 61 | b.Bytes = nil 62 | return nil 63 | } 64 | 65 | var bv []byte 66 | if err := json.Unmarshal(data, &bv); err != nil { 67 | return err 68 | } 69 | 70 | b.Bytes = bv 71 | b.Valid = true 72 | return nil 73 | } 74 | 75 | // UnmarshalText implements encoding.TextUnmarshaler. 76 | func (b *Bytes) UnmarshalText(text []byte) error { 77 | b.Set = true 78 | if len(text) == 0 { 79 | b.Bytes = nil 80 | b.Valid = false 81 | } else { 82 | b.Bytes = append(b.Bytes[0:0], text...) 83 | b.Valid = true 84 | } 85 | 86 | return nil 87 | } 88 | 89 | // MarshalJSON implements json.Marshaler. 90 | func (b Bytes) MarshalJSON() ([]byte, error) { 91 | if len(b.Bytes) == 0 { 92 | return NullBytes, nil 93 | } 94 | return json.Marshal(b.Bytes) 95 | } 96 | 97 | // MarshalText implements encoding.TextMarshaler. 98 | func (b Bytes) MarshalText() ([]byte, error) { 99 | if !b.Valid { 100 | return nil, nil 101 | } 102 | return b.Bytes, nil 103 | } 104 | 105 | // SetValid changes this Bytes's value and also sets it to be non-null. 106 | func (b *Bytes) SetValid(n []byte) { 107 | b.Bytes = n 108 | b.Valid = true 109 | b.Set = true 110 | } 111 | 112 | // Ptr returns a pointer to this Bytes's value, or a nil pointer if this Bytes is null. 113 | func (b Bytes) Ptr() *[]byte { 114 | if !b.Valid { 115 | return nil 116 | } 117 | return &b.Bytes 118 | } 119 | 120 | // IsZero returns true for null or zero Bytes's, for future omitempty support (Go 1.4?) 121 | func (b Bytes) IsZero() bool { 122 | return !b.Valid 123 | } 124 | 125 | // Scan implements the Scanner interface. 126 | func (b *Bytes) Scan(value interface{}) error { 127 | if value == nil { 128 | b.Bytes, b.Valid, b.Set = nil, false, false 129 | return nil 130 | } 131 | b.Valid, b.Set = true, true 132 | return convert.ConvertAssign(&b.Bytes, value) 133 | } 134 | 135 | // Value implements the driver Valuer interface. 136 | func (b Bytes) Value() (driver.Value, error) { 137 | if !b.Valid { 138 | return nil, nil 139 | } 140 | return b.Bytes, nil 141 | } 142 | -------------------------------------------------------------------------------- /time.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "bytes" 5 | "database/sql/driver" 6 | "fmt" 7 | "time" 8 | ) 9 | 10 | // Time is a nullable time.Time. It supports SQL and JSON serialization. 11 | type Time struct { 12 | Time time.Time 13 | Valid bool 14 | Set bool 15 | } 16 | 17 | // NewTime creates a new Time. 18 | func NewTime(t time.Time, valid bool) Time { 19 | return Time{ 20 | Time: t, 21 | Valid: valid, 22 | Set: true, 23 | } 24 | } 25 | 26 | // TimeFrom creates a new Time that will always be valid. 27 | func TimeFrom(t time.Time) Time { 28 | return NewTime(t, true) 29 | } 30 | 31 | // TimeFromPtr creates a new Time that will be null if t is nil. 32 | func TimeFromPtr(t *time.Time) Time { 33 | if t == nil { 34 | return NewTime(time.Time{}, false) 35 | } 36 | return NewTime(*t, true) 37 | } 38 | 39 | // IsValid returns true if this carries and explicit value and 40 | // is not null. 41 | func (t Time) IsValid() bool { 42 | return t.Set && t.Valid 43 | } 44 | 45 | // IsSet returns true if this carries an explicit value (null inclusive) 46 | func (t Time) IsSet() bool { 47 | return t.Set 48 | } 49 | 50 | // MarshalJSON implements json.Marshaler. 51 | func (t Time) MarshalJSON() ([]byte, error) { 52 | if !t.Valid { 53 | return NullBytes, nil 54 | } 55 | return t.Time.MarshalJSON() 56 | } 57 | 58 | // UnmarshalJSON implements json.Unmarshaler. 59 | func (t *Time) UnmarshalJSON(data []byte) error { 60 | t.Set = true 61 | if bytes.Equal(data, NullBytes) { 62 | t.Valid = false 63 | t.Time = time.Time{} 64 | return nil 65 | } 66 | 67 | if err := t.Time.UnmarshalJSON(data); err != nil { 68 | return err 69 | } 70 | 71 | t.Valid = true 72 | return nil 73 | } 74 | 75 | // MarshalText implements encoding.TextMarshaler. 76 | func (t Time) MarshalText() ([]byte, error) { 77 | if !t.Valid { 78 | return NullBytes, nil 79 | } 80 | return t.Time.MarshalText() 81 | } 82 | 83 | // UnmarshalText implements encoding.TextUnmarshaler. 84 | func (t *Time) UnmarshalText(text []byte) error { 85 | t.Set = true 86 | if len(text) == 0 { 87 | t.Valid = false 88 | return nil 89 | } 90 | if err := t.Time.UnmarshalText(text); err != nil { 91 | return err 92 | } 93 | t.Valid = true 94 | return nil 95 | } 96 | 97 | // SetValid changes this Time's value and sets it to be non-null. 98 | func (t *Time) SetValid(v time.Time) { 99 | t.Time = v 100 | t.Valid = true 101 | t.Set = true 102 | } 103 | 104 | // Ptr returns a pointer to this Time's value, or a nil pointer if this Time is null. 105 | func (t Time) Ptr() *time.Time { 106 | if !t.Valid { 107 | return nil 108 | } 109 | return &t.Time 110 | } 111 | 112 | // IsZero returns true for an invalid Time's value, for potential future omitempty support. 113 | func (t Time) IsZero() bool { 114 | return !t.Valid 115 | } 116 | 117 | // Scan implements the Scanner interface. 118 | func (t *Time) Scan(value interface{}) error { 119 | var err error 120 | switch x := value.(type) { 121 | case time.Time: 122 | t.Time = x 123 | case nil: 124 | t.Valid, t.Set = false, false 125 | return nil 126 | default: 127 | err = fmt.Errorf("null: cannot scan type %T into null.Time: %v", value, value) 128 | } 129 | if err == nil { 130 | t.Valid, t.Set = true, true 131 | } 132 | return err 133 | } 134 | 135 | // Value implements the driver Valuer interface. 136 | func (t Time) Value() (driver.Value, error) { 137 | if !t.Valid { 138 | return nil, nil 139 | } 140 | return t.Time, nil 141 | } 142 | -------------------------------------------------------------------------------- /float64.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "bytes" 5 | "database/sql/driver" 6 | "encoding/json" 7 | "strconv" 8 | 9 | "github.com/aarondl/null/v9/convert" 10 | ) 11 | 12 | // Float64 is a nullable float64. 13 | type Float64 struct { 14 | Float64 float64 15 | Valid bool 16 | Set bool 17 | } 18 | 19 | // NewFloat64 creates a new Float64 20 | func NewFloat64(f float64, valid bool) Float64 { 21 | return Float64{ 22 | Float64: f, 23 | Valid: valid, 24 | Set: true, 25 | } 26 | } 27 | 28 | // Float64From creates a new Float64 that will always be valid. 29 | func Float64From(f float64) Float64 { 30 | return NewFloat64(f, true) 31 | } 32 | 33 | // Float64FromPtr creates a new Float64 that be null if f is nil. 34 | func Float64FromPtr(f *float64) Float64 { 35 | if f == nil { 36 | return NewFloat64(0, false) 37 | } 38 | return NewFloat64(*f, true) 39 | } 40 | 41 | // IsValid returns true if this carries and explicit value and 42 | // is not null. 43 | func (f Float64) IsValid() bool { 44 | return f.Set && f.Valid 45 | } 46 | 47 | // IsSet returns true if this carries an explicit value (null inclusive) 48 | func (f Float64) IsSet() bool { 49 | return f.Set 50 | } 51 | 52 | // UnmarshalJSON implements json.Unmarshaler. 53 | func (f *Float64) UnmarshalJSON(data []byte) error { 54 | f.Set = true 55 | if bytes.Equal(data, NullBytes) { 56 | f.Float64 = 0 57 | f.Valid = false 58 | return nil 59 | } 60 | 61 | if err := json.Unmarshal(data, &f.Float64); err != nil { 62 | return err 63 | } 64 | 65 | f.Valid = true 66 | return nil 67 | } 68 | 69 | // UnmarshalText implements encoding.TextUnmarshaler. 70 | func (f *Float64) UnmarshalText(text []byte) error { 71 | f.Set = true 72 | if len(text) == 0 { 73 | f.Valid = false 74 | return nil 75 | } 76 | var err error 77 | f.Float64, err = strconv.ParseFloat(string(text), 64) 78 | f.Valid = err == nil 79 | return err 80 | } 81 | 82 | // MarshalJSON implements json.Marshaler. 83 | func (f Float64) MarshalJSON() ([]byte, error) { 84 | if !f.Valid { 85 | return NullBytes, nil 86 | } 87 | return []byte(strconv.FormatFloat(f.Float64, 'f', -1, 64)), nil 88 | } 89 | 90 | // MarshalText implements encoding.TextMarshaler. 91 | func (f Float64) MarshalText() ([]byte, error) { 92 | if !f.Valid { 93 | return []byte{}, nil 94 | } 95 | return []byte(strconv.FormatFloat(f.Float64, 'f', -1, 64)), nil 96 | } 97 | 98 | // SetValid changes this Float64's value and also sets it to be non-null. 99 | func (f *Float64) SetValid(n float64) { 100 | f.Float64 = n 101 | f.Valid = true 102 | f.Set = true 103 | } 104 | 105 | // Ptr returns a pointer to this Float64's value, or a nil pointer if this Float64 is null. 106 | func (f Float64) Ptr() *float64 { 107 | if !f.Valid { 108 | return nil 109 | } 110 | return &f.Float64 111 | } 112 | 113 | // IsZero returns true for invalid Float64s, for future omitempty support (Go 1.4?) 114 | func (f Float64) IsZero() bool { 115 | return !f.Valid 116 | } 117 | 118 | // Scan implements the Scanner interface. 119 | func (f *Float64) Scan(value interface{}) error { 120 | if value == nil { 121 | f.Float64, f.Valid, f.Set = 0, false, false 122 | return nil 123 | } 124 | f.Valid, f.Set = true, true 125 | return convert.ConvertAssign(&f.Float64, value) 126 | } 127 | 128 | // Value implements the driver Valuer interface. 129 | func (f Float64) Value() (driver.Value, error) { 130 | if !f.Valid { 131 | return nil, nil 132 | } 133 | return f.Float64, nil 134 | } 135 | -------------------------------------------------------------------------------- /bool.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "bytes" 5 | "database/sql/driver" 6 | "encoding/json" 7 | "errors" 8 | 9 | "github.com/aarondl/null/v9/convert" 10 | ) 11 | 12 | // Bool is a nullable bool. 13 | type Bool struct { 14 | Bool bool 15 | Valid bool 16 | Set bool 17 | } 18 | 19 | // NewBool creates a new Bool 20 | func NewBool(b, valid bool) Bool { 21 | return Bool{ 22 | Bool: b, 23 | Valid: valid, 24 | Set: true, 25 | } 26 | } 27 | 28 | // BoolFrom creates a new Bool that will always be valid. 29 | func BoolFrom(b bool) Bool { 30 | return NewBool(b, true) 31 | } 32 | 33 | // BoolFromPtr creates a new Bool that will be null if f is nil. 34 | func BoolFromPtr(b *bool) Bool { 35 | if b == nil { 36 | return NewBool(false, false) 37 | } 38 | return NewBool(*b, true) 39 | } 40 | 41 | // IsValid returns true if this carries and explicit value and 42 | // is not null. 43 | func (b Bool) IsValid() bool { 44 | return b.Set && b.Valid 45 | } 46 | 47 | // IsSet returns true if this carries an explicit value (null inclusive) 48 | func (b Bool) IsSet() bool { 49 | return b.Set 50 | } 51 | 52 | // UnmarshalJSON implements json.Unmarshaler. 53 | func (b *Bool) UnmarshalJSON(data []byte) error { 54 | b.Set = true 55 | 56 | if bytes.Equal(data, NullBytes) { 57 | b.Bool = false 58 | b.Valid = false 59 | return nil 60 | } 61 | 62 | if err := json.Unmarshal(data, &b.Bool); err != nil { 63 | return err 64 | } 65 | 66 | b.Valid = true 67 | return nil 68 | } 69 | 70 | // UnmarshalText implements encoding.TextUnmarshaler. 71 | func (b *Bool) UnmarshalText(text []byte) error { 72 | b.Set = true 73 | if len(text) == 0 { 74 | b.Valid = false 75 | return nil 76 | } 77 | 78 | str := string(text) 79 | switch str { 80 | case "true": 81 | b.Bool = true 82 | case "false": 83 | b.Bool = false 84 | default: 85 | b.Valid = false 86 | return errors.New("invalid input:" + str) 87 | } 88 | b.Valid = true 89 | return nil 90 | } 91 | 92 | // MarshalJSON implements json.Marshaler. 93 | func (b Bool) MarshalJSON() ([]byte, error) { 94 | if !b.Valid { 95 | return NullBytes, nil 96 | } 97 | if !b.Bool { 98 | return []byte("false"), nil 99 | } 100 | return []byte("true"), nil 101 | } 102 | 103 | // MarshalText implements encoding.TextMarshaler. 104 | func (b Bool) MarshalText() ([]byte, error) { 105 | if !b.Valid { 106 | return []byte{}, nil 107 | } 108 | if !b.Bool { 109 | return []byte("false"), nil 110 | } 111 | return []byte("true"), nil 112 | } 113 | 114 | // SetValid changes this Bool's value and also sets it to be non-null. 115 | func (b *Bool) SetValid(v bool) { 116 | b.Bool = v 117 | b.Valid = true 118 | b.Set = true 119 | } 120 | 121 | // Ptr returns a pointer to this Bool's value, or a nil pointer if this Bool is null. 122 | func (b Bool) Ptr() *bool { 123 | if !b.Valid { 124 | return nil 125 | } 126 | return &b.Bool 127 | } 128 | 129 | // IsZero returns true for invalid Bools, for future omitempty support (Go 1.4?) 130 | func (b Bool) IsZero() bool { 131 | return !b.Valid 132 | } 133 | 134 | // Scan implements the Scanner interface. 135 | func (b *Bool) Scan(value interface{}) error { 136 | if value == nil { 137 | b.Bool, b.Valid, b.Set = false, false, false 138 | return nil 139 | } 140 | b.Valid, b.Set = true, true 141 | return convert.ConvertAssign(&b.Bool, value) 142 | } 143 | 144 | // Value implements the driver Valuer interface. 145 | func (b Bool) Value() (driver.Value, error) { 146 | if !b.Valid { 147 | return nil, nil 148 | } 149 | return b.Bool, nil 150 | } 151 | -------------------------------------------------------------------------------- /int8.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "bytes" 5 | "database/sql/driver" 6 | "encoding/json" 7 | "fmt" 8 | "math" 9 | "strconv" 10 | 11 | "github.com/aarondl/null/v9/convert" 12 | ) 13 | 14 | // Int8 is an nullable int8. 15 | type Int8 struct { 16 | Int8 int8 17 | Valid bool 18 | Set bool 19 | } 20 | 21 | // NewInt8 creates a new Int8 22 | func NewInt8(i int8, valid bool) Int8 { 23 | return Int8{ 24 | Int8: i, 25 | Valid: valid, 26 | Set: true, 27 | } 28 | } 29 | 30 | // Int8From creates a new Int8 that will always be valid. 31 | func Int8From(i int8) Int8 { 32 | return NewInt8(i, true) 33 | } 34 | 35 | // Int8FromPtr creates a new Int8 that be null if i is nil. 36 | func Int8FromPtr(i *int8) Int8 { 37 | if i == nil { 38 | return NewInt8(0, false) 39 | } 40 | return NewInt8(*i, true) 41 | } 42 | 43 | // IsValid returns true if this carries and explicit value and 44 | // is not null. 45 | func (i Int8) IsValid() bool { 46 | return i.Set && i.Valid 47 | } 48 | 49 | // IsSet returns true if this carries an explicit value (null inclusive) 50 | func (i Int8) IsSet() bool { 51 | return i.Set 52 | } 53 | 54 | // UnmarshalJSON implements json.Unmarshaler. 55 | func (i *Int8) UnmarshalJSON(data []byte) error { 56 | i.Set = true 57 | if bytes.Equal(data, NullBytes) { 58 | i.Valid = false 59 | i.Int8 = 0 60 | return nil 61 | } 62 | 63 | var x int64 64 | if err := json.Unmarshal(data, &x); err != nil { 65 | return err 66 | } 67 | 68 | if x > math.MaxInt8 { 69 | return fmt.Errorf("json: %d overflows max int8 value", x) 70 | } 71 | 72 | i.Int8 = int8(x) 73 | i.Valid = true 74 | return nil 75 | } 76 | 77 | // UnmarshalText implements encoding.TextUnmarshaler. 78 | func (i *Int8) UnmarshalText(text []byte) error { 79 | i.Set = true 80 | if len(text) == 0 { 81 | i.Valid = false 82 | return nil 83 | } 84 | var err error 85 | res, err := strconv.ParseInt(string(text), 10, 8) 86 | i.Valid = err == nil 87 | if i.Valid { 88 | i.Int8 = int8(res) 89 | } 90 | return err 91 | } 92 | 93 | // MarshalJSON implements json.Marshaler. 94 | func (i Int8) MarshalJSON() ([]byte, error) { 95 | if !i.Valid { 96 | return NullBytes, nil 97 | } 98 | return []byte(strconv.FormatInt(int64(i.Int8), 10)), nil 99 | } 100 | 101 | // MarshalText implements encoding.TextMarshaler. 102 | func (i Int8) MarshalText() ([]byte, error) { 103 | if !i.Valid { 104 | return []byte{}, nil 105 | } 106 | return []byte(strconv.FormatInt(int64(i.Int8), 10)), nil 107 | } 108 | 109 | // SetValid changes this Int8's value and also sets it to be non-null. 110 | func (i *Int8) SetValid(n int8) { 111 | i.Int8 = n 112 | i.Valid = true 113 | i.Set = true 114 | } 115 | 116 | // Ptr returns a pointer to this Int8's value, or a nil pointer if this Int8 is null. 117 | func (i Int8) Ptr() *int8 { 118 | if !i.Valid { 119 | return nil 120 | } 121 | return &i.Int8 122 | } 123 | 124 | // IsZero returns true for invalid Int8's, for future omitempty support (Go 1.4?) 125 | func (i Int8) IsZero() bool { 126 | return !i.Valid 127 | } 128 | 129 | // Scan implements the Scanner interface. 130 | func (i *Int8) Scan(value interface{}) error { 131 | if value == nil { 132 | i.Int8, i.Valid, i.Set = 0, false, false 133 | return nil 134 | } 135 | i.Valid, i.Set = true, true 136 | return convert.ConvertAssign(&i.Int8, value) 137 | } 138 | 139 | // Value implements the driver Valuer interface. 140 | func (i Int8) Value() (driver.Value, error) { 141 | if !i.Valid { 142 | return nil, nil 143 | } 144 | return int64(i.Int8), nil 145 | } 146 | -------------------------------------------------------------------------------- /int16.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "bytes" 5 | "database/sql/driver" 6 | "encoding/json" 7 | "fmt" 8 | "math" 9 | "strconv" 10 | 11 | "github.com/aarondl/null/v9/convert" 12 | ) 13 | 14 | // Int16 is an nullable int16. 15 | type Int16 struct { 16 | Int16 int16 17 | Valid bool 18 | Set bool 19 | } 20 | 21 | // NewInt16 creates a new Int16 22 | func NewInt16(i int16, valid bool) Int16 { 23 | return Int16{ 24 | Int16: i, 25 | Valid: valid, 26 | Set: true, 27 | } 28 | } 29 | 30 | // Int16From creates a new Int16 that will always be valid. 31 | func Int16From(i int16) Int16 { 32 | return NewInt16(i, true) 33 | } 34 | 35 | // Int16FromPtr creates a new Int16 that be null if i is nil. 36 | func Int16FromPtr(i *int16) Int16 { 37 | if i == nil { 38 | return NewInt16(0, false) 39 | } 40 | return NewInt16(*i, true) 41 | } 42 | 43 | // IsValid returns true if this carries and explicit value and 44 | // is not null. 45 | func (i Int16) IsValid() bool { 46 | return i.Set && i.Valid 47 | } 48 | 49 | // IsSet returns true if this carries an explicit value (null inclusive) 50 | func (i Int16) IsSet() bool { 51 | return i.Set 52 | } 53 | 54 | // UnmarshalJSON implements json.Unmarshaler. 55 | func (i *Int16) UnmarshalJSON(data []byte) error { 56 | i.Set = true 57 | if bytes.Equal(data, NullBytes) { 58 | i.Int16, i.Valid = 0, false 59 | return nil 60 | } 61 | 62 | var x int64 63 | if err := json.Unmarshal(data, &x); err != nil { 64 | return err 65 | } 66 | 67 | if x > math.MaxInt16 { 68 | return fmt.Errorf("json: %d overflows max int16 value", x) 69 | } 70 | 71 | i.Int16, i.Valid = int16(x), true 72 | return nil 73 | } 74 | 75 | // UnmarshalText implements encoding.TextUnmarshaler. 76 | func (i *Int16) UnmarshalText(text []byte) error { 77 | i.Set = true 78 | if len(text) == 0 { 79 | i.Valid = false 80 | return nil 81 | } 82 | var err error 83 | res, err := strconv.ParseInt(string(text), 10, 16) 84 | i.Valid = err == nil 85 | if i.Valid { 86 | i.Int16 = int16(res) 87 | } 88 | return err 89 | } 90 | 91 | // MarshalJSON implements json.Marshaler. 92 | func (i Int16) MarshalJSON() ([]byte, error) { 93 | if !i.Valid { 94 | return NullBytes, nil 95 | } 96 | return []byte(strconv.FormatInt(int64(i.Int16), 10)), nil 97 | } 98 | 99 | // MarshalText implements encoding.TextMarshaler. 100 | func (i Int16) MarshalText() ([]byte, error) { 101 | if !i.Valid { 102 | return []byte{}, nil 103 | } 104 | return []byte(strconv.FormatInt(int64(i.Int16), 10)), nil 105 | } 106 | 107 | // SetValid changes this Int16's value and also sets it to be non-null. 108 | func (i *Int16) SetValid(n int16) { 109 | i.Int16 = n 110 | i.Valid = true 111 | i.Set = true 112 | } 113 | 114 | // Ptr returns a pointer to this Int16's value, or a nil pointer if this Int16 is null. 115 | func (i Int16) Ptr() *int16 { 116 | if !i.Valid { 117 | return nil 118 | } 119 | return &i.Int16 120 | } 121 | 122 | // IsZero returns true for invalid Int16's, for future omitempty support (Go 1.4?) 123 | func (i Int16) IsZero() bool { 124 | return !i.Valid 125 | } 126 | 127 | // Scan implements the Scanner interface. 128 | func (i *Int16) Scan(value interface{}) error { 129 | if value == nil { 130 | i.Int16, i.Valid, i.Set = 0, false, false 131 | return nil 132 | } 133 | i.Valid, i.Set = true, true 134 | return convert.ConvertAssign(&i.Int16, value) 135 | } 136 | 137 | // Value implements the driver Valuer interface. 138 | func (i Int16) Value() (driver.Value, error) { 139 | if !i.Valid { 140 | return nil, nil 141 | } 142 | return int64(i.Int16), nil 143 | } 144 | -------------------------------------------------------------------------------- /int32.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "bytes" 5 | "database/sql/driver" 6 | "encoding/json" 7 | "fmt" 8 | "math" 9 | "strconv" 10 | 11 | "github.com/aarondl/null/v9/convert" 12 | ) 13 | 14 | // Int32 is an nullable int32. 15 | type Int32 struct { 16 | Int32 int32 17 | Valid bool 18 | Set bool 19 | } 20 | 21 | // NewInt32 creates a new Int32 22 | func NewInt32(i int32, valid bool) Int32 { 23 | return Int32{ 24 | Int32: i, 25 | Valid: valid, 26 | Set: true, 27 | } 28 | } 29 | 30 | // Int32From creates a new Int32 that will always be valid. 31 | func Int32From(i int32) Int32 { 32 | return NewInt32(i, true) 33 | } 34 | 35 | // Int32FromPtr creates a new Int32 that be null if i is nil. 36 | func Int32FromPtr(i *int32) Int32 { 37 | if i == nil { 38 | return NewInt32(0, false) 39 | } 40 | return NewInt32(*i, true) 41 | } 42 | 43 | // IsValid returns true if this carries and explicit value and 44 | // is not null. 45 | func (i Int32) IsValid() bool { 46 | return i.Set && i.Valid 47 | } 48 | 49 | // IsSet returns true if this carries an explicit value (null inclusive) 50 | func (i Int32) IsSet() bool { 51 | return i.Set 52 | } 53 | 54 | // UnmarshalJSON implements json.Unmarshaler. 55 | func (i *Int32) UnmarshalJSON(data []byte) error { 56 | i.Set = true 57 | if bytes.Equal(data, NullBytes) { 58 | i.Valid = false 59 | i.Int32 = 0 60 | return nil 61 | } 62 | 63 | var x int64 64 | if err := json.Unmarshal(data, &x); err != nil { 65 | return err 66 | } 67 | 68 | if x > math.MaxInt32 { 69 | return fmt.Errorf("json: %d overflows max int32 value", x) 70 | } 71 | 72 | i.Int32 = int32(x) 73 | i.Valid = true 74 | return nil 75 | } 76 | 77 | // UnmarshalText implements encoding.TextUnmarshaler. 78 | func (i *Int32) UnmarshalText(text []byte) error { 79 | i.Set = true 80 | if len(text) == 0 { 81 | i.Valid = false 82 | return nil 83 | } 84 | var err error 85 | res, err := strconv.ParseInt(string(text), 10, 32) 86 | i.Valid = err == nil 87 | if i.Valid { 88 | i.Int32 = int32(res) 89 | } 90 | return err 91 | } 92 | 93 | // MarshalJSON implements json.Marshaler. 94 | func (i Int32) MarshalJSON() ([]byte, error) { 95 | if !i.Valid { 96 | return NullBytes, nil 97 | } 98 | return []byte(strconv.FormatInt(int64(i.Int32), 10)), nil 99 | } 100 | 101 | // MarshalText implements encoding.TextMarshaler. 102 | func (i Int32) MarshalText() ([]byte, error) { 103 | if !i.Valid { 104 | return []byte{}, nil 105 | } 106 | return []byte(strconv.FormatInt(int64(i.Int32), 10)), nil 107 | } 108 | 109 | // SetValid changes this Int32's value and also sets it to be non-null. 110 | func (i *Int32) SetValid(n int32) { 111 | i.Int32 = n 112 | i.Valid = true 113 | i.Set = true 114 | } 115 | 116 | // Ptr returns a pointer to this Int32's value, or a nil pointer if this Int32 is null. 117 | func (i Int32) Ptr() *int32 { 118 | if !i.Valid { 119 | return nil 120 | } 121 | return &i.Int32 122 | } 123 | 124 | // IsZero returns true for invalid Int32's, for future omitempty support (Go 1.4?) 125 | func (i Int32) IsZero() bool { 126 | return !i.Valid 127 | } 128 | 129 | // Scan implements the Scanner interface. 130 | func (i *Int32) Scan(value interface{}) error { 131 | if value == nil { 132 | i.Int32, i.Valid, i.Set = 0, false, false 133 | return nil 134 | } 135 | i.Valid, i.Set = true, true 136 | return convert.ConvertAssign(&i.Int32, value) 137 | } 138 | 139 | // Value implements the driver Valuer interface. 140 | func (i Int32) Value() (driver.Value, error) { 141 | if !i.Valid { 142 | return nil, nil 143 | } 144 | return int64(i.Int32), nil 145 | } 146 | -------------------------------------------------------------------------------- /byte.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "bytes" 5 | "database/sql/driver" 6 | "encoding/json" 7 | "errors" 8 | ) 9 | 10 | // Byte is an nullable int. 11 | type Byte struct { 12 | Byte byte 13 | Valid bool 14 | Set bool 15 | } 16 | 17 | // NewByte creates a new Byte 18 | func NewByte(b byte, valid bool) Byte { 19 | return Byte{ 20 | Byte: b, 21 | Valid: valid, 22 | Set: true, 23 | } 24 | } 25 | 26 | // ByteFrom creates a new Byte that will always be valid. 27 | func ByteFrom(b byte) Byte { 28 | return NewByte(b, true) 29 | } 30 | 31 | // ByteFromPtr creates a new Byte that be null if i is nil. 32 | func ByteFromPtr(b *byte) Byte { 33 | if b == nil { 34 | return NewByte(0, false) 35 | } 36 | return NewByte(*b, true) 37 | } 38 | 39 | // IsValid returns true if this carries and explicit value and 40 | // is not null. 41 | func (b Byte) IsValid() bool { 42 | return b.Set && b.Valid 43 | } 44 | 45 | // IsSet returns true if this carries an explicit value (null inclusive) 46 | func (b Byte) IsSet() bool { 47 | return b.Set 48 | } 49 | 50 | // UnmarshalJSON implements json.Unmarshaler. 51 | func (b *Byte) UnmarshalJSON(data []byte) error { 52 | b.Set = true 53 | 54 | if len(data) == 0 || bytes.Equal(data, NullBytes) { 55 | b.Valid = false 56 | b.Byte = 0 57 | return nil 58 | } 59 | 60 | var x string 61 | if err := json.Unmarshal(data, &x); err != nil { 62 | return err 63 | } 64 | 65 | if len(x) > 1 { 66 | return errors.New("json: cannot convert to byte, text len is greater than one") 67 | } 68 | 69 | b.Byte = x[0] 70 | b.Valid = true 71 | return nil 72 | } 73 | 74 | // UnmarshalText implements encoding.TextUnmarshaler. 75 | func (b *Byte) UnmarshalText(text []byte) error { 76 | b.Set = true 77 | if len(text) == 0 { 78 | b.Valid = false 79 | return nil 80 | } 81 | 82 | if len(text) > 1 { 83 | return errors.New("text: cannot convert to byte, text len is greater than one") 84 | } 85 | 86 | b.Valid = true 87 | b.Byte = text[0] 88 | return nil 89 | } 90 | 91 | // MarshalJSON implements json.Marshaler. 92 | func (b Byte) MarshalJSON() ([]byte, error) { 93 | if !b.Valid { 94 | return NullBytes, nil 95 | } 96 | return []byte{'"', b.Byte, '"'}, nil 97 | } 98 | 99 | // MarshalText implements encoding.TextMarshaler. 100 | func (b Byte) MarshalText() ([]byte, error) { 101 | if !b.Valid { 102 | return []byte{}, nil 103 | } 104 | return []byte{b.Byte}, nil 105 | } 106 | 107 | // SetValid changes this Byte's value and also sets it to be non-null. 108 | func (b *Byte) SetValid(n byte) { 109 | b.Byte = n 110 | b.Valid = true 111 | b.Set = true 112 | } 113 | 114 | // Ptr returns a pointer to this Byte's value, or a nil pointer if this Byte is null. 115 | func (b Byte) Ptr() *byte { 116 | if !b.Valid { 117 | return nil 118 | } 119 | return &b.Byte 120 | } 121 | 122 | // IsZero returns true for invalid Bytes, for future omitempty support (Go 1.4?) 123 | func (b Byte) IsZero() bool { 124 | return !b.Valid 125 | } 126 | 127 | // Scan implements the Scanner interface. 128 | func (b *Byte) Scan(value interface{}) error { 129 | if value == nil { 130 | b.Byte, b.Valid, b.Set = 0, false, false 131 | return nil 132 | } 133 | 134 | val := value.(string) 135 | if len(val) == 0 { 136 | b.Byte, b.Valid, b.Set = 0, false, false 137 | return nil 138 | } 139 | 140 | b.Byte, b.Valid, b.Set = val[0], true, true 141 | return nil 142 | } 143 | 144 | // Value implements the driver Valuer interface. 145 | func (b Byte) Value() (driver.Value, error) { 146 | if !b.Valid { 147 | return nil, nil 148 | } 149 | return []byte{b.Byte}, nil 150 | } 151 | -------------------------------------------------------------------------------- /float32.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "bytes" 5 | "database/sql/driver" 6 | "encoding/json" 7 | "strconv" 8 | 9 | "github.com/aarondl/null/v9/convert" 10 | ) 11 | 12 | // Float32 is a nullable float32. 13 | type Float32 struct { 14 | Float32 float32 15 | Valid bool 16 | Set bool 17 | } 18 | 19 | // NewFloat32 creates a new Float32 20 | func NewFloat32(f float32, valid bool) Float32 { 21 | return Float32{ 22 | Float32: f, 23 | Valid: valid, 24 | Set: true, 25 | } 26 | } 27 | 28 | // Float32From creates a new Float32 that will always be valid. 29 | func Float32From(f float32) Float32 { 30 | return NewFloat32(f, true) 31 | } 32 | 33 | // Float32FromPtr creates a new Float32 that be null if f is nil. 34 | func Float32FromPtr(f *float32) Float32 { 35 | if f == nil { 36 | return NewFloat32(0, false) 37 | } 38 | return NewFloat32(*f, true) 39 | } 40 | 41 | // IsValid returns true if this carries and explicit value and 42 | // is not null. 43 | func (f Float32) IsValid() bool { 44 | return f.Set && f.Valid 45 | } 46 | 47 | // IsSet returns true if this carries an explicit value (null inclusive) 48 | func (f Float32) IsSet() bool { 49 | return f.Set 50 | } 51 | 52 | // UnmarshalJSON implements json.Unmarshaler. 53 | func (f *Float32) UnmarshalJSON(data []byte) error { 54 | f.Set = true 55 | if bytes.Equal(data, NullBytes) { 56 | f.Valid = false 57 | f.Float32 = 0 58 | return nil 59 | } 60 | 61 | var x float64 62 | if err := json.Unmarshal(data, &x); err != nil { 63 | return err 64 | } 65 | 66 | f.Float32 = float32(x) 67 | f.Valid = true 68 | return nil 69 | } 70 | 71 | // UnmarshalText implements encoding.TextUnmarshaler. 72 | func (f *Float32) UnmarshalText(text []byte) error { 73 | f.Set = true 74 | if len(text) == 0 { 75 | f.Valid = false 76 | return nil 77 | } 78 | var err error 79 | res, err := strconv.ParseFloat(string(text), 32) 80 | f.Valid = err == nil 81 | if f.Valid { 82 | f.Float32 = float32(res) 83 | } 84 | return err 85 | } 86 | 87 | // MarshalJSON implements json.Marshaler. 88 | func (f Float32) MarshalJSON() ([]byte, error) { 89 | if !f.Valid { 90 | return NullBytes, nil 91 | } 92 | return []byte(strconv.FormatFloat(float64(f.Float32), 'f', -1, 32)), nil 93 | } 94 | 95 | // MarshalText implements encoding.TextMarshaler. 96 | func (f Float32) MarshalText() ([]byte, error) { 97 | if !f.Valid { 98 | return []byte{}, nil 99 | } 100 | return []byte(strconv.FormatFloat(float64(f.Float32), 'f', -1, 32)), nil 101 | } 102 | 103 | // SetValid changes this Float32's value and also sets it to be non-null. 104 | func (f *Float32) SetValid(n float32) { 105 | f.Float32 = n 106 | f.Valid = true 107 | f.Set = true 108 | } 109 | 110 | // Ptr returns a pointer to this Float32's value, or a nil pointer if this Float32 is null. 111 | func (f Float32) Ptr() *float32 { 112 | if !f.Valid { 113 | return nil 114 | } 115 | return &f.Float32 116 | } 117 | 118 | // IsZero returns true for invalid Float32s, for future omitempty support (Go 1.4?) 119 | func (f Float32) IsZero() bool { 120 | return !f.Valid 121 | } 122 | 123 | // Scan implements the Scanner interface. 124 | func (f *Float32) Scan(value interface{}) error { 125 | if value == nil { 126 | f.Float32, f.Valid, f.Set = 0, false, false 127 | return nil 128 | } 129 | f.Valid, f.Set = true, true 130 | return convert.ConvertAssign(&f.Float32, value) 131 | } 132 | 133 | // Value implements the driver Valuer interface. 134 | func (f Float32) Value() (driver.Value, error) { 135 | if !f.Valid { 136 | return nil, nil 137 | } 138 | return float64(f.Float32), nil 139 | } 140 | -------------------------------------------------------------------------------- /uint8.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "bytes" 5 | "database/sql/driver" 6 | "encoding/json" 7 | "fmt" 8 | "math" 9 | "strconv" 10 | 11 | "github.com/aarondl/null/v9/convert" 12 | ) 13 | 14 | // Uint8 is an nullable uint8. 15 | type Uint8 struct { 16 | Uint8 uint8 17 | Valid bool 18 | Set bool 19 | } 20 | 21 | // NewUint8 creates a new Uint8 22 | func NewUint8(i uint8, valid bool) Uint8 { 23 | return Uint8{ 24 | Uint8: i, 25 | Valid: valid, 26 | Set: true, 27 | } 28 | } 29 | 30 | // Uint8From creates a new Uint8 that will always be valid. 31 | func Uint8From(i uint8) Uint8 { 32 | return NewUint8(i, true) 33 | } 34 | 35 | // Uint8FromPtr creates a new Uint8 that be null if i is nil. 36 | func Uint8FromPtr(i *uint8) Uint8 { 37 | if i == nil { 38 | return NewUint8(0, false) 39 | } 40 | return NewUint8(*i, true) 41 | } 42 | 43 | // IsValid returns true if this carries and explicit value and 44 | // is not null. 45 | func (u Uint8) IsValid() bool { 46 | return u.Set && u.Valid 47 | } 48 | 49 | // IsSet returns true if this carries an explicit value (null inclusive) 50 | func (u Uint8) IsSet() bool { 51 | return u.Set 52 | } 53 | 54 | // UnmarshalJSON implements json.Unmarshaler. 55 | func (u *Uint8) UnmarshalJSON(data []byte) error { 56 | u.Set = true 57 | if bytes.Equal(data, NullBytes) { 58 | u.Valid = false 59 | u.Uint8 = 0 60 | return nil 61 | } 62 | 63 | var x uint64 64 | if err := json.Unmarshal(data, &x); err != nil { 65 | return err 66 | } 67 | 68 | if x > math.MaxUint8 { 69 | return fmt.Errorf("json: %d overflows max uint8 value", x) 70 | } 71 | 72 | u.Uint8 = uint8(x) 73 | u.Valid = true 74 | return nil 75 | } 76 | 77 | // UnmarshalText implements encoding.TextUnmarshaler. 78 | func (u *Uint8) UnmarshalText(text []byte) error { 79 | u.Set = true 80 | if len(text) == 0 { 81 | u.Valid = false 82 | return nil 83 | } 84 | var err error 85 | res, err := strconv.ParseUint(string(text), 10, 8) 86 | u.Valid = err == nil 87 | if u.Valid { 88 | u.Uint8 = uint8(res) 89 | } 90 | return err 91 | } 92 | 93 | // MarshalJSON implements json.Marshaler. 94 | func (u Uint8) MarshalJSON() ([]byte, error) { 95 | if !u.Valid { 96 | return NullBytes, nil 97 | } 98 | return []byte(strconv.FormatUint(uint64(u.Uint8), 10)), nil 99 | } 100 | 101 | // MarshalText implements encoding.TextMarshaler. 102 | func (u Uint8) MarshalText() ([]byte, error) { 103 | if !u.Valid { 104 | return []byte{}, nil 105 | } 106 | return []byte(strconv.FormatUint(uint64(u.Uint8), 10)), nil 107 | } 108 | 109 | // SetValid changes this Uint8's value and also sets it to be non-null. 110 | func (u *Uint8) SetValid(n uint8) { 111 | u.Uint8 = n 112 | u.Valid = true 113 | u.Set = true 114 | } 115 | 116 | // Ptr returns a pointer to this Uint8's value, or a nil pointer if this Uint8 is null. 117 | func (u Uint8) Ptr() *uint8 { 118 | if !u.Valid { 119 | return nil 120 | } 121 | return &u.Uint8 122 | } 123 | 124 | // IsZero returns true for invalid Uint8's, for future omitempty support (Go 1.4?) 125 | func (u Uint8) IsZero() bool { 126 | return !u.Valid 127 | } 128 | 129 | // Scan implements the Scanner interface. 130 | func (u *Uint8) Scan(value interface{}) error { 131 | if value == nil { 132 | u.Uint8, u.Valid, u.Set = 0, false, false 133 | return nil 134 | } 135 | u.Valid, u.Set = true, true 136 | return convert.ConvertAssign(&u.Uint8, value) 137 | } 138 | 139 | // Value implements the driver Valuer interface. 140 | func (u Uint8) Value() (driver.Value, error) { 141 | if !u.Valid { 142 | return nil, nil 143 | } 144 | return int64(u.Uint8), nil 145 | } 146 | -------------------------------------------------------------------------------- /uint16.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "bytes" 5 | "database/sql/driver" 6 | "encoding/json" 7 | "fmt" 8 | "math" 9 | "strconv" 10 | 11 | "github.com/aarondl/null/v9/convert" 12 | ) 13 | 14 | // Uint16 is an nullable uint16. 15 | type Uint16 struct { 16 | Uint16 uint16 17 | Valid bool 18 | Set bool 19 | } 20 | 21 | // NewUint16 creates a new Uint16 22 | func NewUint16(i uint16, valid bool) Uint16 { 23 | return Uint16{ 24 | Uint16: i, 25 | Valid: valid, 26 | Set: true, 27 | } 28 | } 29 | 30 | // Uint16From creates a new Uint16 that will always be valid. 31 | func Uint16From(i uint16) Uint16 { 32 | return NewUint16(i, true) 33 | } 34 | 35 | // Uint16FromPtr creates a new Uint16 that be null if i is nil. 36 | func Uint16FromPtr(i *uint16) Uint16 { 37 | if i == nil { 38 | return NewUint16(0, false) 39 | } 40 | return NewUint16(*i, true) 41 | } 42 | 43 | // IsValid returns true if this carries and explicit value and 44 | // is not null. 45 | func (u Uint16) IsValid() bool { 46 | return u.Set && u.Valid 47 | } 48 | 49 | // IsSet returns true if this carries an explicit value (null inclusive) 50 | func (u Uint16) IsSet() bool { 51 | return u.Set 52 | } 53 | 54 | // UnmarshalJSON implements json.Unmarshaler. 55 | func (u *Uint16) UnmarshalJSON(data []byte) error { 56 | u.Set = true 57 | if bytes.Equal(data, NullBytes) { 58 | u.Valid = false 59 | u.Uint16 = 0 60 | return nil 61 | } 62 | 63 | var x uint64 64 | if err := json.Unmarshal(data, &x); err != nil { 65 | return err 66 | } 67 | 68 | if x > math.MaxUint16 { 69 | return fmt.Errorf("json: %d overflows max uint8 value", x) 70 | } 71 | 72 | u.Uint16 = uint16(x) 73 | u.Valid = true 74 | return nil 75 | } 76 | 77 | // UnmarshalText implements encoding.TextUnmarshaler. 78 | func (u *Uint16) UnmarshalText(text []byte) error { 79 | u.Set = true 80 | if len(text) == 0 { 81 | u.Valid = false 82 | return nil 83 | } 84 | var err error 85 | res, err := strconv.ParseUint(string(text), 10, 16) 86 | u.Valid = err == nil 87 | if u.Valid { 88 | u.Uint16 = uint16(res) 89 | } 90 | return err 91 | } 92 | 93 | // MarshalJSON implements json.Marshaler. 94 | func (u Uint16) MarshalJSON() ([]byte, error) { 95 | if !u.Valid { 96 | return NullBytes, nil 97 | } 98 | return []byte(strconv.FormatUint(uint64(u.Uint16), 10)), nil 99 | } 100 | 101 | // MarshalText implements encoding.TextMarshaler. 102 | func (u Uint16) MarshalText() ([]byte, error) { 103 | if !u.Valid { 104 | return []byte{}, nil 105 | } 106 | return []byte(strconv.FormatUint(uint64(u.Uint16), 10)), nil 107 | } 108 | 109 | // SetValid changes this Uint16's value and also sets it to be non-null. 110 | func (u *Uint16) SetValid(n uint16) { 111 | u.Uint16 = n 112 | u.Valid = true 113 | u.Set = true 114 | } 115 | 116 | // Ptr returns a pointer to this Uint16's value, or a nil pointer if this Uint16 is null. 117 | func (u Uint16) Ptr() *uint16 { 118 | if !u.Valid { 119 | return nil 120 | } 121 | return &u.Uint16 122 | } 123 | 124 | // IsZero returns true for invalid Uint16's, for future omitempty support (Go 1.4?) 125 | func (u Uint16) IsZero() bool { 126 | return !u.Valid 127 | } 128 | 129 | // Scan implements the Scanner interface. 130 | func (u *Uint16) Scan(value interface{}) error { 131 | if value == nil { 132 | u.Uint16, u.Valid, u.Set = 0, false, false 133 | return nil 134 | } 135 | u.Valid, u.Set = true, true 136 | return convert.ConvertAssign(&u.Uint16, value) 137 | } 138 | 139 | // Value implements the driver Valuer interface. 140 | func (u Uint16) Value() (driver.Value, error) { 141 | if !u.Valid { 142 | return nil, nil 143 | } 144 | return int64(u.Uint16), nil 145 | } 146 | -------------------------------------------------------------------------------- /uint32.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "bytes" 5 | "database/sql/driver" 6 | "encoding/json" 7 | "fmt" 8 | "math" 9 | "strconv" 10 | 11 | "github.com/aarondl/null/v9/convert" 12 | ) 13 | 14 | // Uint32 is an nullable uint32. 15 | type Uint32 struct { 16 | Uint32 uint32 17 | Valid bool 18 | Set bool 19 | } 20 | 21 | // NewUint32 creates a new Uint32 22 | func NewUint32(i uint32, valid bool) Uint32 { 23 | return Uint32{ 24 | Uint32: i, 25 | Valid: valid, 26 | Set: true, 27 | } 28 | } 29 | 30 | // Uint32From creates a new Uint32 that will always be valid. 31 | func Uint32From(i uint32) Uint32 { 32 | return NewUint32(i, true) 33 | } 34 | 35 | // Uint32FromPtr creates a new Uint32 that be null if i is nil. 36 | func Uint32FromPtr(i *uint32) Uint32 { 37 | if i == nil { 38 | return NewUint32(0, false) 39 | } 40 | return NewUint32(*i, true) 41 | } 42 | 43 | // IsValid returns true if this carries and explicit value and 44 | // is not null. 45 | func (u Uint32) IsValid() bool { 46 | return u.Set && u.Valid 47 | } 48 | 49 | // IsSet returns true if this carries an explicit value (null inclusive) 50 | func (u Uint32) IsSet() bool { 51 | return u.Set 52 | } 53 | 54 | // UnmarshalJSON implements json.Unmarshaler. 55 | func (u *Uint32) UnmarshalJSON(data []byte) error { 56 | u.Set = true 57 | if bytes.Equal(data, NullBytes) { 58 | u.Valid = false 59 | u.Uint32 = 0 60 | return nil 61 | } 62 | 63 | var x uint64 64 | if err := json.Unmarshal(data, &x); err != nil { 65 | return err 66 | } 67 | 68 | if x > math.MaxUint32 { 69 | return fmt.Errorf("json: %d overflows max uint32 value", x) 70 | } 71 | 72 | u.Uint32 = uint32(x) 73 | u.Valid = true 74 | return nil 75 | } 76 | 77 | // UnmarshalText implements encoding.TextUnmarshaler. 78 | func (u *Uint32) UnmarshalText(text []byte) error { 79 | u.Set = true 80 | if len(text) == 0 { 81 | u.Valid = false 82 | return nil 83 | } 84 | var err error 85 | res, err := strconv.ParseUint(string(text), 10, 32) 86 | u.Valid = err == nil 87 | if u.Valid { 88 | u.Uint32 = uint32(res) 89 | } 90 | return err 91 | } 92 | 93 | // MarshalJSON implements json.Marshaler. 94 | func (u Uint32) MarshalJSON() ([]byte, error) { 95 | if !u.Valid { 96 | return NullBytes, nil 97 | } 98 | return []byte(strconv.FormatUint(uint64(u.Uint32), 10)), nil 99 | } 100 | 101 | // MarshalText implements encoding.TextMarshaler. 102 | func (u Uint32) MarshalText() ([]byte, error) { 103 | if !u.Valid { 104 | return []byte{}, nil 105 | } 106 | return []byte(strconv.FormatUint(uint64(u.Uint32), 10)), nil 107 | } 108 | 109 | // SetValid changes this Uint32's value and also sets it to be non-null. 110 | func (u *Uint32) SetValid(n uint32) { 111 | u.Uint32 = n 112 | u.Valid = true 113 | u.Set = true 114 | } 115 | 116 | // Ptr returns a pointer to this Uint32's value, or a nil pointer if this Uint32 is null. 117 | func (u Uint32) Ptr() *uint32 { 118 | if !u.Valid { 119 | return nil 120 | } 121 | return &u.Uint32 122 | } 123 | 124 | // IsZero returns true for invalid Uint32's, for future omitempty support (Go 1.4?) 125 | func (u Uint32) IsZero() bool { 126 | return !u.Valid 127 | } 128 | 129 | // Scan implements the Scanner interface. 130 | func (u *Uint32) Scan(value interface{}) error { 131 | if value == nil { 132 | u.Uint32, u.Valid, u.Set = 0, false, false 133 | return nil 134 | } 135 | u.Valid, u.Set = true, true 136 | return convert.ConvertAssign(&u.Uint32, value) 137 | } 138 | 139 | // Value implements the driver Valuer interface. 140 | func (u Uint32) Value() (driver.Value, error) { 141 | if !u.Valid { 142 | return nil, nil 143 | } 144 | return int64(u.Uint32), nil 145 | } 146 | -------------------------------------------------------------------------------- /uint64.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "bytes" 5 | "database/sql/driver" 6 | "encoding/json" 7 | "strconv" 8 | 9 | "github.com/aarondl/null/v9/convert" 10 | ) 11 | 12 | // Uint64 is an nullable uint64. 13 | type Uint64 struct { 14 | Uint64 uint64 15 | Valid bool 16 | Set bool 17 | } 18 | 19 | // NewUint64 creates a new Uint64 20 | func NewUint64(i uint64, valid bool) Uint64 { 21 | return Uint64{ 22 | Uint64: i, 23 | Valid: valid, 24 | Set: true, 25 | } 26 | } 27 | 28 | // Uint64From creates a new Uint64 that will always be valid. 29 | func Uint64From(i uint64) Uint64 { 30 | return NewUint64(i, true) 31 | } 32 | 33 | // Uint64FromPtr creates a new Uint64 that be null if i is nil. 34 | func Uint64FromPtr(i *uint64) Uint64 { 35 | if i == nil { 36 | return NewUint64(0, false) 37 | } 38 | return NewUint64(*i, true) 39 | } 40 | 41 | // IsValid returns true if this carries and explicit value and 42 | // is not null. 43 | func (u Uint64) IsValid() bool { 44 | return u.Set && u.Valid 45 | } 46 | 47 | // IsSet returns true if this carries an explicit value (null inclusive) 48 | func (u Uint64) IsSet() bool { 49 | return u.Set 50 | } 51 | 52 | // UnmarshalJSON implements json.Unmarshaler. 53 | func (u *Uint64) UnmarshalJSON(data []byte) error { 54 | u.Set = true 55 | if bytes.Equal(data, NullBytes) { 56 | u.Uint64 = 0 57 | u.Valid = false 58 | return nil 59 | } 60 | 61 | if err := json.Unmarshal(data, &u.Uint64); err != nil { 62 | return err 63 | } 64 | 65 | u.Valid = true 66 | return nil 67 | } 68 | 69 | // UnmarshalText implements encoding.TextUnmarshaler. 70 | func (u *Uint64) UnmarshalText(text []byte) error { 71 | u.Set = true 72 | if len(text) == 0 { 73 | u.Valid = false 74 | return nil 75 | } 76 | var err error 77 | res, err := strconv.ParseUint(string(text), 10, 64) 78 | u.Valid = err == nil 79 | if u.Valid { 80 | u.Uint64 = uint64(res) 81 | } 82 | return err 83 | } 84 | 85 | // MarshalJSON implements json.Marshaler. 86 | func (u Uint64) MarshalJSON() ([]byte, error) { 87 | if !u.Valid { 88 | return NullBytes, nil 89 | } 90 | return []byte(strconv.FormatUint(u.Uint64, 10)), nil 91 | } 92 | 93 | // MarshalText implements encoding.TextMarshaler. 94 | func (u Uint64) MarshalText() ([]byte, error) { 95 | if !u.Valid { 96 | return []byte{}, nil 97 | } 98 | return []byte(strconv.FormatUint(u.Uint64, 10)), nil 99 | } 100 | 101 | // SetValid changes this Uint64's value and also sets it to be non-null. 102 | func (u *Uint64) SetValid(n uint64) { 103 | u.Uint64 = n 104 | u.Valid = true 105 | u.Set = true 106 | } 107 | 108 | // Ptr returns a pointer to this Uint64's value, or a nil pointer if this Uint64 is null. 109 | func (u Uint64) Ptr() *uint64 { 110 | if !u.Valid { 111 | return nil 112 | } 113 | return &u.Uint64 114 | } 115 | 116 | // IsZero returns true for invalid Uint64's, for future omitempty support (Go 1.4?) 117 | func (u Uint64) IsZero() bool { 118 | return !u.Valid 119 | } 120 | 121 | // Scan implements the Scanner interface. 122 | func (u *Uint64) Scan(value interface{}) error { 123 | if value == nil { 124 | u.Uint64, u.Valid, u.Set = 0, false, false 125 | return nil 126 | } 127 | u.Valid, u.Set = true, true 128 | 129 | // If value is negative int64, convert it to uint64 130 | if i, ok := value.(int64); ok && i < 0 { 131 | return convert.ConvertAssign(&u.Uint64, uint64(i)) 132 | } 133 | 134 | return convert.ConvertAssign(&u.Uint64, value) 135 | } 136 | 137 | // Value implements the driver Valuer interface. 138 | func (u Uint64) Value() (driver.Value, error) { 139 | if !u.Valid { 140 | return nil, nil 141 | } 142 | 143 | // If u.Uint64 overflows the range of int64, convert it to string 144 | if u.Uint64 >= 1<<63 { 145 | return strconv.FormatUint(u.Uint64, 10), nil 146 | } 147 | 148 | return int64(u.Uint64), nil 149 | } 150 | -------------------------------------------------------------------------------- /byte_test.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | ) 7 | 8 | func TestByteFrom(t *testing.T) { 9 | i := ByteFrom('b') 10 | assertByte(t, i, "ByteFrom()") 11 | 12 | zero := ByteFrom(0) 13 | if !zero.Valid { 14 | t.Error("ByteFrom(0)", "is invalid, but should be valid") 15 | } 16 | } 17 | 18 | func TestByteFromPtr(t *testing.T) { 19 | n := byte('b') 20 | iptr := &n 21 | i := ByteFromPtr(iptr) 22 | assertByte(t, i, "ByteFromPtr()") 23 | 24 | null := ByteFromPtr(nil) 25 | assertNullByte(t, null, "ByteFromPtr(nil)") 26 | } 27 | 28 | func TestUnmarshalByte(t *testing.T) { 29 | var null Byte 30 | err := json.Unmarshal(nullJSON, &null) 31 | maybePanic(err) 32 | assertNullByte(t, null, "null json") 33 | if !null.Set { 34 | t.Error("expected Set to be true") 35 | } 36 | 37 | var badType Byte 38 | err = json.Unmarshal(boolJSON, &badType) 39 | if err == nil { 40 | panic("err should not be nil") 41 | } 42 | assertNullByte(t, badType, "wrong type json") 43 | 44 | var invalid Byte 45 | err = invalid.UnmarshalJSON(invalidJSON) 46 | if _, ok := err.(*json.SyntaxError); !ok { 47 | t.Errorf("expected json.SyntaxError, not %T", err) 48 | } 49 | assertNullByte(t, invalid, "invalid json") 50 | } 51 | 52 | func TestUnmarshalNonByteegerNumber(t *testing.T) { 53 | var i Byte 54 | err := json.Unmarshal(float64JSON, &i) 55 | if err == nil { 56 | panic("err should be present; non-integer number coerced to int") 57 | } 58 | } 59 | 60 | func TestTextUnmarshalByte(t *testing.T) { 61 | var i Byte 62 | err := i.UnmarshalText([]byte("b")) 63 | maybePanic(err) 64 | assertByte(t, i, "UnmarshalText() int") 65 | 66 | var blank Byte 67 | err = blank.UnmarshalText([]byte("")) 68 | maybePanic(err) 69 | assertNullByte(t, blank, "UnmarshalText() empty int") 70 | } 71 | 72 | func TestMarshalByte(t *testing.T) { 73 | i := ByteFrom('b') 74 | data, err := json.Marshal(i) 75 | maybePanic(err) 76 | assertJSONEquals(t, data, `"b"`, "non-empty json marshal") 77 | 78 | // invalid values should be encoded as null 79 | null := NewByte(0, false) 80 | data, err = json.Marshal(null) 81 | maybePanic(err) 82 | assertJSONEquals(t, data, "null", "null json marshal") 83 | } 84 | 85 | func TestMarshalByteText(t *testing.T) { 86 | i := ByteFrom('b') 87 | data, err := i.MarshalText() 88 | maybePanic(err) 89 | assertJSONEquals(t, data, "b", "non-empty text marshal") 90 | 91 | // invalid values should be encoded as null 92 | null := NewByte(0, false) 93 | data, err = null.MarshalText() 94 | maybePanic(err) 95 | assertJSONEquals(t, data, "", "null text marshal") 96 | } 97 | 98 | func TestBytePointer(t *testing.T) { 99 | i := ByteFrom('b') 100 | ptr := i.Ptr() 101 | if *ptr != 'b' { 102 | t.Errorf("bad %s int: %#v ≠ %d\n", "pointer", ptr, 'b') 103 | } 104 | 105 | null := NewByte(0, false) 106 | ptr = null.Ptr() 107 | if ptr != nil { 108 | t.Errorf("bad %s int: %#v ≠ %s\n", "nil pointer", ptr, "nil") 109 | } 110 | } 111 | 112 | func TestByteIsZero(t *testing.T) { 113 | i := ByteFrom('b') 114 | if i.IsZero() { 115 | t.Errorf("IsZero() should be false") 116 | } 117 | 118 | null := NewByte(0, false) 119 | if !null.IsZero() { 120 | t.Errorf("IsZero() should be true") 121 | } 122 | 123 | zero := NewByte(0, true) 124 | if zero.IsZero() { 125 | t.Errorf("IsZero() should be false") 126 | } 127 | } 128 | 129 | func TestByteSetValid(t *testing.T) { 130 | change := NewByte(0, false) 131 | assertNullByte(t, change, "SetValid()") 132 | change.SetValid('b') 133 | assertByte(t, change, "SetValid()") 134 | } 135 | 136 | func TestByteScan(t *testing.T) { 137 | var i Byte 138 | err := i.Scan("b") 139 | maybePanic(err) 140 | assertByte(t, i, "scanned int") 141 | 142 | var null Byte 143 | err = null.Scan(nil) 144 | maybePanic(err) 145 | assertNullByte(t, null, "scanned null") 146 | } 147 | 148 | func assertByte(t *testing.T, i Byte, from string) { 149 | if i.Byte != 'b' { 150 | t.Errorf("bad %s int: %d ≠ %d\n", from, i.Byte, 'b') 151 | } 152 | if !i.Valid { 153 | t.Error(from, "is invalid, but should be valid") 154 | } 155 | } 156 | 157 | func assertNullByte(t *testing.T, i Byte, from string) { 158 | if i.Valid { 159 | t.Error(from, "is valid, but should be invalid") 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /float32_test.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | ) 7 | 8 | var ( 9 | float32JSON = []byte(`1.2345`) 10 | ) 11 | 12 | func TestFloat32From(t *testing.T) { 13 | f := Float32From(1.2345) 14 | assertFloat32(t, f, "Float32From()") 15 | 16 | zero := Float32From(0) 17 | if !zero.Valid { 18 | t.Error("Float32From(0)", "is invalid, but should be valid") 19 | } 20 | } 21 | 22 | func TestFloat32FromPtr(t *testing.T) { 23 | n := float32(1.2345) 24 | iptr := &n 25 | f := Float32FromPtr(iptr) 26 | assertFloat32(t, f, "Float32FromPtr()") 27 | 28 | null := Float32FromPtr(nil) 29 | assertNullFloat32(t, null, "Float32FromPtr(nil)") 30 | } 31 | 32 | func TestUnmarshalFloat32(t *testing.T) { 33 | var f Float32 34 | err := json.Unmarshal(float32JSON, &f) 35 | maybePanic(err) 36 | assertFloat32(t, f, "float32 json") 37 | 38 | var null Float32 39 | err = json.Unmarshal(nullJSON, &null) 40 | maybePanic(err) 41 | assertNullFloat32(t, null, "null json") 42 | if !null.Set { 43 | t.Error("should be Set") 44 | } 45 | 46 | var badType Float32 47 | err = json.Unmarshal(boolJSON, &badType) 48 | if err == nil { 49 | panic("err should not be nil") 50 | } 51 | assertNullFloat32(t, badType, "wrong type json") 52 | 53 | var invalid Float32 54 | err = invalid.UnmarshalJSON(invalidJSON) 55 | if _, ok := err.(*json.SyntaxError); !ok { 56 | t.Errorf("expected json.SyntaxError, not %T", err) 57 | } 58 | } 59 | 60 | func TestTextUnmarshalFloat32(t *testing.T) { 61 | var f Float32 62 | err := f.UnmarshalText([]byte("1.2345")) 63 | maybePanic(err) 64 | assertFloat32(t, f, "UnmarshalText() float32") 65 | 66 | var blank Float32 67 | err = blank.UnmarshalText([]byte("")) 68 | maybePanic(err) 69 | assertNullFloat32(t, blank, "UnmarshalText() empty float32") 70 | } 71 | 72 | func TestMarshalFloat32(t *testing.T) { 73 | f := Float32From(1.2345) 74 | data, err := json.Marshal(f) 75 | maybePanic(err) 76 | assertJSONEquals(t, data, "1.2345", "non-empty json marshal") 77 | 78 | // invalid values should be encoded as null 79 | null := NewFloat32(0, false) 80 | data, err = json.Marshal(null) 81 | maybePanic(err) 82 | assertJSONEquals(t, data, "null", "null json marshal") 83 | } 84 | 85 | func TestMarshalFloat32Text(t *testing.T) { 86 | f := Float32From(1.2345) 87 | data, err := f.MarshalText() 88 | maybePanic(err) 89 | assertJSONEquals(t, data, "1.2345", "non-empty text marshal") 90 | 91 | // invalid values should be encoded as null 92 | null := NewFloat32(0, false) 93 | data, err = null.MarshalText() 94 | maybePanic(err) 95 | assertJSONEquals(t, data, "", "null text marshal") 96 | } 97 | 98 | func TestFloat32Pointer(t *testing.T) { 99 | f := Float32From(1.2345) 100 | ptr := f.Ptr() 101 | if *ptr != 1.2345 { 102 | t.Errorf("bad %s float32: %#v ≠ %v\n", "pointer", ptr, 1.2345) 103 | } 104 | 105 | null := NewFloat32(0, false) 106 | ptr = null.Ptr() 107 | if ptr != nil { 108 | t.Errorf("bad %s float32: %#v ≠ %s\n", "nil pointer", ptr, "nil") 109 | } 110 | } 111 | 112 | func TestFloat32IsZero(t *testing.T) { 113 | f := Float32From(1.2345) 114 | if f.IsZero() { 115 | t.Errorf("IsZero() should be false") 116 | } 117 | 118 | null := NewFloat32(0, false) 119 | if !null.IsZero() { 120 | t.Errorf("IsZero() should be true") 121 | } 122 | 123 | zero := NewFloat32(0, true) 124 | if zero.IsZero() { 125 | t.Errorf("IsZero() should be false") 126 | } 127 | } 128 | 129 | func TestFloat32SetValid(t *testing.T) { 130 | change := NewFloat32(0, false) 131 | assertNullFloat32(t, change, "SetValid()") 132 | change.SetValid(1.2345) 133 | assertFloat32(t, change, "SetValid()") 134 | } 135 | 136 | func TestFloat32Scan(t *testing.T) { 137 | var f Float32 138 | err := f.Scan(1.2345) 139 | maybePanic(err) 140 | assertFloat32(t, f, "scanned float32") 141 | 142 | var null Float32 143 | err = null.Scan(nil) 144 | maybePanic(err) 145 | assertNullFloat32(t, null, "scanned null") 146 | } 147 | 148 | func assertFloat32(t *testing.T, f Float32, from string) { 149 | if f.Float32 != 1.2345 { 150 | t.Errorf("bad %s float32: %f ≠ %f\n", from, f.Float32, 1.2345) 151 | } 152 | if !f.Valid { 153 | t.Error(from, "is invalid, but should be valid") 154 | } 155 | } 156 | 157 | func assertNullFloat32(t *testing.T, f Float32, from string) { 158 | if f.Valid { 159 | t.Error(from, "is valid, but should be invalid") 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /float64_test.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | ) 7 | 8 | var ( 9 | float64JSON = []byte(`1.2345`) 10 | ) 11 | 12 | func TestFloat64From(t *testing.T) { 13 | f := Float64From(1.2345) 14 | assertFloat64(t, f, "Float64From()") 15 | 16 | zero := Float64From(0) 17 | if !zero.Valid { 18 | t.Error("Float64From(0)", "is invalid, but should be valid") 19 | } 20 | } 21 | 22 | func TestFloat64FromPtr(t *testing.T) { 23 | n := float64(1.2345) 24 | iptr := &n 25 | f := Float64FromPtr(iptr) 26 | assertFloat64(t, f, "Float64FromPtr()") 27 | 28 | null := Float64FromPtr(nil) 29 | assertNullFloat64(t, null, "Float64FromPtr(nil)") 30 | } 31 | 32 | func TestUnmarshalFloat64(t *testing.T) { 33 | var f Float64 34 | err := json.Unmarshal(float64JSON, &f) 35 | maybePanic(err) 36 | assertFloat64(t, f, "float64 json") 37 | 38 | var null Float64 39 | err = json.Unmarshal(nullJSON, &null) 40 | maybePanic(err) 41 | assertNullFloat64(t, null, "null json") 42 | if !null.Set { 43 | t.Error("should be Set") 44 | } 45 | 46 | var badType Float64 47 | err = json.Unmarshal(boolJSON, &badType) 48 | if err == nil { 49 | panic("err should not be nil") 50 | } 51 | assertNullFloat64(t, badType, "wrong type json") 52 | 53 | var invalid Float64 54 | err = invalid.UnmarshalJSON(invalidJSON) 55 | if _, ok := err.(*json.SyntaxError); !ok { 56 | t.Errorf("expected json.SyntaxError, not %T", err) 57 | } 58 | } 59 | 60 | func TestTextUnmarshalFloat64(t *testing.T) { 61 | var f Float64 62 | err := f.UnmarshalText([]byte("1.2345")) 63 | maybePanic(err) 64 | assertFloat64(t, f, "UnmarshalText() float64") 65 | 66 | var blank Float64 67 | err = blank.UnmarshalText([]byte("")) 68 | maybePanic(err) 69 | assertNullFloat64(t, blank, "UnmarshalText() empty float64") 70 | } 71 | 72 | func TestMarshalFloat64(t *testing.T) { 73 | f := Float64From(1.2345) 74 | data, err := json.Marshal(f) 75 | maybePanic(err) 76 | assertJSONEquals(t, data, "1.2345", "non-empty json marshal") 77 | 78 | // invalid values should be encoded as null 79 | null := NewFloat64(0, false) 80 | data, err = json.Marshal(null) 81 | maybePanic(err) 82 | assertJSONEquals(t, data, "null", "null json marshal") 83 | } 84 | 85 | func TestMarshalFloat64Text(t *testing.T) { 86 | f := Float64From(1.2345) 87 | data, err := f.MarshalText() 88 | maybePanic(err) 89 | assertJSONEquals(t, data, "1.2345", "non-empty text marshal") 90 | 91 | // invalid values should be encoded as null 92 | null := NewFloat64(0, false) 93 | data, err = null.MarshalText() 94 | maybePanic(err) 95 | assertJSONEquals(t, data, "", "null text marshal") 96 | } 97 | 98 | func TestFloat64Pointer(t *testing.T) { 99 | f := Float64From(1.2345) 100 | ptr := f.Ptr() 101 | if *ptr != 1.2345 { 102 | t.Errorf("bad %s float64: %#v ≠ %v\n", "pointer", ptr, 1.2345) 103 | } 104 | 105 | null := NewFloat64(0, false) 106 | ptr = null.Ptr() 107 | if ptr != nil { 108 | t.Errorf("bad %s float64: %#v ≠ %s\n", "nil pointer", ptr, "nil") 109 | } 110 | } 111 | 112 | func TestFloat64IsZero(t *testing.T) { 113 | f := Float64From(1.2345) 114 | if f.IsZero() { 115 | t.Errorf("IsZero() should be false") 116 | } 117 | 118 | null := NewFloat64(0, false) 119 | if !null.IsZero() { 120 | t.Errorf("IsZero() should be true") 121 | } 122 | 123 | zero := NewFloat64(0, true) 124 | if zero.IsZero() { 125 | t.Errorf("IsZero() should be false") 126 | } 127 | } 128 | 129 | func TestFloat64SetValid(t *testing.T) { 130 | change := NewFloat64(0, false) 131 | assertNullFloat64(t, change, "SetValid()") 132 | change.SetValid(1.2345) 133 | assertFloat64(t, change, "SetValid()") 134 | } 135 | 136 | func TestFloat64Scan(t *testing.T) { 137 | var f Float64 138 | err := f.Scan(1.2345) 139 | maybePanic(err) 140 | assertFloat64(t, f, "scanned float64") 141 | 142 | var null Float64 143 | err = null.Scan(nil) 144 | maybePanic(err) 145 | assertNullFloat64(t, null, "scanned null") 146 | } 147 | 148 | func assertFloat64(t *testing.T, f Float64, from string) { 149 | if f.Float64 != 1.2345 { 150 | t.Errorf("bad %s float64: %f ≠ %f\n", from, f.Float64, 1.2345) 151 | } 152 | if !f.Valid { 153 | t.Error(from, "is invalid, but should be valid") 154 | } 155 | } 156 | 157 | func assertNullFloat64(t *testing.T, f Float64, from string) { 158 | if f.Valid { 159 | t.Error(from, "is valid, but should be invalid") 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /int_test.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | ) 7 | 8 | var ( 9 | intJSON = []byte(`12345`) 10 | ) 11 | 12 | func TestIntFrom(t *testing.T) { 13 | i := IntFrom(12345) 14 | assertInt(t, i, "IntFrom()") 15 | 16 | zero := IntFrom(0) 17 | if !zero.Valid { 18 | t.Error("IntFrom(0)", "is invalid, but should be valid") 19 | } 20 | } 21 | 22 | func TestIntFromPtr(t *testing.T) { 23 | n := int(12345) 24 | iptr := &n 25 | i := IntFromPtr(iptr) 26 | assertInt(t, i, "IntFromPtr()") 27 | 28 | null := IntFromPtr(nil) 29 | assertNullInt(t, null, "IntFromPtr(nil)") 30 | } 31 | 32 | func TestUnmarshalInt(t *testing.T) { 33 | var i Int 34 | err := json.Unmarshal(intJSON, &i) 35 | maybePanic(err) 36 | assertInt(t, i, "int json") 37 | 38 | var null Int 39 | err = json.Unmarshal(nullJSON, &null) 40 | maybePanic(err) 41 | assertNullInt(t, null, "null json") 42 | if !null.Set { 43 | t.Error("is not Set, but should be") 44 | } 45 | 46 | var badType Int 47 | err = json.Unmarshal(boolJSON, &badType) 48 | if err == nil { 49 | panic("err should not be nil") 50 | } 51 | assertNullInt(t, badType, "wrong type json") 52 | 53 | var invalid Int 54 | err = invalid.UnmarshalJSON(invalidJSON) 55 | if _, ok := err.(*json.SyntaxError); !ok { 56 | t.Errorf("expected json.SyntaxError, not %T", err) 57 | } 58 | assertNullInt(t, invalid, "invalid json") 59 | } 60 | 61 | func TestUnmarshalNonIntegerNumber(t *testing.T) { 62 | var i Int 63 | err := json.Unmarshal(float64JSON, &i) 64 | if err == nil { 65 | panic("err should be present; non-integer number coerced to int") 66 | } 67 | } 68 | 69 | func TestTextUnmarshalInt(t *testing.T) { 70 | var i Int 71 | err := i.UnmarshalText([]byte("12345")) 72 | maybePanic(err) 73 | assertInt(t, i, "UnmarshalText() int") 74 | 75 | var blank Int 76 | err = blank.UnmarshalText([]byte("")) 77 | maybePanic(err) 78 | assertNullInt(t, blank, "UnmarshalText() empty int") 79 | } 80 | 81 | func TestMarshalInt(t *testing.T) { 82 | i := IntFrom(12345) 83 | data, err := json.Marshal(i) 84 | maybePanic(err) 85 | assertJSONEquals(t, data, "12345", "non-empty json marshal") 86 | 87 | // invalid values should be encoded as null 88 | null := NewInt(0, false) 89 | data, err = json.Marshal(null) 90 | maybePanic(err) 91 | assertJSONEquals(t, data, "null", "null json marshal") 92 | } 93 | 94 | func TestMarshalIntText(t *testing.T) { 95 | i := IntFrom(12345) 96 | data, err := i.MarshalText() 97 | maybePanic(err) 98 | assertJSONEquals(t, data, "12345", "non-empty text marshal") 99 | 100 | // invalid values should be encoded as null 101 | null := NewInt(0, false) 102 | data, err = null.MarshalText() 103 | maybePanic(err) 104 | assertJSONEquals(t, data, "", "null text marshal") 105 | } 106 | 107 | func TestIntPointer(t *testing.T) { 108 | i := IntFrom(12345) 109 | ptr := i.Ptr() 110 | if *ptr != 12345 { 111 | t.Errorf("bad %s int: %#v ≠ %d\n", "pointer", ptr, 12345) 112 | } 113 | 114 | null := NewInt(0, false) 115 | ptr = null.Ptr() 116 | if ptr != nil { 117 | t.Errorf("bad %s int: %#v ≠ %s\n", "nil pointer", ptr, "nil") 118 | } 119 | } 120 | 121 | func TestIntIsZero(t *testing.T) { 122 | i := IntFrom(12345) 123 | if i.IsZero() { 124 | t.Errorf("IsZero() should be false") 125 | } 126 | 127 | null := NewInt(0, false) 128 | if !null.IsZero() { 129 | t.Errorf("IsZero() should be true") 130 | } 131 | 132 | zero := NewInt(0, true) 133 | if zero.IsZero() { 134 | t.Errorf("IsZero() should be false") 135 | } 136 | } 137 | 138 | func TestIntSetValid(t *testing.T) { 139 | change := NewInt(0, false) 140 | assertNullInt(t, change, "SetValid()") 141 | change.SetValid(12345) 142 | assertInt(t, change, "SetValid()") 143 | } 144 | 145 | func TestIntScan(t *testing.T) { 146 | var i Int 147 | err := i.Scan(12345) 148 | maybePanic(err) 149 | assertInt(t, i, "scanned int") 150 | 151 | var null Int 152 | err = null.Scan(nil) 153 | maybePanic(err) 154 | assertNullInt(t, null, "scanned null") 155 | } 156 | 157 | func assertInt(t *testing.T, i Int, from string) { 158 | if i.Int != 12345 { 159 | t.Errorf("bad %s int: %d ≠ %d\n", from, i.Int, 12345) 160 | } 161 | if !i.Valid { 162 | t.Error(from, "is invalid, but should be valid") 163 | } 164 | } 165 | 166 | func assertNullInt(t *testing.T, i Int, from string) { 167 | if i.Valid { 168 | t.Error(from, "is valid, but should be invalid") 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /uint_test.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | ) 7 | 8 | var ( 9 | uintJSON = []byte(`12345`) 10 | ) 11 | 12 | func TestUintFrom(t *testing.T) { 13 | i := UintFrom(12345) 14 | assertUint(t, i, "UintFrom()") 15 | 16 | zero := UintFrom(0) 17 | if !zero.Valid { 18 | t.Error("UintFrom(0)", "is invalid, but should be valid") 19 | } 20 | } 21 | 22 | func TestUintFromPtr(t *testing.T) { 23 | n := uint(12345) 24 | iptr := &n 25 | i := UintFromPtr(iptr) 26 | assertUint(t, i, "UintFromPtr()") 27 | 28 | null := UintFromPtr(nil) 29 | assertNullUint(t, null, "UintFromPtr(nil)") 30 | } 31 | 32 | func TestUnmarshalUint(t *testing.T) { 33 | var i Uint 34 | err := json.Unmarshal(uintJSON, &i) 35 | maybePanic(err) 36 | assertUint(t, i, "uint json") 37 | 38 | var null Uint 39 | err = json.Unmarshal(nullJSON, &null) 40 | maybePanic(err) 41 | assertNullUint(t, null, "null json") 42 | if !null.Set { 43 | t.Error("should be Set") 44 | } 45 | 46 | var badType Uint 47 | err = json.Unmarshal(boolJSON, &badType) 48 | if err == nil { 49 | panic("err should not be nil") 50 | } 51 | assertNullUint(t, badType, "wrong type json") 52 | 53 | var invalid Uint 54 | err = invalid.UnmarshalJSON(invalidJSON) 55 | if _, ok := err.(*json.SyntaxError); !ok { 56 | t.Errorf("expected json.SyntaxError, not %T", err) 57 | } 58 | assertNullUint(t, invalid, "invalid json") 59 | } 60 | 61 | func TestUnmarshalNonUintegerNumber(t *testing.T) { 62 | var i Uint 63 | err := json.Unmarshal(float64JSON, &i) 64 | if err == nil { 65 | panic("err should be present; non-uinteger number coerced to uint") 66 | } 67 | } 68 | 69 | func TestTextUnmarshalUint(t *testing.T) { 70 | var i Uint 71 | err := i.UnmarshalText([]byte("12345")) 72 | maybePanic(err) 73 | assertUint(t, i, "UnmarshalText() uint") 74 | 75 | var blank Uint 76 | err = blank.UnmarshalText([]byte("")) 77 | maybePanic(err) 78 | assertNullUint(t, blank, "UnmarshalText() empty uint") 79 | } 80 | 81 | func TestMarshalUint(t *testing.T) { 82 | i := UintFrom(12345) 83 | data, err := json.Marshal(i) 84 | maybePanic(err) 85 | assertJSONEquals(t, data, "12345", "non-empty json marshal") 86 | 87 | // invalid values should be encoded as null 88 | null := NewUint(0, false) 89 | data, err = json.Marshal(null) 90 | maybePanic(err) 91 | assertJSONEquals(t, data, "null", "null json marshal") 92 | } 93 | 94 | func TestMarshalUintText(t *testing.T) { 95 | i := UintFrom(12345) 96 | data, err := i.MarshalText() 97 | maybePanic(err) 98 | assertJSONEquals(t, data, "12345", "non-empty text marshal") 99 | 100 | // invalid values should be encoded as null 101 | null := NewUint(0, false) 102 | data, err = null.MarshalText() 103 | maybePanic(err) 104 | assertJSONEquals(t, data, "", "null text marshal") 105 | } 106 | 107 | func TestUintPointer(t *testing.T) { 108 | i := UintFrom(12345) 109 | ptr := i.Ptr() 110 | if *ptr != 12345 { 111 | t.Errorf("bad %s uint: %#v ≠ %d\n", "pointer", ptr, 12345) 112 | } 113 | 114 | null := NewUint(0, false) 115 | ptr = null.Ptr() 116 | if ptr != nil { 117 | t.Errorf("bad %s uint: %#v ≠ %s\n", "nil pointer", ptr, "nil") 118 | } 119 | } 120 | 121 | func TestUintIsZero(t *testing.T) { 122 | i := UintFrom(12345) 123 | if i.IsZero() { 124 | t.Errorf("IsZero() should be false") 125 | } 126 | 127 | null := NewUint(0, false) 128 | if !null.IsZero() { 129 | t.Errorf("IsZero() should be true") 130 | } 131 | 132 | zero := NewUint(0, true) 133 | if zero.IsZero() { 134 | t.Errorf("IsZero() should be false") 135 | } 136 | } 137 | 138 | func TestUintSetValid(t *testing.T) { 139 | change := NewUint(0, false) 140 | assertNullUint(t, change, "SetValid()") 141 | change.SetValid(12345) 142 | assertUint(t, change, "SetValid()") 143 | } 144 | 145 | func TestUintScan(t *testing.T) { 146 | var i Uint 147 | err := i.Scan(12345) 148 | maybePanic(err) 149 | assertUint(t, i, "scanned uint") 150 | 151 | var null Uint 152 | err = null.Scan(nil) 153 | maybePanic(err) 154 | assertNullUint(t, null, "scanned null") 155 | } 156 | 157 | func assertUint(t *testing.T, i Uint, from string) { 158 | if i.Uint != 12345 { 159 | t.Errorf("bad %s uint: %d ≠ %d\n", from, i.Uint, 12345) 160 | } 161 | if !i.Valid { 162 | t.Error(from, "is invalid, but should be valid") 163 | } 164 | } 165 | 166 | func assertNullUint(t *testing.T, i Uint, from string) { 167 | if i.Valid { 168 | t.Error(from, "is valid, but should be invalid") 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /bytes_test.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "testing" 7 | ) 8 | 9 | var ( 10 | hello = []byte("hello") 11 | bytesJSON = []byte(`"hello"`) 12 | b64BytesJSON = []byte(`"aGVsbG8="`) 13 | ) 14 | 15 | func TestBytesFrom(t *testing.T) { 16 | i := BytesFrom(hello) 17 | assertBytes(t, i, "BytesFrom()") 18 | 19 | zero := BytesFrom(nil) 20 | if zero.Valid { 21 | t.Error("BytesFrom(nil)", "is valid, but should be invalid") 22 | } 23 | 24 | zero = BytesFrom([]byte{}) 25 | if !zero.Valid { 26 | t.Error("BytesFrom([]byte{})", "is invalid, but should be valid") 27 | } 28 | } 29 | 30 | func TestBytesFromPtr(t *testing.T) { 31 | n := hello 32 | iptr := &n 33 | i := BytesFromPtr(iptr) 34 | assertBytes(t, i, "BytesFromPtr()") 35 | 36 | null := BytesFromPtr(nil) 37 | assertNullBytes(t, null, "BytesFromPtr(nil)") 38 | } 39 | 40 | func TestUnmarshalBytes(t *testing.T) { 41 | var i Bytes 42 | err := json.Unmarshal(b64BytesJSON, &i) 43 | maybePanic(err) 44 | assertBytes(t, i, "[]byte json") 45 | 46 | var ni Bytes 47 | err = ni.UnmarshalJSON([]byte{}) 48 | if err == nil { 49 | t.Errorf("Expected error") 50 | } 51 | 52 | var null Bytes 53 | err = null.UnmarshalJSON(NullBytes) 54 | if err != nil { 55 | t.Error(err) 56 | } 57 | if null.Valid { 58 | t.Errorf("expected Valid to be false, got true") 59 | } 60 | if null.Bytes != nil { 61 | t.Errorf("Expected Bytes to be nil, but was not: %#v %#v", null.Bytes, []byte(`null`)) 62 | } 63 | if !null.Set { 64 | t.Errorf("Expected Set to be true; got false") 65 | } 66 | } 67 | 68 | func TestTextUnmarshalBytes(t *testing.T) { 69 | var i Bytes 70 | err := i.UnmarshalText(hello) 71 | maybePanic(err) 72 | assertBytes(t, i, "UnmarshalText() []byte") 73 | 74 | var blank Bytes 75 | err = blank.UnmarshalText([]byte("")) 76 | maybePanic(err) 77 | assertNullBytes(t, blank, "UnmarshalText() empty []byte") 78 | } 79 | 80 | func TestMarshalBytes(t *testing.T) { 81 | i := BytesFrom(hello) 82 | data, err := json.Marshal(i) 83 | maybePanic(err) 84 | assertJSONEquals(t, data, string(b64BytesJSON), "non-empty json marshal") 85 | 86 | // invalid values should be encoded as null 87 | null := NewBytes(nil, false) 88 | data, err = json.Marshal(null) 89 | maybePanic(err) 90 | assertJSONEquals(t, data, "null", "null json marshal") 91 | } 92 | 93 | func TestMarshalBytesText(t *testing.T) { 94 | i := BytesFrom(bytesJSON) 95 | data, err := i.MarshalText() 96 | maybePanic(err) 97 | assertJSONEquals(t, data, `"hello"`, "non-empty text marshal") 98 | 99 | // invalid values should be encoded as null 100 | null := NewBytes(nil, false) 101 | data, err = null.MarshalText() 102 | maybePanic(err) 103 | assertJSONEquals(t, data, "", "null text marshal") 104 | } 105 | 106 | func TestBytesPointer(t *testing.T) { 107 | i := BytesFrom([]byte(`"hello"`)) 108 | ptr := i.Ptr() 109 | if !bytes.Equal(*ptr, bytesJSON) { 110 | t.Errorf("bad %s []byte: %#v ≠ %s\n", "pointer", ptr, `"hello"`) 111 | } 112 | 113 | null := NewBytes(nil, false) 114 | ptr = null.Ptr() 115 | if ptr != nil { 116 | t.Errorf("bad %s []byte: %#v ≠ %s\n", "nil pointer", ptr, "nil") 117 | } 118 | } 119 | 120 | func TestBytesIsZero(t *testing.T) { 121 | i := BytesFrom(bytesJSON) 122 | if i.IsZero() { 123 | t.Errorf("IsZero() should be false") 124 | } 125 | 126 | null := NewBytes(nil, false) 127 | if !null.IsZero() { 128 | t.Errorf("IsZero() should be true") 129 | } 130 | 131 | zero := NewBytes(nil, true) 132 | if zero.IsZero() { 133 | t.Errorf("IsZero() should be false") 134 | } 135 | } 136 | 137 | func TestBytesSetValid(t *testing.T) { 138 | change := NewBytes(nil, false) 139 | assertNullBytes(t, change, "SetValid()") 140 | change.SetValid(hello) 141 | assertBytes(t, change, "SetValid()") 142 | } 143 | 144 | func TestBytesScan(t *testing.T) { 145 | var i Bytes 146 | err := i.Scan(`hello`) 147 | maybePanic(err) 148 | assertBytes(t, i, "Scan() []byte") 149 | 150 | var null Bytes 151 | err = null.Scan(nil) 152 | maybePanic(err) 153 | assertNullBytes(t, null, "scanned null") 154 | } 155 | 156 | func assertBytes(t *testing.T, i Bytes, from string) { 157 | if !bytes.Equal(i.Bytes, hello) { 158 | t.Errorf("bad %s []byte: %v ≠ %v\n", from, string(i.Bytes), "hello") 159 | } 160 | if !i.Valid { 161 | t.Error(from, "is invalid, but should be valid") 162 | } 163 | } 164 | 165 | func assertNullBytes(t *testing.T, i Bytes, from string) { 166 | if i.Valid { 167 | t.Error(from, "is valid, but should be invalid") 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /time_test.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | var ( 10 | timeString = "2012-12-21T21:21:21Z" 11 | timeJSON = []byte(`"` + timeString + `"`) 12 | nullTimeJSON = []byte(`null`) 13 | timeValue, _ = time.Parse(time.RFC3339, timeString) 14 | badObject = []byte(`{"hello": "world"}`) 15 | ) 16 | 17 | func TestUnmarshalTimeJSON(t *testing.T) { 18 | var ti Time 19 | err := json.Unmarshal(timeJSON, &ti) 20 | maybePanic(err) 21 | assertTime(t, ti, "UnmarshalJSON() json") 22 | 23 | var null Time 24 | err = json.Unmarshal(nullTimeJSON, &null) 25 | maybePanic(err) 26 | assertNullTime(t, null, "null time json") 27 | if !null.Set { 28 | t.Error("should be Set") 29 | } 30 | 31 | var invalid Time 32 | err = invalid.UnmarshalJSON(invalidJSON) 33 | if _, ok := err.(*time.ParseError); !ok { 34 | t.Errorf("expected json.ParseError, not %T", err) 35 | } 36 | assertNullTime(t, invalid, "invalid from object json") 37 | 38 | var bad Time 39 | err = json.Unmarshal(badObject, &bad) 40 | if err == nil { 41 | t.Errorf("expected error: bad object") 42 | } 43 | assertNullTime(t, bad, "bad from object json") 44 | 45 | var wrongType Time 46 | err = json.Unmarshal(intJSON, &wrongType) 47 | if err == nil { 48 | t.Errorf("expected error: wrong type JSON") 49 | } 50 | assertNullTime(t, wrongType, "wrong type object json") 51 | } 52 | 53 | func TestUnmarshalTimeText(t *testing.T) { 54 | ti := TimeFrom(timeValue) 55 | txt, err := ti.MarshalText() 56 | maybePanic(err) 57 | assertJSONEquals(t, txt, timeString, "marshal text") 58 | 59 | var unmarshal Time 60 | err = unmarshal.UnmarshalText(txt) 61 | maybePanic(err) 62 | assertTime(t, unmarshal, "unmarshal text") 63 | 64 | var invalid Time 65 | err = invalid.UnmarshalText([]byte("hello world")) 66 | if err == nil { 67 | t.Error("expected error") 68 | } 69 | assertNullTime(t, invalid, "bad string") 70 | } 71 | 72 | func TestMarshalTime(t *testing.T) { 73 | ti := TimeFrom(timeValue) 74 | data, err := json.Marshal(ti) 75 | maybePanic(err) 76 | assertJSONEquals(t, data, string(timeJSON), "non-empty json marshal") 77 | 78 | ti.Valid = false 79 | data, err = json.Marshal(ti) 80 | maybePanic(err) 81 | assertJSONEquals(t, data, string(nullJSON), "null json marshal") 82 | } 83 | 84 | func TestTimeFrom(t *testing.T) { 85 | ti := TimeFrom(timeValue) 86 | assertTime(t, ti, "TimeFrom() time.Time") 87 | } 88 | 89 | func TestTimeFromPtr(t *testing.T) { 90 | ti := TimeFromPtr(&timeValue) 91 | assertTime(t, ti, "TimeFromPtr() time") 92 | 93 | null := TimeFromPtr(nil) 94 | assertNullTime(t, null, "TimeFromPtr(nil)") 95 | } 96 | 97 | func TestTimeSetValid(t *testing.T) { 98 | var ti time.Time 99 | change := NewTime(ti, false) 100 | assertNullTime(t, change, "SetValid()") 101 | change.SetValid(timeValue) 102 | assertTime(t, change, "SetValid()") 103 | } 104 | 105 | func TestTimePointer(t *testing.T) { 106 | ti := TimeFrom(timeValue) 107 | ptr := ti.Ptr() 108 | if *ptr != timeValue { 109 | t.Errorf("bad %s time: %#v ≠ %v\n", "pointer", ptr, timeValue) 110 | } 111 | 112 | var nt time.Time 113 | null := NewTime(nt, false) 114 | ptr = null.Ptr() 115 | if ptr != nil { 116 | t.Errorf("bad %s time: %#v ≠ %s\n", "nil pointer", ptr, "nil") 117 | } 118 | } 119 | 120 | func TestTimeIsZero(t *testing.T) { 121 | ti := TimeFrom(time.Now()) 122 | if ti.IsZero() { 123 | t.Errorf("IsZero() should be false") 124 | } 125 | 126 | null := TimeFromPtr(nil) 127 | if !null.IsZero() { 128 | t.Errorf("IsZero() should be true") 129 | } 130 | } 131 | 132 | func TestTimeScanValue(t *testing.T) { 133 | var ti Time 134 | err := ti.Scan(timeValue) 135 | maybePanic(err) 136 | assertTime(t, ti, "scanned time") 137 | if v, err := ti.Value(); v != timeValue || err != nil { 138 | t.Error("bad value or err:", v, err) 139 | } 140 | 141 | var null Time 142 | err = null.Scan(nil) 143 | maybePanic(err) 144 | assertNullTime(t, null, "scanned null") 145 | if v, err := null.Value(); v != nil || err != nil { 146 | t.Error("bad value or err:", v, err) 147 | } 148 | 149 | var wrong Time 150 | err = wrong.Scan(int64(42)) 151 | if err == nil { 152 | t.Error("expected error") 153 | } 154 | assertNullTime(t, wrong, "scanned wrong") 155 | } 156 | 157 | func assertTime(t *testing.T, ti Time, from string) { 158 | if ti.Time != timeValue { 159 | t.Errorf("bad %v time: %v ≠ %v\n", from, ti.Time, timeValue) 160 | } 161 | if !ti.Valid { 162 | t.Error(from, "is invalid, but should be valid") 163 | } 164 | } 165 | 166 | func assertNullTime(t *testing.T, ti Time, from string) { 167 | if ti.Valid { 168 | t.Error(from, "is valid, but should be invalid") 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /uint64_test.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | ) 7 | 8 | var ( 9 | uint64JSON = []byte(`18446744073709551614`) 10 | ) 11 | 12 | func TestUint64From(t *testing.T) { 13 | i := Uint64From(18446744073709551614) 14 | assertUint64(t, i, "Uint64From()") 15 | 16 | zero := Uint64From(0) 17 | if !zero.Valid { 18 | t.Error("Uint64From(0)", "is invalid, but should be valid") 19 | } 20 | } 21 | 22 | func TestUint64FromPtr(t *testing.T) { 23 | n := uint64(18446744073709551614) 24 | iptr := &n 25 | i := Uint64FromPtr(iptr) 26 | assertUint64(t, i, "Uint64FromPtr()") 27 | 28 | null := Uint64FromPtr(nil) 29 | assertNullUint64(t, null, "Uint64FromPtr(nil)") 30 | } 31 | 32 | func TestUnmarshalUint64(t *testing.T) { 33 | var i Uint64 34 | err := json.Unmarshal(uint64JSON, &i) 35 | maybePanic(err) 36 | assertUint64(t, i, "uint64 json") 37 | 38 | var null Uint64 39 | err = json.Unmarshal(nullJSON, &null) 40 | maybePanic(err) 41 | assertNullUint64(t, null, "null json") 42 | if !null.Set { 43 | t.Error("should be Set") 44 | } 45 | 46 | var badType Uint64 47 | err = json.Unmarshal(boolJSON, &badType) 48 | if err == nil { 49 | panic("err should not be nil") 50 | } 51 | assertNullUint64(t, badType, "wrong type json") 52 | 53 | var invalid Uint64 54 | err = invalid.UnmarshalJSON(invalidJSON) 55 | if _, ok := err.(*json.SyntaxError); !ok { 56 | t.Errorf("expected json.SyntaxError, not %T", err) 57 | } 58 | assertNullUint64(t, invalid, "invalid json") 59 | } 60 | 61 | func TestUnmarshalNonUintegerNumber64(t *testing.T) { 62 | var i Uint64 63 | err := json.Unmarshal(float64JSON, &i) 64 | if err == nil { 65 | panic("err should be present; non-integer number coerced to uint64") 66 | } 67 | } 68 | 69 | func TestTextUnmarshalUint64(t *testing.T) { 70 | var i Uint64 71 | err := i.UnmarshalText([]byte("18446744073709551614")) 72 | maybePanic(err) 73 | assertUint64(t, i, "UnmarshalText() uint64") 74 | 75 | var blank Uint64 76 | err = blank.UnmarshalText([]byte("")) 77 | maybePanic(err) 78 | assertNullUint64(t, blank, "UnmarshalText() empty uint64") 79 | } 80 | 81 | func TestMarshalUint64(t *testing.T) { 82 | i := Uint64From(18446744073709551614) 83 | data, err := json.Marshal(i) 84 | maybePanic(err) 85 | assertJSONEquals(t, data, "18446744073709551614", "non-empty json marshal") 86 | 87 | // invalid values should be encoded as null 88 | null := NewUint64(0, false) 89 | data, err = json.Marshal(null) 90 | maybePanic(err) 91 | assertJSONEquals(t, data, "null", "null json marshal") 92 | } 93 | 94 | func TestMarshalUint64Text(t *testing.T) { 95 | i := Uint64From(18446744073709551614) 96 | data, err := i.MarshalText() 97 | maybePanic(err) 98 | assertJSONEquals(t, data, "18446744073709551614", "non-empty text marshal") 99 | 100 | // invalid values should be encoded as null 101 | null := NewUint64(0, false) 102 | data, err = null.MarshalText() 103 | maybePanic(err) 104 | assertJSONEquals(t, data, "", "null text marshal") 105 | } 106 | 107 | func TestUint64Pointer(t *testing.T) { 108 | i := Uint64From(18446744073709551614) 109 | ptr := i.Ptr() 110 | if *ptr != 18446744073709551614 { 111 | t.Errorf("bad %s uint64: %#v ≠ %d\n", "pointer", ptr, uint64(18446744073709551614)) 112 | } 113 | 114 | null := NewUint64(0, false) 115 | ptr = null.Ptr() 116 | if ptr != nil { 117 | t.Errorf("bad %s uint64: %#v ≠ %s\n", "nil pointer", ptr, "nil") 118 | } 119 | } 120 | 121 | func TestUint64IsZero(t *testing.T) { 122 | i := Uint64From(18446744073709551614) 123 | if i.IsZero() { 124 | t.Errorf("IsZero() should be false") 125 | } 126 | 127 | null := NewUint64(0, false) 128 | if !null.IsZero() { 129 | t.Errorf("IsZero() should be true") 130 | } 131 | 132 | zero := NewUint64(0, true) 133 | if zero.IsZero() { 134 | t.Errorf("IsZero() should be false") 135 | } 136 | } 137 | 138 | func TestUint64SetValid(t *testing.T) { 139 | change := NewUint64(0, false) 140 | assertNullUint64(t, change, "SetValid()") 141 | change.SetValid(18446744073709551614) 142 | assertUint64(t, change, "SetValid()") 143 | } 144 | 145 | func TestUint64Scan(t *testing.T) { 146 | var i Uint64 147 | err := i.Scan(uint64(18446744073709551614)) 148 | maybePanic(err) 149 | assertUint64(t, i, "scanned uint64") 150 | 151 | err = i.Scan(int64(-2)) 152 | maybePanic(err) 153 | assertUint64(t, i, "scanned int64") 154 | 155 | err = i.Scan(nil) 156 | maybePanic(err) 157 | assertNullUint64(t, i, "scanned null") 158 | } 159 | 160 | func assertUint64(t *testing.T, i Uint64, from string) { 161 | if i.Uint64 != 18446744073709551614 { 162 | t.Errorf("bad %s uint64: %d ≠ %d\n", from, i.Uint64, uint64(18446744073709551614)) 163 | } 164 | if !i.Valid { 165 | t.Error(from, "is invalid, but should be valid") 166 | } 167 | } 168 | 169 | func assertNullUint64(t *testing.T, i Uint64, from string) { 170 | if i.Valid { 171 | t.Error(from, "is valid, but should be invalid") 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /int8_test.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "encoding/json" 5 | "math" 6 | "strconv" 7 | "testing" 8 | ) 9 | 10 | var ( 11 | int8JSON = []byte(`126`) 12 | ) 13 | 14 | func TestInt8From(t *testing.T) { 15 | i := Int8From(126) 16 | assertInt8(t, i, "Int8From()") 17 | 18 | zero := Int8From(0) 19 | if !zero.Valid { 20 | t.Error("Int8From(0)", "is invalid, but should be valid") 21 | } 22 | } 23 | 24 | func TestInt8FromPtr(t *testing.T) { 25 | n := int8(126) 26 | iptr := &n 27 | i := Int8FromPtr(iptr) 28 | assertInt8(t, i, "Int8FromPtr()") 29 | 30 | null := Int8FromPtr(nil) 31 | assertNullInt8(t, null, "Int8FromPtr(nil)") 32 | } 33 | 34 | func TestUnmarshalInt8(t *testing.T) { 35 | var i Int8 36 | err := json.Unmarshal(int8JSON, &i) 37 | maybePanic(err) 38 | assertInt8(t, i, "int8 json") 39 | 40 | var null Int8 41 | err = json.Unmarshal(nullJSON, &null) 42 | maybePanic(err) 43 | assertNullInt8(t, null, "null json") 44 | if !null.Set { 45 | t.Error("should be Set") 46 | } 47 | 48 | var badType Int8 49 | err = json.Unmarshal(boolJSON, &badType) 50 | if err == nil { 51 | panic("err should not be nil") 52 | } 53 | assertNullInt8(t, badType, "wrong type json") 54 | 55 | var invalid Int8 56 | err = invalid.UnmarshalJSON(invalidJSON) 57 | if _, ok := err.(*json.SyntaxError); !ok { 58 | t.Errorf("expected json.SyntaxError, not %T", err) 59 | } 60 | assertNullInt8(t, invalid, "invalid json") 61 | } 62 | 63 | func TestUnmarshalNonIntegerNumber8(t *testing.T) { 64 | var i Int8 65 | err := json.Unmarshal(float64JSON, &i) 66 | if err == nil { 67 | panic("err should be present; non-integer number coerced to int8") 68 | } 69 | } 70 | 71 | func TestUnmarshalInt8Overflow(t *testing.T) { 72 | int8Overflow := uint8(math.MaxInt8) 73 | 74 | // Max int8 should decode successfully 75 | var i Int8 76 | err := json.Unmarshal([]byte(strconv.FormatUint(uint64(int8Overflow), 10)), &i) 77 | maybePanic(err) 78 | 79 | // Attempt to overflow 80 | int8Overflow++ 81 | err = json.Unmarshal([]byte(strconv.FormatUint(uint64(int8Overflow), 10)), &i) 82 | if err == nil { 83 | panic("err should be present; decoded value overflows int8") 84 | } 85 | } 86 | 87 | func TestTextUnmarshalInt8(t *testing.T) { 88 | var i Int8 89 | err := i.UnmarshalText([]byte("126")) 90 | maybePanic(err) 91 | assertInt8(t, i, "UnmarshalText() int8") 92 | 93 | var blank Int8 94 | err = blank.UnmarshalText([]byte("")) 95 | maybePanic(err) 96 | assertNullInt8(t, blank, "UnmarshalText() empty int8") 97 | } 98 | 99 | func TestMarshalInt8(t *testing.T) { 100 | i := Int8From(126) 101 | data, err := json.Marshal(i) 102 | maybePanic(err) 103 | assertJSONEquals(t, data, "126", "non-empty json marshal") 104 | 105 | // invalid values should be encoded as null 106 | null := NewInt8(0, false) 107 | data, err = json.Marshal(null) 108 | maybePanic(err) 109 | assertJSONEquals(t, data, "null", "null json marshal") 110 | } 111 | 112 | func TestMarshalInt8Text(t *testing.T) { 113 | i := Int8From(126) 114 | data, err := i.MarshalText() 115 | maybePanic(err) 116 | assertJSONEquals(t, data, "126", "non-empty text marshal") 117 | 118 | // invalid values should be encoded as null 119 | null := NewInt8(0, false) 120 | data, err = null.MarshalText() 121 | maybePanic(err) 122 | assertJSONEquals(t, data, "", "null text marshal") 123 | } 124 | 125 | func TestInt8Pointer(t *testing.T) { 126 | i := Int8From(126) 127 | ptr := i.Ptr() 128 | if *ptr != 126 { 129 | t.Errorf("bad %s int8: %#v ≠ %d\n", "pointer", ptr, 126) 130 | } 131 | 132 | null := NewInt8(0, false) 133 | ptr = null.Ptr() 134 | if ptr != nil { 135 | t.Errorf("bad %s int8: %#v ≠ %s\n", "nil pointer", ptr, "nil") 136 | } 137 | } 138 | 139 | func TestInt8IsZero(t *testing.T) { 140 | i := Int8From(126) 141 | if i.IsZero() { 142 | t.Errorf("IsZero() should be false") 143 | } 144 | 145 | null := NewInt8(0, false) 146 | if !null.IsZero() { 147 | t.Errorf("IsZero() should be true") 148 | } 149 | 150 | zero := NewInt8(0, true) 151 | if zero.IsZero() { 152 | t.Errorf("IsZero() should be false") 153 | } 154 | } 155 | 156 | func TestInt8SetValid(t *testing.T) { 157 | change := NewInt8(0, false) 158 | assertNullInt8(t, change, "SetValid()") 159 | change.SetValid(126) 160 | assertInt8(t, change, "SetValid()") 161 | } 162 | 163 | func TestInt8Scan(t *testing.T) { 164 | var i Int8 165 | err := i.Scan(126) 166 | maybePanic(err) 167 | assertInt8(t, i, "scanned int8") 168 | 169 | var null Int8 170 | err = null.Scan(nil) 171 | maybePanic(err) 172 | assertNullInt8(t, null, "scanned null") 173 | } 174 | 175 | func assertInt8(t *testing.T, i Int8, from string) { 176 | if i.Int8 != 126 { 177 | t.Errorf("bad %s int8: %d ≠ %d\n", from, i.Int8, 126) 178 | } 179 | if !i.Valid { 180 | t.Error(from, "is invalid, but should be valid") 181 | } 182 | } 183 | 184 | func assertNullInt8(t *testing.T, i Int8, from string) { 185 | if i.Valid { 186 | t.Error(from, "is valid, but should be invalid") 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /bool_test.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | ) 7 | 8 | var ( 9 | boolJSON = []byte(`true`) 10 | ) 11 | 12 | func TestBoolFrom(t *testing.T) { 13 | b := BoolFrom(true) 14 | assertBool(t, b, "BoolFrom()") 15 | 16 | zero := BoolFrom(false) 17 | if !zero.Valid { 18 | t.Error("BoolFrom(false)", "is invalid, but should be valid") 19 | } 20 | } 21 | 22 | func TestBoolFromPtr(t *testing.T) { 23 | n := true 24 | bptr := &n 25 | b := BoolFromPtr(bptr) 26 | assertBool(t, b, "BoolFromPtr()") 27 | 28 | null := BoolFromPtr(nil) 29 | assertNullBool(t, null, "BoolFromPtr(nil)") 30 | } 31 | 32 | func TestUnmarshalBool(t *testing.T) { 33 | var null Bool 34 | err := json.Unmarshal(nullJSON, &null) 35 | maybePanic(err) 36 | assertNullBool(t, null, "null json") 37 | if !null.Set { 38 | t.Error("should be Set", err) 39 | } 40 | 41 | var badType Bool 42 | err = json.Unmarshal(intJSON, &badType) 43 | if err == nil { 44 | panic("err should not be nil") 45 | } 46 | assertNullBool(t, badType, "wrong type json") 47 | 48 | var invalid Bool 49 | err = invalid.UnmarshalJSON(invalidJSON) 50 | if _, ok := err.(*json.SyntaxError); !ok { 51 | t.Errorf("expected json.SyntaxError, not %T", err) 52 | } 53 | } 54 | 55 | func TestTextUnmarshalBool(t *testing.T) { 56 | var b Bool 57 | err := b.UnmarshalText([]byte("true")) 58 | maybePanic(err) 59 | assertBool(t, b, "UnmarshalText() bool") 60 | 61 | var zero Bool 62 | err = zero.UnmarshalText([]byte("false")) 63 | maybePanic(err) 64 | assertFalseBool(t, zero, "UnmarshalText() false") 65 | 66 | var blank Bool 67 | err = blank.UnmarshalText([]byte("")) 68 | maybePanic(err) 69 | assertNullBool(t, blank, "UnmarshalText() empty bool") 70 | 71 | var invalid Bool 72 | err = invalid.UnmarshalText([]byte(":D")) 73 | if err == nil { 74 | panic("err should not be nil") 75 | } 76 | assertNullBool(t, invalid, "invalid json") 77 | } 78 | 79 | func TestMarshalBool(t *testing.T) { 80 | b := BoolFrom(true) 81 | data, err := json.Marshal(b) 82 | maybePanic(err) 83 | assertJSONEquals(t, data, "true", "non-empty json marshal") 84 | 85 | zero := NewBool(false, true) 86 | data, err = json.Marshal(zero) 87 | maybePanic(err) 88 | assertJSONEquals(t, data, "false", "zero json marshal") 89 | 90 | // invalid values should be encoded as null 91 | null := NewBool(false, false) 92 | data, err = json.Marshal(null) 93 | maybePanic(err) 94 | assertJSONEquals(t, data, "null", "null json marshal") 95 | } 96 | 97 | func TestMarshalBoolText(t *testing.T) { 98 | b := BoolFrom(true) 99 | data, err := b.MarshalText() 100 | maybePanic(err) 101 | assertJSONEquals(t, data, "true", "non-empty text marshal") 102 | 103 | zero := NewBool(false, true) 104 | data, err = zero.MarshalText() 105 | maybePanic(err) 106 | assertJSONEquals(t, data, "false", "zero text marshal") 107 | 108 | // invalid values should be encoded as null 109 | null := NewBool(false, false) 110 | data, err = null.MarshalText() 111 | maybePanic(err) 112 | assertJSONEquals(t, data, "", "null text marshal") 113 | } 114 | 115 | func TestBoolPointer(t *testing.T) { 116 | b := BoolFrom(true) 117 | ptr := b.Ptr() 118 | if *ptr != true { 119 | t.Errorf("bad %s bool: %#v ≠ %v\n", "pointer", ptr, true) 120 | } 121 | 122 | null := NewBool(false, false) 123 | ptr = null.Ptr() 124 | if ptr != nil { 125 | t.Errorf("bad %s bool: %#v ≠ %s\n", "nil pointer", ptr, "nil") 126 | } 127 | } 128 | 129 | func TestBoolIsZero(t *testing.T) { 130 | b := BoolFrom(true) 131 | if b.IsZero() { 132 | t.Errorf("IsZero() should be false") 133 | } 134 | 135 | null := NewBool(false, false) 136 | if !null.IsZero() { 137 | t.Errorf("IsZero() should be true") 138 | } 139 | 140 | zero := NewBool(false, true) 141 | if zero.IsZero() { 142 | t.Errorf("IsZero() should be false") 143 | } 144 | } 145 | 146 | func TestBoolSetValid(t *testing.T) { 147 | change := NewBool(false, false) 148 | assertNullBool(t, change, "SetValid()") 149 | change.SetValid(true) 150 | assertBool(t, change, "SetValid()") 151 | } 152 | 153 | func TestBoolScan(t *testing.T) { 154 | var b Bool 155 | err := b.Scan(true) 156 | maybePanic(err) 157 | assertBool(t, b, "scanned bool") 158 | 159 | var null Bool 160 | err = null.Scan(nil) 161 | maybePanic(err) 162 | assertNullBool(t, null, "scanned null") 163 | } 164 | 165 | func assertBool(t *testing.T, b Bool, from string) { 166 | if b.Bool != true { 167 | t.Errorf("bad %s bool: %v ≠ %v\n", from, b.Bool, true) 168 | } 169 | if !b.Valid { 170 | t.Error(from, "is invalid, but should be valid") 171 | } 172 | } 173 | 174 | func assertFalseBool(t *testing.T, b Bool, from string) { 175 | if b.Bool != false { 176 | t.Errorf("bad %s bool: %v ≠ %v\n", from, b.Bool, false) 177 | } 178 | if !b.Valid { 179 | t.Error(from, "is invalid, but should be valid") 180 | } 181 | } 182 | 183 | func assertNullBool(t *testing.T, b Bool, from string) { 184 | if b.Valid { 185 | t.Error(from, "is valid, but should be invalid") 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /uint8_test.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "encoding/json" 5 | "math" 6 | "strconv" 7 | "testing" 8 | ) 9 | 10 | var ( 11 | uint8JSON = []byte(`254`) 12 | ) 13 | 14 | func TestUint8From(t *testing.T) { 15 | i := Uint8From(254) 16 | assertUint8(t, i, "Uint8From()") 17 | 18 | zero := Uint8From(0) 19 | if !zero.Valid { 20 | t.Error("Uint8From(0)", "is invalid, but should be valid") 21 | } 22 | } 23 | 24 | func TestUint8FromPtr(t *testing.T) { 25 | n := uint8(254) 26 | iptr := &n 27 | i := Uint8FromPtr(iptr) 28 | assertUint8(t, i, "Uint8FromPtr()") 29 | 30 | null := Uint8FromPtr(nil) 31 | assertNullUint8(t, null, "Uint8FromPtr(nil)") 32 | } 33 | 34 | func TestUnmarshalUint8(t *testing.T) { 35 | var i Uint8 36 | err := json.Unmarshal(uint8JSON, &i) 37 | maybePanic(err) 38 | assertUint8(t, i, "uint8 json") 39 | 40 | var null Uint8 41 | err = json.Unmarshal(nullJSON, &null) 42 | maybePanic(err) 43 | assertNullUint8(t, null, "null json") 44 | if !null.Set { 45 | t.Error("should be Set") 46 | } 47 | 48 | var badType Uint8 49 | err = json.Unmarshal(boolJSON, &badType) 50 | if err == nil { 51 | panic("err should not be nil") 52 | } 53 | assertNullUint8(t, badType, "wrong type json") 54 | 55 | var invalid Uint8 56 | err = invalid.UnmarshalJSON(invalidJSON) 57 | if _, ok := err.(*json.SyntaxError); !ok { 58 | t.Errorf("expected json.SyntaxError, not %T", err) 59 | } 60 | assertNullUint8(t, invalid, "invalid json") 61 | } 62 | 63 | func TestUnmarshalNonUintegerNumber8(t *testing.T) { 64 | var i Uint8 65 | err := json.Unmarshal(float64JSON, &i) 66 | if err == nil { 67 | panic("err should be present; non-integer number coerced to uint8") 68 | } 69 | } 70 | 71 | func TestUnmarshalUint8Overflow(t *testing.T) { 72 | uint8Overflow := int64(math.MaxUint8) 73 | 74 | // Max uint8 should decode successfully 75 | var i Uint8 76 | err := json.Unmarshal([]byte(strconv.FormatUint(uint64(uint8Overflow), 10)), &i) 77 | maybePanic(err) 78 | 79 | // Attempt to overflow 80 | uint8Overflow++ 81 | err = json.Unmarshal([]byte(strconv.FormatUint(uint64(uint8Overflow), 10)), &i) 82 | if err == nil { 83 | panic("err should be present; decoded value overflows uint8") 84 | } 85 | } 86 | 87 | func TestTextUnmarshalUint8(t *testing.T) { 88 | var i Uint8 89 | err := i.UnmarshalText([]byte("254")) 90 | maybePanic(err) 91 | assertUint8(t, i, "UnmarshalText() uint8") 92 | 93 | var blank Uint8 94 | err = blank.UnmarshalText([]byte("")) 95 | maybePanic(err) 96 | assertNullUint8(t, blank, "UnmarshalText() empty uint8") 97 | } 98 | 99 | func TestMarshalUint8(t *testing.T) { 100 | i := Uint8From(254) 101 | data, err := json.Marshal(i) 102 | maybePanic(err) 103 | assertJSONEquals(t, data, "254", "non-empty json marshal") 104 | 105 | // invalid values should be encoded as null 106 | null := NewUint8(0, false) 107 | data, err = json.Marshal(null) 108 | maybePanic(err) 109 | assertJSONEquals(t, data, "null", "null json marshal") 110 | } 111 | 112 | func TestMarshalUint8Text(t *testing.T) { 113 | i := Uint8From(254) 114 | data, err := i.MarshalText() 115 | maybePanic(err) 116 | assertJSONEquals(t, data, "254", "non-empty text marshal") 117 | 118 | // invalid values should be encoded as null 119 | null := NewUint8(0, false) 120 | data, err = null.MarshalText() 121 | maybePanic(err) 122 | assertJSONEquals(t, data, "", "null text marshal") 123 | } 124 | 125 | func TestUint8Pointer(t *testing.T) { 126 | i := Uint8From(254) 127 | ptr := i.Ptr() 128 | if *ptr != 254 { 129 | t.Errorf("bad %s uint8: %#v ≠ %d\n", "pointer", ptr, 254) 130 | } 131 | 132 | null := NewUint8(0, false) 133 | ptr = null.Ptr() 134 | if ptr != nil { 135 | t.Errorf("bad %s uint8: %#v ≠ %s\n", "nil pointer", ptr, "nil") 136 | } 137 | } 138 | 139 | func TestUint8IsZero(t *testing.T) { 140 | i := Uint8From(254) 141 | if i.IsZero() { 142 | t.Errorf("IsZero() should be false") 143 | } 144 | 145 | null := NewUint8(0, false) 146 | if !null.IsZero() { 147 | t.Errorf("IsZero() should be true") 148 | } 149 | 150 | zero := NewUint8(0, true) 151 | if zero.IsZero() { 152 | t.Errorf("IsZero() should be false") 153 | } 154 | } 155 | 156 | func TestUint8SetValid(t *testing.T) { 157 | change := NewUint8(0, false) 158 | assertNullUint8(t, change, "SetValid()") 159 | change.SetValid(254) 160 | assertUint8(t, change, "SetValid()") 161 | } 162 | 163 | func TestUint8Scan(t *testing.T) { 164 | var i Uint8 165 | err := i.Scan(254) 166 | maybePanic(err) 167 | assertUint8(t, i, "scanned uint8") 168 | 169 | var null Uint8 170 | err = null.Scan(nil) 171 | maybePanic(err) 172 | assertNullUint8(t, null, "scanned null") 173 | } 174 | 175 | func assertUint8(t *testing.T, i Uint8, from string) { 176 | if i.Uint8 != 254 { 177 | t.Errorf("bad %s uint8: %d ≠ %d\n", from, i.Uint8, 254) 178 | } 179 | if !i.Valid { 180 | t.Error(from, "is invalid, but should be valid") 181 | } 182 | } 183 | 184 | func assertNullUint8(t *testing.T, i Uint8, from string) { 185 | if i.Valid { 186 | t.Error(from, "is valid, but should be invalid") 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /int16_test.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "encoding/json" 5 | "math" 6 | "strconv" 7 | "testing" 8 | ) 9 | 10 | var ( 11 | int16JSON = []byte(`32766`) 12 | ) 13 | 14 | func TestInt16From(t *testing.T) { 15 | i := Int16From(32766) 16 | assertInt16(t, i, "Int16From()") 17 | 18 | zero := Int16From(0) 19 | if !zero.Valid { 20 | t.Error("Int16From(0)", "is invalid, but should be valid") 21 | } 22 | } 23 | 24 | func TestInt16FromPtr(t *testing.T) { 25 | n := int16(32766) 26 | iptr := &n 27 | i := Int16FromPtr(iptr) 28 | assertInt16(t, i, "Int16FromPtr()") 29 | 30 | null := Int16FromPtr(nil) 31 | assertNullInt16(t, null, "Int16FromPtr(nil)") 32 | } 33 | 34 | func TestUnmarshalInt16(t *testing.T) { 35 | var i Int16 36 | err := json.Unmarshal(int16JSON, &i) 37 | maybePanic(err) 38 | assertInt16(t, i, "int16 json") 39 | 40 | var null Int16 41 | err = json.Unmarshal(nullJSON, &null) 42 | maybePanic(err) 43 | assertNullInt16(t, null, "null json") 44 | if !null.Set { 45 | t.Error("should be Set") 46 | } 47 | 48 | var badType Int16 49 | err = json.Unmarshal(boolJSON, &badType) 50 | if err == nil { 51 | panic("err should not be nil") 52 | } 53 | assertNullInt16(t, badType, "wrong type json") 54 | 55 | var invalid Int16 56 | err = invalid.UnmarshalJSON(invalidJSON) 57 | if _, ok := err.(*json.SyntaxError); !ok { 58 | t.Errorf("expected json.SyntaxError, not %T", err) 59 | } 60 | assertNullInt16(t, invalid, "invalid json") 61 | } 62 | 63 | func TestUnmarshalNonIntegerNumber16(t *testing.T) { 64 | var i Int16 65 | err := json.Unmarshal(float64JSON, &i) 66 | if err == nil { 67 | panic("err should be present; non-integer number coerced to int16") 68 | } 69 | } 70 | 71 | func TestUnmarshalInt16Overflow(t *testing.T) { 72 | int16Overflow := uint16(math.MaxInt16) 73 | 74 | // Max int16 should decode successfully 75 | var i Int16 76 | err := json.Unmarshal([]byte(strconv.FormatUint(uint64(int16Overflow), 10)), &i) 77 | maybePanic(err) 78 | // Attempt to overflow 79 | int16Overflow++ 80 | err = json.Unmarshal([]byte(strconv.FormatUint(uint64(int16Overflow), 10)), &i) 81 | if err == nil { 82 | panic("err should be present; decoded value overflows int16") 83 | } 84 | } 85 | 86 | func TestTextUnmarshalInt16(t *testing.T) { 87 | var i Int16 88 | err := i.UnmarshalText([]byte("32766")) 89 | maybePanic(err) 90 | assertInt16(t, i, "UnmarshalText() int16") 91 | 92 | var blank Int16 93 | err = blank.UnmarshalText([]byte("")) 94 | maybePanic(err) 95 | assertNullInt16(t, blank, "UnmarshalText() empty int16") 96 | } 97 | 98 | func TestMarshalInt16(t *testing.T) { 99 | i := Int16From(32766) 100 | data, err := json.Marshal(i) 101 | maybePanic(err) 102 | assertJSONEquals(t, data, "32766", "non-empty json marshal") 103 | 104 | // invalid values should be encoded as null 105 | null := NewInt16(0, false) 106 | data, err = json.Marshal(null) 107 | maybePanic(err) 108 | assertJSONEquals(t, data, "null", "null json marshal") 109 | } 110 | 111 | func TestMarshalInt16Text(t *testing.T) { 112 | i := Int16From(32766) 113 | data, err := i.MarshalText() 114 | maybePanic(err) 115 | assertJSONEquals(t, data, "32766", "non-empty text marshal") 116 | 117 | // invalid values should be encoded as null 118 | null := NewInt16(0, false) 119 | data, err = null.MarshalText() 120 | maybePanic(err) 121 | assertJSONEquals(t, data, "", "null text marshal") 122 | } 123 | 124 | func TestInt16Pointer(t *testing.T) { 125 | i := Int16From(32766) 126 | ptr := i.Ptr() 127 | if *ptr != 32766 { 128 | t.Errorf("bad %s int16: %#v ≠ %d\n", "pointer", ptr, 32766) 129 | } 130 | 131 | null := NewInt16(0, false) 132 | ptr = null.Ptr() 133 | if ptr != nil { 134 | t.Errorf("bad %s int16: %#v ≠ %s\n", "nil pointer", ptr, "nil") 135 | } 136 | } 137 | 138 | func TestInt16IsZero(t *testing.T) { 139 | i := Int16From(32766) 140 | if i.IsZero() { 141 | t.Errorf("IsZero() should be false") 142 | } 143 | 144 | null := NewInt16(0, false) 145 | if !null.IsZero() { 146 | t.Errorf("IsZero() should be true") 147 | } 148 | 149 | zero := NewInt16(0, true) 150 | if zero.IsZero() { 151 | t.Errorf("IsZero() should be false") 152 | } 153 | } 154 | 155 | func TestInt16SetValid(t *testing.T) { 156 | change := NewInt16(0, false) 157 | assertNullInt16(t, change, "SetValid()") 158 | change.SetValid(32766) 159 | assertInt16(t, change, "SetValid()") 160 | } 161 | 162 | func TestInt16Scan(t *testing.T) { 163 | var i Int16 164 | err := i.Scan(32766) 165 | maybePanic(err) 166 | assertInt16(t, i, "scanned int16") 167 | 168 | var null Int16 169 | err = null.Scan(nil) 170 | maybePanic(err) 171 | assertNullInt16(t, null, "scanned null") 172 | } 173 | 174 | func assertInt16(t *testing.T, i Int16, from string) { 175 | if i.Int16 != 32766 { 176 | t.Errorf("bad %s int16: %d ≠ %d\n", from, i.Int16, 32766) 177 | } 178 | if !i.Valid { 179 | t.Error(from, "is invalid, but should be valid") 180 | } 181 | } 182 | 183 | func assertNullInt16(t *testing.T, i Int16, from string) { 184 | if i.Valid { 185 | t.Error(from, "is valid, but should be invalid") 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /int32_test.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "encoding/json" 5 | "math" 6 | "strconv" 7 | "testing" 8 | ) 9 | 10 | var ( 11 | int32JSON = []byte(`2147483646`) 12 | ) 13 | 14 | func TestInt32From(t *testing.T) { 15 | i := Int32From(2147483646) 16 | assertInt32(t, i, "Int32From()") 17 | 18 | zero := Int32From(0) 19 | if !zero.Valid { 20 | t.Error("Int32From(0)", "is invalid, but should be valid") 21 | } 22 | } 23 | 24 | func TestInt32FromPtr(t *testing.T) { 25 | n := int32(2147483646) 26 | iptr := &n 27 | i := Int32FromPtr(iptr) 28 | assertInt32(t, i, "Int32FromPtr()") 29 | 30 | null := Int32FromPtr(nil) 31 | assertNullInt32(t, null, "Int32FromPtr(nil)") 32 | } 33 | 34 | func TestUnmarshalInt32(t *testing.T) { 35 | var i Int32 36 | err := json.Unmarshal(int32JSON, &i) 37 | maybePanic(err) 38 | assertInt32(t, i, "int32 json") 39 | 40 | var null Int32 41 | err = json.Unmarshal(nullJSON, &null) 42 | maybePanic(err) 43 | assertNullInt32(t, null, "null json") 44 | if !null.Set { 45 | t.Error("should be Set") 46 | } 47 | 48 | var badType Int32 49 | err = json.Unmarshal(boolJSON, &badType) 50 | if err == nil { 51 | panic("err should not be nil") 52 | } 53 | assertNullInt32(t, badType, "wrong type json") 54 | 55 | var invalid Int32 56 | err = invalid.UnmarshalJSON(invalidJSON) 57 | if _, ok := err.(*json.SyntaxError); !ok { 58 | t.Errorf("expected json.SyntaxError, not %T", err) 59 | } 60 | assertNullInt32(t, invalid, "invalid json") 61 | } 62 | 63 | func TestUnmarshalNonIntegerNumber32(t *testing.T) { 64 | var i Int32 65 | err := json.Unmarshal(float64JSON, &i) 66 | if err == nil { 67 | panic("err should be present; non-integer number coerced to int32") 68 | } 69 | } 70 | 71 | func TestUnmarshalInt32Overflow(t *testing.T) { 72 | int32Overflow := uint32(math.MaxInt32) 73 | 74 | // Max int32 should decode successfully 75 | var i Int32 76 | err := json.Unmarshal([]byte(strconv.FormatUint(uint64(int32Overflow), 10)), &i) 77 | maybePanic(err) 78 | 79 | // Attempt to overflow 80 | int32Overflow++ 81 | err = json.Unmarshal([]byte(strconv.FormatUint(uint64(int32Overflow), 10)), &i) 82 | if err == nil { 83 | panic("err should be present; decoded value overflows int32") 84 | } 85 | } 86 | 87 | func TestTextUnmarshalInt32(t *testing.T) { 88 | var i Int32 89 | err := i.UnmarshalText([]byte("2147483646")) 90 | maybePanic(err) 91 | assertInt32(t, i, "UnmarshalText() int32") 92 | 93 | var blank Int32 94 | err = blank.UnmarshalText([]byte("")) 95 | maybePanic(err) 96 | assertNullInt32(t, blank, "UnmarshalText() empty int32") 97 | } 98 | 99 | func TestMarshalInt32(t *testing.T) { 100 | i := Int32From(2147483646) 101 | data, err := json.Marshal(i) 102 | maybePanic(err) 103 | assertJSONEquals(t, data, "2147483646", "non-empty json marshal") 104 | 105 | // invalid values should be encoded as null 106 | null := NewInt32(0, false) 107 | data, err = json.Marshal(null) 108 | maybePanic(err) 109 | assertJSONEquals(t, data, "null", "null json marshal") 110 | } 111 | 112 | func TestMarshalInt32Text(t *testing.T) { 113 | i := Int32From(2147483646) 114 | data, err := i.MarshalText() 115 | maybePanic(err) 116 | assertJSONEquals(t, data, "2147483646", "non-empty text marshal") 117 | 118 | // invalid values should be encoded as null 119 | null := NewInt32(0, false) 120 | data, err = null.MarshalText() 121 | maybePanic(err) 122 | assertJSONEquals(t, data, "", "null text marshal") 123 | } 124 | 125 | func TestInt32Pointer(t *testing.T) { 126 | i := Int32From(2147483646) 127 | ptr := i.Ptr() 128 | if *ptr != 2147483646 { 129 | t.Errorf("bad %s int32: %#v ≠ %d\n", "pointer", ptr, 2147483646) 130 | } 131 | 132 | null := NewInt32(0, false) 133 | ptr = null.Ptr() 134 | if ptr != nil { 135 | t.Errorf("bad %s int32: %#v ≠ %s\n", "nil pointer", ptr, "nil") 136 | } 137 | } 138 | 139 | func TestInt32IsZero(t *testing.T) { 140 | i := Int32From(2147483646) 141 | if i.IsZero() { 142 | t.Errorf("IsZero() should be false") 143 | } 144 | 145 | null := NewInt32(0, false) 146 | if !null.IsZero() { 147 | t.Errorf("IsZero() should be true") 148 | } 149 | 150 | zero := NewInt32(0, true) 151 | if zero.IsZero() { 152 | t.Errorf("IsZero() should be false") 153 | } 154 | } 155 | 156 | func TestInt32SetValid(t *testing.T) { 157 | change := NewInt32(0, false) 158 | assertNullInt32(t, change, "SetValid()") 159 | change.SetValid(2147483646) 160 | assertInt32(t, change, "SetValid()") 161 | } 162 | 163 | func TestInt32Scan(t *testing.T) { 164 | var i Int32 165 | err := i.Scan(2147483646) 166 | maybePanic(err) 167 | assertInt32(t, i, "scanned int32") 168 | 169 | var null Int32 170 | err = null.Scan(nil) 171 | maybePanic(err) 172 | assertNullInt32(t, null, "scanned null") 173 | } 174 | 175 | func assertInt32(t *testing.T, i Int32, from string) { 176 | if i.Int32 != 2147483646 { 177 | t.Errorf("bad %s int32: %d ≠ %d\n", from, i.Int32, 2147483646) 178 | } 179 | if !i.Valid { 180 | t.Error(from, "is invalid, but should be valid") 181 | } 182 | } 183 | 184 | func assertNullInt32(t *testing.T, i Int32, from string) { 185 | if i.Valid { 186 | t.Error(from, "is valid, but should be invalid") 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /uint16_test.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "encoding/json" 5 | "math" 6 | "strconv" 7 | "testing" 8 | ) 9 | 10 | var ( 11 | uint16JSON = []byte(`65534`) 12 | ) 13 | 14 | func TestUint16From(t *testing.T) { 15 | i := Uint16From(65534) 16 | assertUint16(t, i, "Uint16From()") 17 | 18 | zero := Uint16From(0) 19 | if !zero.Valid { 20 | t.Error("Uint16From(0)", "is invalid, but should be valid") 21 | } 22 | } 23 | 24 | func TestUint16FromPtr(t *testing.T) { 25 | n := uint16(65534) 26 | iptr := &n 27 | i := Uint16FromPtr(iptr) 28 | assertUint16(t, i, "Uint16FromPtr()") 29 | 30 | null := Uint16FromPtr(nil) 31 | assertNullUint16(t, null, "Uint16FromPtr(nil)") 32 | } 33 | 34 | func TestUnmarshalUint16(t *testing.T) { 35 | var i Uint16 36 | err := json.Unmarshal(uint16JSON, &i) 37 | maybePanic(err) 38 | assertUint16(t, i, "uint16 json") 39 | 40 | var null Uint16 41 | err = json.Unmarshal(nullJSON, &null) 42 | maybePanic(err) 43 | assertNullUint16(t, null, "null json") 44 | if !null.Set { 45 | t.Error("should be Set") 46 | } 47 | 48 | var badType Uint16 49 | err = json.Unmarshal(boolJSON, &badType) 50 | if err == nil { 51 | panic("err should not be nil") 52 | } 53 | assertNullUint16(t, badType, "wrong type json") 54 | 55 | var invalid Uint16 56 | err = invalid.UnmarshalJSON(invalidJSON) 57 | if _, ok := err.(*json.SyntaxError); !ok { 58 | t.Errorf("expected json.SyntaxError, not %T", err) 59 | } 60 | assertNullUint16(t, invalid, "invalid json") 61 | } 62 | 63 | func TestUnmarshalNonUintegerNumber16(t *testing.T) { 64 | var i Uint16 65 | err := json.Unmarshal(float64JSON, &i) 66 | if err == nil { 67 | panic("err should be present; non-integer number coerced to uint16") 68 | } 69 | } 70 | 71 | func TestUnmarshalUint16Overflow(t *testing.T) { 72 | uint16Overflow := int64(math.MaxUint16) 73 | 74 | // Max uint16 should decode successfully 75 | var i Uint16 76 | err := json.Unmarshal([]byte(strconv.FormatUint(uint64(uint16Overflow), 10)), &i) 77 | maybePanic(err) 78 | 79 | // Attempt to overflow 80 | uint16Overflow++ 81 | err = json.Unmarshal([]byte(strconv.FormatUint(uint64(uint16Overflow), 10)), &i) 82 | if err == nil { 83 | panic("err should be present; decoded value overflows uint16") 84 | } 85 | } 86 | 87 | func TestTextUnmarshalUint16(t *testing.T) { 88 | var i Uint16 89 | err := i.UnmarshalText([]byte("65534")) 90 | maybePanic(err) 91 | assertUint16(t, i, "UnmarshalText() uint16") 92 | 93 | var blank Uint16 94 | err = blank.UnmarshalText([]byte("")) 95 | maybePanic(err) 96 | assertNullUint16(t, blank, "UnmarshalText() empty uint16") 97 | } 98 | 99 | func TestMarshalUint16(t *testing.T) { 100 | i := Uint16From(65534) 101 | data, err := json.Marshal(i) 102 | maybePanic(err) 103 | assertJSONEquals(t, data, "65534", "non-empty json marshal") 104 | 105 | // invalid values should be encoded as null 106 | null := NewUint16(0, false) 107 | data, err = json.Marshal(null) 108 | maybePanic(err) 109 | assertJSONEquals(t, data, "null", "null json marshal") 110 | } 111 | 112 | func TestMarshalUint16Text(t *testing.T) { 113 | i := Uint16From(65534) 114 | data, err := i.MarshalText() 115 | maybePanic(err) 116 | assertJSONEquals(t, data, "65534", "non-empty text marshal") 117 | 118 | // invalid values should be encoded as null 119 | null := NewUint16(0, false) 120 | data, err = null.MarshalText() 121 | maybePanic(err) 122 | assertJSONEquals(t, data, "", "null text marshal") 123 | } 124 | 125 | func TestUint16Pointer(t *testing.T) { 126 | i := Uint16From(65534) 127 | ptr := i.Ptr() 128 | if *ptr != 65534 { 129 | t.Errorf("bad %s uint16: %#v ≠ %d\n", "pointer", ptr, 65534) 130 | } 131 | 132 | null := NewUint16(0, false) 133 | ptr = null.Ptr() 134 | if ptr != nil { 135 | t.Errorf("bad %s uint16: %#v ≠ %s\n", "nil pointer", ptr, "nil") 136 | } 137 | } 138 | 139 | func TestUint16IsZero(t *testing.T) { 140 | i := Uint16From(65534) 141 | if i.IsZero() { 142 | t.Errorf("IsZero() should be false") 143 | } 144 | 145 | null := NewUint16(0, false) 146 | if !null.IsZero() { 147 | t.Errorf("IsZero() should be true") 148 | } 149 | 150 | zero := NewUint16(0, true) 151 | if zero.IsZero() { 152 | t.Errorf("IsZero() should be false") 153 | } 154 | } 155 | 156 | func TestUint16SetValid(t *testing.T) { 157 | change := NewUint16(0, false) 158 | assertNullUint16(t, change, "SetValid()") 159 | change.SetValid(65534) 160 | assertUint16(t, change, "SetValid()") 161 | } 162 | 163 | func TestUint16Scan(t *testing.T) { 164 | var i Uint16 165 | err := i.Scan(65534) 166 | maybePanic(err) 167 | assertUint16(t, i, "scanned uint16") 168 | 169 | var null Uint16 170 | err = null.Scan(nil) 171 | maybePanic(err) 172 | assertNullUint16(t, null, "scanned null") 173 | } 174 | 175 | func assertUint16(t *testing.T, i Uint16, from string) { 176 | if i.Uint16 != 65534 { 177 | t.Errorf("bad %s uint16: %d ≠ %d\n", from, i.Uint16, 65534) 178 | } 179 | if !i.Valid { 180 | t.Error(from, "is invalid, but should be valid") 181 | } 182 | } 183 | 184 | func assertNullUint16(t *testing.T, i Uint16, from string) { 185 | if i.Valid { 186 | t.Error(from, "is valid, but should be invalid") 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /uint32_test.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "encoding/json" 5 | "math" 6 | "strconv" 7 | "testing" 8 | ) 9 | 10 | var ( 11 | uint32JSON = []byte(`4294967294`) 12 | ) 13 | 14 | func TestUint32From(t *testing.T) { 15 | i := Uint32From(4294967294) 16 | assertUint32(t, i, "Uint32From()") 17 | 18 | zero := Uint32From(0) 19 | if !zero.Valid { 20 | t.Error("Uint32From(0)", "is invalid, but should be valid") 21 | } 22 | } 23 | 24 | func TestUint32FromPtr(t *testing.T) { 25 | n := uint32(4294967294) 26 | iptr := &n 27 | i := Uint32FromPtr(iptr) 28 | assertUint32(t, i, "Uint32FromPtr()") 29 | 30 | null := Uint32FromPtr(nil) 31 | assertNullUint32(t, null, "Uint32FromPtr(nil)") 32 | } 33 | 34 | func TestUnmarshalUint32(t *testing.T) { 35 | var i Uint32 36 | err := json.Unmarshal(uint32JSON, &i) 37 | maybePanic(err) 38 | assertUint32(t, i, "uint32 json") 39 | 40 | var null Uint32 41 | err = json.Unmarshal(nullJSON, &null) 42 | maybePanic(err) 43 | assertNullUint32(t, null, "null json") 44 | if !null.Set { 45 | t.Error("should be Set") 46 | } 47 | 48 | var badType Uint32 49 | err = json.Unmarshal(boolJSON, &badType) 50 | if err == nil { 51 | panic("err should not be nil") 52 | } 53 | assertNullUint32(t, badType, "wrong type json") 54 | 55 | var invalid Uint32 56 | err = invalid.UnmarshalJSON(invalidJSON) 57 | if _, ok := err.(*json.SyntaxError); !ok { 58 | t.Errorf("expected json.SyntaxError, not %T", err) 59 | } 60 | assertNullUint32(t, invalid, "invalid json") 61 | } 62 | 63 | func TestUnmarshalNonUintegerNumber32(t *testing.T) { 64 | var i Uint32 65 | err := json.Unmarshal(float64JSON, &i) 66 | if err == nil { 67 | panic("err should be present; non-integer number coerced to uint32") 68 | } 69 | } 70 | 71 | func TestUnmarshalUint32Overflow(t *testing.T) { 72 | uint32Overflow := int64(math.MaxUint32) 73 | 74 | // Max uint32 should decode successfully 75 | var i Uint32 76 | err := json.Unmarshal([]byte(strconv.FormatUint(uint64(uint32Overflow), 10)), &i) 77 | maybePanic(err) 78 | 79 | // Attempt to overflow 80 | uint32Overflow++ 81 | err = json.Unmarshal([]byte(strconv.FormatUint(uint64(uint32Overflow), 10)), &i) 82 | if err == nil { 83 | panic("err should be present; decoded value overflows uint32") 84 | } 85 | } 86 | 87 | func TestTextUnmarshalUint32(t *testing.T) { 88 | var i Uint32 89 | err := i.UnmarshalText([]byte("4294967294")) 90 | maybePanic(err) 91 | assertUint32(t, i, "UnmarshalText() uint32") 92 | 93 | var blank Uint32 94 | err = blank.UnmarshalText([]byte("")) 95 | maybePanic(err) 96 | assertNullUint32(t, blank, "UnmarshalText() empty uint32") 97 | } 98 | 99 | func TestMarshalUint32(t *testing.T) { 100 | i := Uint32From(4294967294) 101 | data, err := json.Marshal(i) 102 | maybePanic(err) 103 | assertJSONEquals(t, data, "4294967294", "non-empty json marshal") 104 | 105 | // invalid values should be encoded as null 106 | null := NewUint32(0, false) 107 | data, err = json.Marshal(null) 108 | maybePanic(err) 109 | assertJSONEquals(t, data, "null", "null json marshal") 110 | } 111 | 112 | func TestMarshalUint32Text(t *testing.T) { 113 | i := Uint32From(4294967294) 114 | data, err := i.MarshalText() 115 | maybePanic(err) 116 | assertJSONEquals(t, data, "4294967294", "non-empty text marshal") 117 | 118 | // invalid values should be encoded as null 119 | null := NewUint32(0, false) 120 | data, err = null.MarshalText() 121 | maybePanic(err) 122 | assertJSONEquals(t, data, "", "null text marshal") 123 | } 124 | 125 | func TestUint32Pointer(t *testing.T) { 126 | i := Uint32From(4294967294) 127 | ptr := i.Ptr() 128 | if *ptr != 4294967294 { 129 | t.Errorf("bad %s uint32: %#v ≠ %d\n", "pointer", ptr, 4294967294) 130 | } 131 | 132 | null := NewUint32(0, false) 133 | ptr = null.Ptr() 134 | if ptr != nil { 135 | t.Errorf("bad %s uint32: %#v ≠ %s\n", "nil pointer", ptr, "nil") 136 | } 137 | } 138 | 139 | func TestUint32IsZero(t *testing.T) { 140 | i := Uint32From(4294967294) 141 | if i.IsZero() { 142 | t.Errorf("IsZero() should be false") 143 | } 144 | 145 | null := NewUint32(0, false) 146 | if !null.IsZero() { 147 | t.Errorf("IsZero() should be true") 148 | } 149 | 150 | zero := NewUint32(0, true) 151 | if zero.IsZero() { 152 | t.Errorf("IsZero() should be false") 153 | } 154 | } 155 | 156 | func TestUint32SetValid(t *testing.T) { 157 | change := NewUint32(0, false) 158 | assertNullUint32(t, change, "SetValid()") 159 | change.SetValid(4294967294) 160 | assertUint32(t, change, "SetValid()") 161 | } 162 | 163 | func TestUint32Scan(t *testing.T) { 164 | var i Uint32 165 | err := i.Scan(4294967294) 166 | maybePanic(err) 167 | assertUint32(t, i, "scanned uint32") 168 | 169 | var null Uint32 170 | err = null.Scan(nil) 171 | maybePanic(err) 172 | assertNullUint32(t, null, "scanned null") 173 | } 174 | 175 | func assertUint32(t *testing.T, i Uint32, from string) { 176 | if i.Uint32 != 4294967294 { 177 | t.Errorf("bad %s uint32: %d ≠ %d\n", from, i.Uint32, 4294967294) 178 | } 179 | if !i.Valid { 180 | t.Error(from, "is invalid, but should be valid") 181 | } 182 | } 183 | 184 | func assertNullUint32(t *testing.T, i Uint32, from string) { 185 | if i.Valid { 186 | t.Error(from, "is valid, but should be invalid") 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /json.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "bytes" 5 | "database/sql/driver" 6 | "encoding/json" 7 | "errors" 8 | "fmt" 9 | 10 | "github.com/aarondl/null/v9/convert" 11 | ) 12 | 13 | // JSON is a nullable []byte that contains JSON. 14 | // 15 | // You might want to use this in the case where you have say a nullable 16 | // JSON column in postgres for instance, where there is one layer of null for 17 | // the postgres column, and then you also have the opportunity to have null 18 | // as a value contained in the json. When unmarshalling json however you 19 | // cannot set 'null' as a value. 20 | type JSON struct { 21 | JSON []byte 22 | Valid bool 23 | Set bool 24 | } 25 | 26 | // NewJSON creates a new JSON 27 | func NewJSON(b []byte, valid bool) JSON { 28 | return JSON{ 29 | JSON: b, 30 | Valid: valid, 31 | Set: true, 32 | } 33 | } 34 | 35 | // JSONFrom creates a new JSON that will be invalid if nil. 36 | func JSONFrom(b []byte) JSON { 37 | return NewJSON(b, b != nil) 38 | } 39 | 40 | // JSONFromPtr creates a new JSON that will be invalid if nil. 41 | func JSONFromPtr(b *[]byte) JSON { 42 | if b == nil { 43 | return NewJSON(nil, false) 44 | } 45 | n := NewJSON(*b, true) 46 | return n 47 | } 48 | 49 | // IsValid returns true if this carries and explicit value and 50 | // is not null. 51 | func (j JSON) IsValid() bool { 52 | return j.Set && j.Valid 53 | } 54 | 55 | // IsSet returns true if this carries an explicit value (null inclusive) 56 | func (j JSON) IsSet() bool { 57 | return j.Set 58 | } 59 | 60 | // Unmarshal will unmarshal your JSON stored in 61 | // your JSON object and store the result in the 62 | // value pointed to by dest. 63 | func (j JSON) Unmarshal(dest interface{}) error { 64 | if dest == nil { 65 | return errors.New("destination is nil, not a valid pointer to an object") 66 | } 67 | 68 | // Call our implementation of 69 | // JSON MarshalJSON through json.Marshal 70 | // to get the value of the JSON object 71 | res, err := json.Marshal(j) 72 | if err != nil { 73 | return err 74 | } 75 | 76 | return json.Unmarshal(res, dest) 77 | } 78 | 79 | // UnmarshalJSON implements json.Unmarshaler. 80 | // 81 | // Example if you have a struct with a null.JSON called v: 82 | // 83 | // {} -> does not call unmarshaljson: !set & !valid 84 | // {"v": null} -> calls unmarshaljson, set & !valid 85 | // {"v": {}} -> calls unmarshaljson, set & valid (json value is '{}') 86 | // 87 | // That's to say if 'null' is passed in at the json level we do not capture that 88 | // value - instead we set the value-level null flag so that an sql value will 89 | // turn out null. 90 | func (j *JSON) UnmarshalJSON(data []byte) error { 91 | if data == nil { 92 | return fmt.Errorf("null: cannot unmarshal nil into Go value of type null.JSON") 93 | } 94 | 95 | j.Set = true 96 | 97 | if bytes.Equal(data, NullBytes) { 98 | j.JSON = nil 99 | j.Valid = false 100 | return nil 101 | } 102 | 103 | j.Valid = true 104 | j.JSON = make([]byte, len(data)) 105 | copy(j.JSON, data) 106 | 107 | return nil 108 | } 109 | 110 | // UnmarshalText implements encoding.TextUnmarshaler. 111 | func (j *JSON) UnmarshalText(text []byte) error { 112 | j.Set = true 113 | if len(text) == 0 { 114 | j.JSON = nil 115 | j.Valid = false 116 | } else { 117 | j.JSON = append(j.JSON[0:0], text...) 118 | j.Valid = true 119 | } 120 | 121 | return nil 122 | } 123 | 124 | // Marshal will marshal the passed in object, 125 | // and store it in the JSON member on the JSON object. 126 | func (j *JSON) Marshal(obj interface{}) error { 127 | res, err := json.Marshal(obj) 128 | if err != nil { 129 | return err 130 | } 131 | 132 | // Call our implementation of 133 | // JSON UnmarshalJSON through json.Unmarshal 134 | // to Set the result to the JSON object 135 | return json.Unmarshal(res, j) 136 | } 137 | 138 | // MarshalJSON implements json.Marshaler. 139 | func (j JSON) MarshalJSON() ([]byte, error) { 140 | if len(j.JSON) == 0 || j.JSON == nil { 141 | return NullBytes, nil 142 | } 143 | return j.JSON, nil 144 | } 145 | 146 | // MarshalText implements encoding.TextMarshaler. 147 | func (j JSON) MarshalText() ([]byte, error) { 148 | if !j.Valid { 149 | return nil, nil 150 | } 151 | return j.JSON, nil 152 | } 153 | 154 | // SetValid changes this JSON's value and also sets it to be non-null. 155 | func (j *JSON) SetValid(n []byte) { 156 | j.JSON = n 157 | j.Valid = true 158 | j.Set = true 159 | } 160 | 161 | // Ptr returns a pointer to this JSON's value, or a nil pointer if this JSON is null. 162 | func (j JSON) Ptr() *[]byte { 163 | if !j.Valid { 164 | return nil 165 | } 166 | return &j.JSON 167 | } 168 | 169 | // IsZero returns true for null or zero JSON's, for future omitempty support (Go 1.4?) 170 | func (j JSON) IsZero() bool { 171 | return !j.Valid 172 | } 173 | 174 | // Scan implements the Scanner interface. 175 | func (j *JSON) Scan(value interface{}) error { 176 | if value == nil { 177 | j.JSON, j.Valid, j.Set = nil, false, false 178 | return nil 179 | } 180 | j.Valid, j.Set = true, true 181 | return convert.ConvertAssign(&j.JSON, value) 182 | } 183 | 184 | // Value implements the driver Valuer interface. 185 | func (j JSON) Value() (driver.Value, error) { 186 | if !j.Valid { 187 | return nil, nil 188 | } 189 | return j.JSON, nil 190 | } 191 | -------------------------------------------------------------------------------- /int64_test.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "encoding/json" 5 | "math" 6 | "strconv" 7 | "testing" 8 | ) 9 | 10 | var ( 11 | int64JSON = []byte(`9223372036854775806`) 12 | ) 13 | 14 | func TestInt64From(t *testing.T) { 15 | i := Int64From(9223372036854775806) 16 | assertInt64(t, i, "Int64From()") 17 | 18 | zero := Int64From(0) 19 | if !zero.Valid { 20 | t.Error("Int64From(0)", "is invalid, but should be valid") 21 | } 22 | } 23 | 24 | func TestInt64FromPtr(t *testing.T) { 25 | n := int64(9223372036854775806) 26 | iptr := &n 27 | i := Int64FromPtr(iptr) 28 | assertInt64(t, i, "Int64FromPtr()") 29 | 30 | null := Int64FromPtr(nil) 31 | assertNullInt64(t, null, "Int64FromPtr(nil)") 32 | } 33 | 34 | func TestUnmarshalInt64(t *testing.T) { 35 | var i Int64 36 | err := json.Unmarshal(int64JSON, &i) 37 | maybePanic(err) 38 | assertInt64(t, i, "int64 json") 39 | 40 | var null Int64 41 | err = json.Unmarshal(nullJSON, &null) 42 | maybePanic(err) 43 | assertNullInt64(t, null, "null json") 44 | if !null.Set { 45 | t.Error("should be Set") 46 | } 47 | 48 | var badType Int64 49 | err = json.Unmarshal(boolJSON, &badType) 50 | if err == nil { 51 | panic("err should not be nil") 52 | } 53 | assertNullInt64(t, badType, "wrong type json") 54 | 55 | var invalid Int64 56 | err = invalid.UnmarshalJSON(invalidJSON) 57 | if _, ok := err.(*json.SyntaxError); !ok { 58 | t.Errorf("expected json.SyntaxError, not %T", err) 59 | } 60 | assertNullInt64(t, invalid, "invalid json") 61 | } 62 | 63 | func TestUnmarshalNonIntegerNumber64(t *testing.T) { 64 | var i Int64 65 | err := json.Unmarshal(float64JSON, &i) 66 | if err == nil { 67 | panic("err should be present; non-integer number coerced to int64") 68 | } 69 | } 70 | 71 | func TestUnmarshalInt64Overflow(t *testing.T) { 72 | int64Overflow := uint64(math.MaxInt64) 73 | 74 | // Max int64 should decode successfully 75 | var i Int64 76 | err := json.Unmarshal([]byte(strconv.FormatUint(uint64(int64Overflow), 10)), &i) 77 | maybePanic(err) 78 | 79 | // Attempt to overflow 80 | int64Overflow++ 81 | err = json.Unmarshal([]byte(strconv.FormatUint(uint64(int64Overflow), 10)), &i) 82 | if err == nil { 83 | panic("err should be present; decoded value overflows int64") 84 | } 85 | } 86 | 87 | func TestTextUnmarshalInt64(t *testing.T) { 88 | var i Int64 89 | err := i.UnmarshalText([]byte("9223372036854775806")) 90 | maybePanic(err) 91 | assertInt64(t, i, "UnmarshalText() int64") 92 | 93 | var blank Int64 94 | err = blank.UnmarshalText([]byte("")) 95 | maybePanic(err) 96 | assertNullInt64(t, blank, "UnmarshalText() empty int64") 97 | } 98 | 99 | func TestMarshalInt64(t *testing.T) { 100 | i := Int64From(9223372036854775806) 101 | data, err := json.Marshal(i) 102 | maybePanic(err) 103 | assertJSONEquals(t, data, "9223372036854775806", "non-empty json marshal") 104 | 105 | // invalid values should be encoded as null 106 | null := NewInt64(0, false) 107 | data, err = json.Marshal(null) 108 | maybePanic(err) 109 | assertJSONEquals(t, data, "null", "null json marshal") 110 | } 111 | 112 | func TestMarshalInt64Text(t *testing.T) { 113 | i := Int64From(9223372036854775806) 114 | data, err := i.MarshalText() 115 | maybePanic(err) 116 | assertJSONEquals(t, data, "9223372036854775806", "non-empty text marshal") 117 | 118 | // invalid values should be encoded as null 119 | null := NewInt64(0, false) 120 | data, err = null.MarshalText() 121 | maybePanic(err) 122 | assertJSONEquals(t, data, "", "null text marshal") 123 | } 124 | 125 | func TestInt64Pointer(t *testing.T) { 126 | i := Int64From(9223372036854775806) 127 | ptr := i.Ptr() 128 | if *ptr != 9223372036854775806 { 129 | t.Errorf("bad %s int64: %#v ≠ %d\n", "pointer", ptr, 9223372036854775806) 130 | } 131 | 132 | null := NewInt64(0, false) 133 | ptr = null.Ptr() 134 | if ptr != nil { 135 | t.Errorf("bad %s int64: %#v ≠ %s\n", "nil pointer", ptr, "nil") 136 | } 137 | } 138 | 139 | func TestInt64IsZero(t *testing.T) { 140 | i := Int64From(9223372036854775806) 141 | if i.IsZero() { 142 | t.Errorf("IsZero() should be false") 143 | } 144 | 145 | null := NewInt64(0, false) 146 | if !null.IsZero() { 147 | t.Errorf("IsZero() should be true") 148 | } 149 | 150 | zero := NewInt64(0, true) 151 | if zero.IsZero() { 152 | t.Errorf("IsZero() should be false") 153 | } 154 | } 155 | 156 | func TestInt64SetValid(t *testing.T) { 157 | change := NewInt64(0, false) 158 | assertNullInt64(t, change, "SetValid()") 159 | change.SetValid(9223372036854775806) 160 | assertInt64(t, change, "SetValid()") 161 | } 162 | 163 | func TestInt64Scan(t *testing.T) { 164 | var i Int64 165 | err := i.Scan(9223372036854775806) 166 | maybePanic(err) 167 | assertInt64(t, i, "scanned int64") 168 | 169 | var null Int64 170 | err = null.Scan(nil) 171 | maybePanic(err) 172 | assertNullInt64(t, null, "scanned null") 173 | } 174 | 175 | func assertInt64(t *testing.T, i Int64, from string) { 176 | if i.Int64 != 9223372036854775806 { 177 | t.Errorf("bad %s int64: %d ≠ %d\n", from, i.Int64, 9223372036854775806) 178 | } 179 | if !i.Valid { 180 | t.Error(from, "is invalid, but should be valid") 181 | } 182 | } 183 | 184 | func assertNullInt64(t *testing.T, i Int64, from string) { 185 | if i.Valid { 186 | t.Error(from, "is valid, but should be invalid") 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /string_test.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | ) 7 | 8 | var ( 9 | stringJSON = []byte(`"test"`) 10 | blankStringJSON = []byte(`""`) 11 | 12 | nullJSON = []byte(`null`) 13 | invalidJSON = []byte(`:)`) 14 | ) 15 | 16 | func TestStringFrom(t *testing.T) { 17 | str := StringFrom("test") 18 | assertStr(t, str, "StringFrom() string") 19 | 20 | zero := StringFrom("") 21 | if !zero.Valid { 22 | t.Error("StringFrom(0)", "is invalid, but should be valid") 23 | } 24 | } 25 | 26 | func TestStringFromPtr(t *testing.T) { 27 | s := "test" 28 | sptr := &s 29 | str := StringFromPtr(sptr) 30 | assertStr(t, str, "StringFromPtr() string") 31 | 32 | null := StringFromPtr(nil) 33 | assertNullStr(t, null, "StringFromPtr(nil)") 34 | } 35 | 36 | func TestUnmarshalString(t *testing.T) { 37 | var str String 38 | err := json.Unmarshal(stringJSON, &str) 39 | maybePanic(err) 40 | assertStr(t, str, "string json") 41 | 42 | var blank String 43 | err = json.Unmarshal(blankStringJSON, &blank) 44 | maybePanic(err) 45 | if !blank.Valid { 46 | t.Error("blank string should be valid") 47 | } 48 | 49 | var null String 50 | err = json.Unmarshal(nullJSON, &null) 51 | maybePanic(err) 52 | assertNullStr(t, null, "null json") 53 | if !null.Set { 54 | t.Error("should be Set") 55 | } 56 | 57 | var badType String 58 | err = json.Unmarshal(boolJSON, &badType) 59 | if err == nil { 60 | panic("err should not be nil") 61 | } 62 | assertNullStr(t, badType, "wrong type json") 63 | 64 | var invalid String 65 | err = invalid.UnmarshalJSON(invalidJSON) 66 | if _, ok := err.(*json.SyntaxError); !ok { 67 | t.Errorf("expected json.SyntaxError, not %T", err) 68 | } 69 | assertNullStr(t, invalid, "invalid json") 70 | } 71 | 72 | func TestTextUnmarshalString(t *testing.T) { 73 | var str String 74 | err := str.UnmarshalText([]byte("test")) 75 | maybePanic(err) 76 | assertStr(t, str, "UnmarshalText() string") 77 | 78 | var null String 79 | err = null.UnmarshalText([]byte("")) 80 | maybePanic(err) 81 | assertNullStr(t, null, "UnmarshalText() empty string") 82 | } 83 | 84 | func TestMarshalString(t *testing.T) { 85 | str := StringFrom("test") 86 | data, err := json.Marshal(str) 87 | maybePanic(err) 88 | assertJSONEquals(t, data, `"test"`, "non-empty json marshal") 89 | data, err = str.MarshalText() 90 | maybePanic(err) 91 | assertJSONEquals(t, data, "test", "non-empty text marshal") 92 | 93 | // empty values should be encoded as an empty string 94 | zero := StringFrom("") 95 | data, err = json.Marshal(zero) 96 | maybePanic(err) 97 | assertJSONEquals(t, data, `""`, "empty json marshal") 98 | data, err = zero.MarshalText() 99 | maybePanic(err) 100 | assertJSONEquals(t, data, "", "string marshal text") 101 | 102 | null := StringFromPtr(nil) 103 | data, err = json.Marshal(null) 104 | maybePanic(err) 105 | assertJSONEquals(t, data, `null`, "null json marshal") 106 | data, err = null.MarshalText() 107 | maybePanic(err) 108 | assertJSONEquals(t, data, "", "string marshal text") 109 | } 110 | 111 | // Tests omitempty... broken until Go 1.4 112 | // type stringInStruct struct { 113 | // Test String `json:"test,omitempty"` 114 | // } 115 | // func TestMarshalStringInStruct(t *testing.T) { 116 | // obj := stringInStruct{Test: StringFrom("")} 117 | // data, err := json.Marshal(obj) 118 | // maybePanic(err) 119 | // assertJSONEquals(t, data, `{}`, "null string in struct") 120 | // } 121 | 122 | func TestStringPointer(t *testing.T) { 123 | str := StringFrom("test") 124 | ptr := str.Ptr() 125 | if *ptr != "test" { 126 | t.Errorf("bad %s string: %#v ≠ %s\n", "pointer", ptr, "test") 127 | } 128 | 129 | null := NewString("", false) 130 | ptr = null.Ptr() 131 | if ptr != nil { 132 | t.Errorf("bad %s string: %#v ≠ %s\n", "nil pointer", ptr, "nil") 133 | } 134 | } 135 | 136 | func TestStringIsZero(t *testing.T) { 137 | str := StringFrom("test") 138 | if str.IsZero() { 139 | t.Errorf("IsZero() should be false") 140 | } 141 | 142 | blank := StringFrom("") 143 | if blank.IsZero() { 144 | t.Errorf("IsZero() should be false") 145 | } 146 | 147 | empty := NewString("", true) 148 | if empty.IsZero() { 149 | t.Errorf("IsZero() should be false") 150 | } 151 | 152 | null := StringFromPtr(nil) 153 | if !null.IsZero() { 154 | t.Errorf("IsZero() should be true") 155 | } 156 | } 157 | 158 | func TestStringSetValid(t *testing.T) { 159 | change := NewString("", false) 160 | assertNullStr(t, change, "SetValid()") 161 | change.SetValid("test") 162 | assertStr(t, change, "SetValid()") 163 | } 164 | 165 | func TestStringScan(t *testing.T) { 166 | var str String 167 | err := str.Scan("test") 168 | maybePanic(err) 169 | assertStr(t, str, "scanned string") 170 | 171 | var null String 172 | err = null.Scan(nil) 173 | maybePanic(err) 174 | assertNullStr(t, null, "scanned null") 175 | } 176 | 177 | func maybePanic(err error) { 178 | if err != nil { 179 | panic(err) 180 | } 181 | } 182 | 183 | func assertStr(t *testing.T, s String, from string) { 184 | if s.String != "test" { 185 | t.Errorf("bad %s string: %s ≠ %s\n", from, s.String, "test") 186 | } 187 | if !s.Valid { 188 | t.Error(from, "is invalid, but should be valid") 189 | } 190 | } 191 | 192 | func assertNullStr(t *testing.T, s String, from string) { 193 | if s.Valid { 194 | t.Error(from, "is valid, but should be invalid") 195 | } 196 | } 197 | 198 | func assertJSONEquals(t *testing.T, data []byte, cmp string, from string) { 199 | if string(data) != cmp { 200 | t.Errorf("bad %s data: %s ≠ %s\n", from, data, cmp) 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /convert/convert.go: -------------------------------------------------------------------------------- 1 | package convert 2 | 3 | // Copyright 2011 The Go Authors. All rights reserved. 4 | // Use of this source code is governed by a BSD-style 5 | // license that can be found in the LICENSE file. 6 | 7 | // Type conversions for Scan. 8 | // These functions are copied from database/sql/convert.go build 1.6.2 9 | 10 | import ( 11 | "database/sql" 12 | "database/sql/driver" 13 | "errors" 14 | "fmt" 15 | "reflect" 16 | "strconv" 17 | "time" 18 | ) 19 | 20 | var errNilPtr = errors.New("destination pointer is nil") // embedded in descriptive error 21 | 22 | // ConvertAssign copies to dest the value in src, converting it if possible. 23 | // An error is returned if the copy would result in loss of information. 24 | // dest should be a pointer type. 25 | func ConvertAssign(dest, src interface{}) error { 26 | // Common cases, without reflect. 27 | switch s := src.(type) { 28 | case string: 29 | switch d := dest.(type) { 30 | case *string: 31 | if d == nil { 32 | return errNilPtr 33 | } 34 | *d = s 35 | return nil 36 | case *[]byte: 37 | if d == nil { 38 | return errNilPtr 39 | } 40 | *d = []byte(s) 41 | return nil 42 | } 43 | case []byte: 44 | switch d := dest.(type) { 45 | case *string: 46 | if d == nil { 47 | return errNilPtr 48 | } 49 | *d = string(s) 50 | return nil 51 | case *interface{}: 52 | if d == nil { 53 | return errNilPtr 54 | } 55 | *d = cloneBytes(s) 56 | return nil 57 | case *[]byte: 58 | if d == nil { 59 | return errNilPtr 60 | } 61 | *d = cloneBytes(s) 62 | return nil 63 | case *sql.RawBytes: 64 | if d == nil { 65 | return errNilPtr 66 | } 67 | *d = s 68 | return nil 69 | } 70 | case time.Time: 71 | switch d := dest.(type) { 72 | case *string: 73 | *d = s.Format(time.RFC3339Nano) 74 | return nil 75 | case *[]byte: 76 | if d == nil { 77 | return errNilPtr 78 | } 79 | *d = []byte(s.Format(time.RFC3339Nano)) 80 | return nil 81 | } 82 | case nil: 83 | switch d := dest.(type) { 84 | case *interface{}: 85 | if d == nil { 86 | return errNilPtr 87 | } 88 | *d = nil 89 | return nil 90 | case *[]byte: 91 | if d == nil { 92 | return errNilPtr 93 | } 94 | *d = nil 95 | return nil 96 | case *sql.RawBytes: 97 | if d == nil { 98 | return errNilPtr 99 | } 100 | *d = nil 101 | return nil 102 | } 103 | } 104 | 105 | var sv reflect.Value 106 | 107 | switch d := dest.(type) { 108 | case *string: 109 | sv = reflect.ValueOf(src) 110 | switch sv.Kind() { 111 | case reflect.Bool, 112 | reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, 113 | reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, 114 | reflect.Float32, reflect.Float64: 115 | *d = asString(src) 116 | return nil 117 | } 118 | case *[]byte: 119 | sv = reflect.ValueOf(src) 120 | if b, ok := asBytes(nil, sv); ok { 121 | *d = b 122 | return nil 123 | } 124 | case *sql.RawBytes: 125 | sv = reflect.ValueOf(src) 126 | if b, ok := asBytes([]byte(*d)[:0], sv); ok { 127 | *d = sql.RawBytes(b) 128 | return nil 129 | } 130 | case *bool: 131 | bv, err := driver.Bool.ConvertValue(src) 132 | if err == nil { 133 | *d = bv.(bool) 134 | } 135 | return err 136 | case *interface{}: 137 | *d = src 138 | return nil 139 | } 140 | 141 | if scanner, ok := dest.(sql.Scanner); ok { 142 | return scanner.Scan(src) 143 | } 144 | 145 | dpv := reflect.ValueOf(dest) 146 | if dpv.Kind() != reflect.Ptr { 147 | return errors.New("destination not a pointer") 148 | } 149 | if dpv.IsNil() { 150 | return errNilPtr 151 | } 152 | 153 | if !sv.IsValid() { 154 | sv = reflect.ValueOf(src) 155 | } 156 | 157 | dv := reflect.Indirect(dpv) 158 | if sv.IsValid() && sv.Type().AssignableTo(dv.Type()) { 159 | dv.Set(sv) 160 | return nil 161 | } 162 | 163 | if dv.Kind() == sv.Kind() && sv.Type().ConvertibleTo(dv.Type()) { 164 | dv.Set(sv.Convert(dv.Type())) 165 | return nil 166 | } 167 | 168 | switch dv.Kind() { 169 | case reflect.Ptr: 170 | if src == nil { 171 | dv.Set(reflect.Zero(dv.Type())) 172 | return nil 173 | } 174 | dv.Set(reflect.New(dv.Type().Elem())) 175 | return ConvertAssign(dv.Interface(), src) 176 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 177 | s := asString(src) 178 | i64, err := strconv.ParseInt(s, 10, dv.Type().Bits()) 179 | if err != nil { 180 | err = strconvErr(err) 181 | return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err) 182 | } 183 | dv.SetInt(i64) 184 | return nil 185 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 186 | s := asString(src) 187 | u64, err := strconv.ParseUint(s, 10, dv.Type().Bits()) 188 | if err != nil { 189 | err = strconvErr(err) 190 | return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err) 191 | } 192 | dv.SetUint(u64) 193 | return nil 194 | case reflect.Float32, reflect.Float64: 195 | s := asString(src) 196 | f64, err := strconv.ParseFloat(s, dv.Type().Bits()) 197 | if err != nil { 198 | err = strconvErr(err) 199 | return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err) 200 | } 201 | dv.SetFloat(f64) 202 | return nil 203 | } 204 | 205 | return fmt.Errorf("unsupported Scan, storing driver.Value type %T into type %T", src, dest) 206 | } 207 | 208 | func strconvErr(err error) error { 209 | if ne, ok := err.(*strconv.NumError); ok { 210 | return ne.Err 211 | } 212 | return err 213 | } 214 | 215 | func cloneBytes(b []byte) []byte { 216 | if b == nil { 217 | return nil 218 | } 219 | c := make([]byte, len(b)) 220 | copy(c, b) 221 | return c 222 | } 223 | 224 | func asString(src interface{}) string { 225 | switch v := src.(type) { 226 | case string: 227 | return v 228 | case []byte: 229 | return string(v) 230 | } 231 | rv := reflect.ValueOf(src) 232 | switch rv.Kind() { 233 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 234 | return strconv.FormatInt(rv.Int(), 10) 235 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 236 | return strconv.FormatUint(rv.Uint(), 10) 237 | case reflect.Float64: 238 | return strconv.FormatFloat(rv.Float(), 'g', -1, 64) 239 | case reflect.Float32: 240 | return strconv.FormatFloat(rv.Float(), 'g', -1, 32) 241 | case reflect.Bool: 242 | return strconv.FormatBool(rv.Bool()) 243 | } 244 | return fmt.Sprintf("%v", src) 245 | } 246 | 247 | func asBytes(buf []byte, rv reflect.Value) (b []byte, ok bool) { 248 | switch rv.Kind() { 249 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 250 | return strconv.AppendInt(buf, rv.Int(), 10), true 251 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 252 | return strconv.AppendUint(buf, rv.Uint(), 10), true 253 | case reflect.Float32: 254 | return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 32), true 255 | case reflect.Float64: 256 | return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 64), true 257 | case reflect.Bool: 258 | return strconv.AppendBool(buf, rv.Bool()), true 259 | case reflect.String: 260 | s := rv.String() 261 | return append(buf, s...), true 262 | } 263 | return 264 | } 265 | -------------------------------------------------------------------------------- /json_test.go: -------------------------------------------------------------------------------- 1 | package null 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "testing" 7 | ) 8 | 9 | var ( 10 | jsonJSON = []byte(`"hello"`) 11 | ) 12 | 13 | func TestJSONFrom(t *testing.T) { 14 | t.Parallel() 15 | 16 | i := JSONFrom([]byte(`"hello"`)) 17 | assertJSON(t, i, "JSONFrom()") 18 | 19 | zero := JSONFrom(nil) 20 | if zero.Valid { 21 | t.Error("JSONFrom(nil)", "is valid, but should be invalid") 22 | } 23 | 24 | zero = JSONFrom([]byte{}) 25 | if !zero.Valid { 26 | t.Error("JSONFrom([]byte{})", "is invalid, but should be valid") 27 | } 28 | } 29 | 30 | func TestJSONFromPtr(t *testing.T) { 31 | t.Parallel() 32 | 33 | n := []byte(`"hello"`) 34 | iptr := &n 35 | i := JSONFromPtr(iptr) 36 | assertJSON(t, i, "JSONFromPtr()") 37 | 38 | null := JSONFromPtr(nil) 39 | assertNullJSON(t, null, "JSONFromPtr(nil)") 40 | } 41 | 42 | type Test struct { 43 | Name string 44 | Age int 45 | } 46 | 47 | func TestMarshal(t *testing.T) { 48 | t.Parallel() 49 | 50 | var i JSON 51 | 52 | test := &Test{Name: "hello", Age: 15} 53 | 54 | err := i.Marshal(test) 55 | if err != nil { 56 | t.Error(err) 57 | } 58 | 59 | if !bytes.Equal(i.JSON, []byte(`{"Name":"hello","Age":15}`)) { 60 | t.Errorf("Mismatch between received and expected, got: %s", string(i.JSON)) 61 | } 62 | if i.Valid == false { 63 | t.Error("Expected valid true, got Valid false") 64 | } 65 | 66 | err = i.Marshal(nil) 67 | if err != nil { 68 | t.Error(err) 69 | } 70 | 71 | if i.Valid == true { 72 | t.Error("Expected Valid false, got Valid true") 73 | } 74 | } 75 | 76 | func TestUnmarshal(t *testing.T) { 77 | t.Parallel() 78 | 79 | var i JSON 80 | 81 | test := &Test{} 82 | 83 | err := i.Unmarshal(test) 84 | if err != nil { 85 | t.Error(err) 86 | } 87 | 88 | x := &Test{Name: "hello", Age: 15} 89 | err = i.Marshal(x) 90 | if err != nil { 91 | t.Error(err) 92 | } 93 | 94 | if !bytes.Equal(i.JSON, []byte(`{"Name":"hello","Age":15}`)) { 95 | t.Errorf("Mismatch between received and expected, got: %s", string(i.JSON)) 96 | } 97 | 98 | err = i.Unmarshal(test) 99 | if err != nil { 100 | t.Error(err) 101 | } 102 | 103 | if test.Age != 15 { 104 | t.Errorf("Expected 15, got %d", test.Age) 105 | } 106 | if test.Name != "hello" { 107 | t.Errorf("Expected name, got %s", test.Name) 108 | } 109 | } 110 | 111 | func TestUnmarshalJSON(t *testing.T) { 112 | t.Parallel() 113 | 114 | var i JSON 115 | err := json.Unmarshal(jsonJSON, &i) 116 | maybePanic(err) 117 | assertJSON(t, i, "[]byte json") 118 | 119 | var ni JSON 120 | err = ni.UnmarshalJSON([]byte{}) 121 | if err != nil { 122 | t.Error(err) 123 | } 124 | if ni.Valid == false { 125 | t.Errorf("expected Valid to be true, got false") 126 | } 127 | if !bytes.Equal(ni.JSON, nil) { 128 | t.Errorf("Expected JSON to be nil, but was not: %#v %#v", ni.JSON, []byte(nil)) 129 | } 130 | 131 | var null JSON 132 | err = null.UnmarshalJSON(nil) 133 | if err == nil { 134 | t.Error("passing a nil should fail") 135 | } 136 | } 137 | 138 | func TestUnmarshalJSONInStruct(t *testing.T) { 139 | t.Parallel() 140 | 141 | type testStruct struct { 142 | Val JSON `json:"val,omitempty"` 143 | } 144 | 145 | // In this case UnmarshalJSON is never called and it should not be 146 | // considered set nor valid. 147 | t1 := testStruct{} 148 | err := json.Unmarshal([]byte(`{}`), &t1) 149 | if err != nil { 150 | t.Error(err) 151 | } 152 | if t1.Val.Set { 153 | t.Error("should not be set, no value was given") 154 | } 155 | if t1.Val.Valid { 156 | t.Error("should not be valid, no value was given") 157 | } 158 | 159 | // In this case UnmarshalJSON is called with [110 117 108 108] 160 | // in this case the value contained in the JSON should not exist 161 | // and it should be set and !valid. 162 | // 163 | // This is so {"val": null} unmarshalling can turn into an sql null value. 164 | t2 := testStruct{} 165 | err = json.Unmarshal([]byte(`{"val": null}`), &t2) 166 | if err != nil { 167 | t.Error(err) 168 | } 169 | if !t2.Val.Set { 170 | t.Error("should be set") 171 | } 172 | if t2.Val.Valid { 173 | t.Error("should not be valid") 174 | } 175 | } 176 | 177 | func TestTextUnmarshalJSON(t *testing.T) { 178 | t.Parallel() 179 | 180 | var i JSON 181 | err := i.UnmarshalText([]byte(`"hello"`)) 182 | maybePanic(err) 183 | assertJSON(t, i, "UnmarshalText() []byte") 184 | 185 | var blank JSON 186 | err = blank.UnmarshalText([]byte("")) 187 | maybePanic(err) 188 | assertNullJSON(t, blank, "UnmarshalText() empty []byte") 189 | } 190 | 191 | func TestMarshalJSON(t *testing.T) { 192 | t.Parallel() 193 | 194 | i := JSONFrom([]byte(`"hello"`)) 195 | data, err := json.Marshal(i) 196 | maybePanic(err) 197 | assertJSONEquals(t, data, `"hello"`, "non-empty json marshal") 198 | 199 | // invalid values should be encoded as null 200 | null := NewJSON(nil, false) 201 | data, err = json.Marshal(null) 202 | maybePanic(err) 203 | assertJSONEquals(t, data, "null", "null json marshal") 204 | } 205 | 206 | func TestMarshalJSONText(t *testing.T) { 207 | t.Parallel() 208 | 209 | i := JSONFrom([]byte(`"hello"`)) 210 | data, err := i.MarshalText() 211 | maybePanic(err) 212 | assertJSONEquals(t, data, `"hello"`, "non-empty text marshal") 213 | 214 | // invalid values should be encoded as null 215 | null := NewJSON(nil, false) 216 | data, err = null.MarshalText() 217 | maybePanic(err) 218 | assertJSONEquals(t, data, "", "null text marshal") 219 | } 220 | 221 | func TestJSONPointer(t *testing.T) { 222 | t.Parallel() 223 | 224 | i := JSONFrom([]byte(`"hello"`)) 225 | ptr := i.Ptr() 226 | if !bytes.Equal(*ptr, []byte(`"hello"`)) { 227 | t.Errorf("bad %s []byte: %#v ≠ %s\n", "pointer", ptr, `"hello"`) 228 | } 229 | 230 | null := NewJSON(nil, false) 231 | ptr = null.Ptr() 232 | if ptr != nil { 233 | t.Errorf("bad %s []byte: %#v ≠ %s\n", "nil pointer", ptr, "nil") 234 | } 235 | } 236 | 237 | func TestJSONIsZero(t *testing.T) { 238 | t.Parallel() 239 | 240 | i := JSONFrom([]byte(`"hello"`)) 241 | if i.IsZero() { 242 | t.Errorf("IsZero() should be false") 243 | } 244 | 245 | null := NewJSON(nil, false) 246 | if !null.IsZero() { 247 | t.Errorf("IsZero() should be true") 248 | } 249 | 250 | zero := NewJSON(nil, true) 251 | if zero.IsZero() { 252 | t.Errorf("IsZero() should be false") 253 | } 254 | } 255 | 256 | func TestJSONSetValid(t *testing.T) { 257 | t.Parallel() 258 | 259 | change := NewJSON(nil, false) 260 | assertNullJSON(t, change, "SetValid()") 261 | change.SetValid([]byte(`"hello"`)) 262 | assertJSON(t, change, "SetValid()") 263 | } 264 | 265 | func TestJSONScan(t *testing.T) { 266 | t.Parallel() 267 | 268 | var i JSON 269 | err := i.Scan(`"hello"`) 270 | maybePanic(err) 271 | assertJSON(t, i, "scanned []byte") 272 | 273 | var null JSON 274 | err = null.Scan(nil) 275 | maybePanic(err) 276 | assertNullJSON(t, null, "scanned null") 277 | } 278 | 279 | func assertJSON(t *testing.T, i JSON, from string) { 280 | t.Helper() 281 | if !bytes.Equal(i.JSON, []byte(`"hello"`)) { 282 | t.Errorf("bad %s []byte: %#v ≠ %#v\n", from, string(i.JSON), string([]byte(`"hello"`))) 283 | } 284 | if !i.Valid { 285 | t.Error(from, "is invalid, but should be valid") 286 | } 287 | } 288 | 289 | func assertNullJSON(t *testing.T, i JSON, from string) { 290 | t.Helper() 291 | if i.Valid { 292 | t.Error(from, "is valid, but should be invalid") 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /convert/convert_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // These functions are copied from database/sql/convert_test.go build 1.6.2 6 | 7 | package convert 8 | 9 | import ( 10 | "database/sql" 11 | "database/sql/driver" 12 | "fmt" 13 | "reflect" 14 | "runtime" 15 | "testing" 16 | "time" 17 | ) 18 | 19 | var someTime = time.Unix(123, 0) 20 | var answer int64 = 42 21 | 22 | type userDefined float64 23 | 24 | type userDefinedSlice []int 25 | 26 | type conversionTest struct { 27 | s, d interface{} // source and destination 28 | 29 | // following are used if they're non-zero 30 | wantint int64 31 | wantuint uint64 32 | wantstr string 33 | wantbytes []byte 34 | wantraw sql.RawBytes 35 | wantf32 float32 36 | wantf64 float64 37 | wanttime time.Time 38 | wantbool bool // used if d is of type *bool 39 | wanterr string 40 | wantiface interface{} 41 | wantptr *int64 // if non-nil, *d's pointed value must be equal to *wantptr 42 | wantnil bool // if true, *d must be *int64(nil) 43 | wantusrdef userDefined 44 | } 45 | 46 | // Target variables for scanning into. 47 | var ( 48 | scanstr string 49 | scanbytes []byte 50 | scanraw sql.RawBytes 51 | scanint int 52 | scanint8 int8 53 | scanint16 int16 54 | scanint32 int32 55 | scanuint8 uint8 56 | scanuint16 uint16 57 | scanbool bool 58 | scanf32 float32 59 | scanf64 float64 60 | scantime time.Time 61 | scanptr *int64 62 | scaniface interface{} 63 | ) 64 | 65 | var conversionTests = []conversionTest{ 66 | // Exact conversions (destination pointer type matches source type) 67 | {s: "foo", d: &scanstr, wantstr: "foo"}, 68 | {s: 123, d: &scanint, wantint: 123}, 69 | {s: someTime, d: &scantime, wanttime: someTime}, 70 | 71 | // To strings 72 | {s: "string", d: &scanstr, wantstr: "string"}, 73 | {s: []byte("byteslice"), d: &scanstr, wantstr: "byteslice"}, 74 | {s: 123, d: &scanstr, wantstr: "123"}, 75 | {s: int8(123), d: &scanstr, wantstr: "123"}, 76 | {s: int64(123), d: &scanstr, wantstr: "123"}, 77 | {s: uint8(123), d: &scanstr, wantstr: "123"}, 78 | {s: uint16(123), d: &scanstr, wantstr: "123"}, 79 | {s: uint32(123), d: &scanstr, wantstr: "123"}, 80 | {s: uint64(123), d: &scanstr, wantstr: "123"}, 81 | {s: 1.5, d: &scanstr, wantstr: "1.5"}, 82 | 83 | // From time.Time: 84 | {s: time.Unix(1, 0).UTC(), d: &scanstr, wantstr: "1970-01-01T00:00:01Z"}, 85 | {s: time.Unix(1453874597, 0).In(time.FixedZone("here", -3600*8)), d: &scanstr, wantstr: "2016-01-26T22:03:17-08:00"}, 86 | {s: time.Unix(1, 2).UTC(), d: &scanstr, wantstr: "1970-01-01T00:00:01.000000002Z"}, 87 | {s: time.Time{}, d: &scanstr, wantstr: "0001-01-01T00:00:00Z"}, 88 | {s: time.Unix(1, 2).UTC(), d: &scanbytes, wantbytes: []byte("1970-01-01T00:00:01.000000002Z")}, 89 | {s: time.Unix(1, 2).UTC(), d: &scaniface, wantiface: time.Unix(1, 2).UTC()}, 90 | 91 | // To []byte 92 | {s: nil, d: &scanbytes, wantbytes: nil}, 93 | {s: "string", d: &scanbytes, wantbytes: []byte("string")}, 94 | {s: []byte("byteslice"), d: &scanbytes, wantbytes: []byte("byteslice")}, 95 | {s: 123, d: &scanbytes, wantbytes: []byte("123")}, 96 | {s: int8(123), d: &scanbytes, wantbytes: []byte("123")}, 97 | {s: int64(123), d: &scanbytes, wantbytes: []byte("123")}, 98 | {s: uint8(123), d: &scanbytes, wantbytes: []byte("123")}, 99 | {s: uint16(123), d: &scanbytes, wantbytes: []byte("123")}, 100 | {s: uint32(123), d: &scanbytes, wantbytes: []byte("123")}, 101 | {s: uint64(123), d: &scanbytes, wantbytes: []byte("123")}, 102 | {s: 1.5, d: &scanbytes, wantbytes: []byte("1.5")}, 103 | 104 | // To sql.RawBytes 105 | {s: nil, d: &scanraw, wantraw: nil}, 106 | {s: []byte("byteslice"), d: &scanraw, wantraw: sql.RawBytes("byteslice")}, 107 | {s: 123, d: &scanraw, wantraw: sql.RawBytes("123")}, 108 | {s: int8(123), d: &scanraw, wantraw: sql.RawBytes("123")}, 109 | {s: int64(123), d: &scanraw, wantraw: sql.RawBytes("123")}, 110 | {s: uint8(123), d: &scanraw, wantraw: sql.RawBytes("123")}, 111 | {s: uint16(123), d: &scanraw, wantraw: sql.RawBytes("123")}, 112 | {s: uint32(123), d: &scanraw, wantraw: sql.RawBytes("123")}, 113 | {s: uint64(123), d: &scanraw, wantraw: sql.RawBytes("123")}, 114 | {s: 1.5, d: &scanraw, wantraw: sql.RawBytes("1.5")}, 115 | 116 | // Strings to integers 117 | {s: "127", d: &scanint8, wantint: 127}, 118 | {s: "128", d: &scanint8, wanterr: `converting driver.Value type string ("128") to a int8: value out of range`}, 119 | {s: "32767", d: &scanint16, wantint: 32767}, 120 | {s: "32768", d: &scanint16, wanterr: `converting driver.Value type string ("32768") to a int16: value out of range`}, 121 | {s: "2147483647", d: &scanint32, wantint: 2147483647}, 122 | {s: "2147483648", d: &scanint32, wanterr: `converting driver.Value type string ("2147483648") to a int32: value out of range`}, 123 | {s: "255", d: &scanuint8, wantuint: 255}, 124 | {s: "256", d: &scanuint8, wanterr: `converting driver.Value type string ("256") to a uint8: value out of range`}, 125 | {s: "256", d: &scanuint16, wantuint: 256}, 126 | {s: "-1", d: &scanint, wantint: -1}, 127 | {s: "foo", d: &scanint, wanterr: `converting driver.Value type string ("foo") to a int: invalid syntax`}, 128 | 129 | // int64 to smaller integers 130 | {s: int64(5), d: &scanuint8, wantuint: 5}, 131 | {s: int64(256), d: &scanuint8, wanterr: `converting driver.Value type int64 ("256") to a uint8: value out of range`}, 132 | {s: int64(256), d: &scanuint16, wantuint: 256}, 133 | {s: int64(65536), d: &scanuint16, wanterr: `converting driver.Value type int64 ("65536") to a uint16: value out of range`}, 134 | 135 | // True bools 136 | {s: true, d: &scanbool, wantbool: true}, 137 | {s: "True", d: &scanbool, wantbool: true}, 138 | {s: "TRUE", d: &scanbool, wantbool: true}, 139 | {s: "1", d: &scanbool, wantbool: true}, 140 | {s: 1, d: &scanbool, wantbool: true}, 141 | {s: int64(1), d: &scanbool, wantbool: true}, 142 | {s: uint16(1), d: &scanbool, wantbool: true}, 143 | 144 | // False bools 145 | {s: false, d: &scanbool, wantbool: false}, 146 | {s: "false", d: &scanbool, wantbool: false}, 147 | {s: "FALSE", d: &scanbool, wantbool: false}, 148 | {s: "0", d: &scanbool, wantbool: false}, 149 | {s: 0, d: &scanbool, wantbool: false}, 150 | {s: int64(0), d: &scanbool, wantbool: false}, 151 | {s: uint16(0), d: &scanbool, wantbool: false}, 152 | 153 | // Not bools 154 | {s: "yup", d: &scanbool, wanterr: `sql/driver: couldn't convert "yup" into type bool`}, 155 | {s: 2, d: &scanbool, wanterr: `sql/driver: couldn't convert 2 into type bool`}, 156 | 157 | // Floats 158 | {s: float64(1.5), d: &scanf64, wantf64: float64(1.5)}, 159 | {s: int64(1), d: &scanf64, wantf64: float64(1)}, 160 | {s: float64(1.5), d: &scanf32, wantf32: float32(1.5)}, 161 | {s: "1.5", d: &scanf32, wantf32: float32(1.5)}, 162 | {s: "1.5", d: &scanf64, wantf64: float64(1.5)}, 163 | 164 | // Pointers 165 | {s: interface{}(nil), d: &scanptr, wantnil: true}, 166 | {s: int64(42), d: &scanptr, wantptr: &answer}, 167 | 168 | // To interface{} 169 | {s: float64(1.5), d: &scaniface, wantiface: float64(1.5)}, 170 | {s: int64(1), d: &scaniface, wantiface: int64(1)}, 171 | {s: "str", d: &scaniface, wantiface: "str"}, 172 | {s: []byte("byteslice"), d: &scaniface, wantiface: []byte("byteslice")}, 173 | {s: true, d: &scaniface, wantiface: true}, 174 | {s: nil, d: &scaniface}, 175 | {s: []byte(nil), d: &scaniface, wantiface: []byte(nil)}, 176 | 177 | // To a user-defined type 178 | {s: 1.5, d: new(userDefined), wantusrdef: 1.5}, 179 | {s: int64(123), d: new(userDefined), wantusrdef: 123}, 180 | {s: "1.5", d: new(userDefined), wantusrdef: 1.5}, 181 | {s: []byte{1, 2, 3}, d: new(userDefinedSlice), wanterr: `unsupported Scan, storing driver.Value type []uint8 into type *convert.userDefinedSlice`}, 182 | 183 | // Other errors 184 | {s: complex(1, 2), d: &scanstr, wanterr: `unsupported Scan, storing driver.Value type complex128 into type *string`}, 185 | } 186 | 187 | func intPtrValue(intptr interface{}) interface{} { 188 | return reflect.Indirect(reflect.Indirect(reflect.ValueOf(intptr))).Int() 189 | } 190 | 191 | func intValue(intptr interface{}) int64 { 192 | return reflect.Indirect(reflect.ValueOf(intptr)).Int() 193 | } 194 | 195 | func uintValue(intptr interface{}) uint64 { 196 | return reflect.Indirect(reflect.ValueOf(intptr)).Uint() 197 | } 198 | 199 | func float64Value(ptr interface{}) float64 { 200 | return *(ptr.(*float64)) 201 | } 202 | 203 | func float32Value(ptr interface{}) float32 { 204 | return *(ptr.(*float32)) 205 | } 206 | 207 | func getTimeValue(ptr interface{}) time.Time { 208 | return *(ptr.(*time.Time)) 209 | } 210 | 211 | func TestConversions(t *testing.T) { 212 | for n, ct := range conversionTests { 213 | err := ConvertAssign(ct.d, ct.s) 214 | errstr := "" 215 | if err != nil { 216 | errstr = err.Error() 217 | } 218 | errf := func(format string, args ...interface{}) { 219 | base := fmt.Sprintf("ConvertAssign #%d: for %v (%T) -> %T, ", n, ct.s, ct.s, ct.d) 220 | t.Errorf(base+format, args...) 221 | } 222 | if errstr != ct.wanterr { 223 | errf("got error %q, want error %q", errstr, ct.wanterr) 224 | } 225 | if ct.wantstr != "" && ct.wantstr != scanstr { 226 | errf("want string %q, got %q", ct.wantstr, scanstr) 227 | } 228 | if ct.wantint != 0 && ct.wantint != intValue(ct.d) { 229 | errf("want int %d, got %d", ct.wantint, intValue(ct.d)) 230 | } 231 | if ct.wantuint != 0 && ct.wantuint != uintValue(ct.d) { 232 | errf("want uint %d, got %d", ct.wantuint, uintValue(ct.d)) 233 | } 234 | if ct.wantf32 != 0 && ct.wantf32 != float32Value(ct.d) { 235 | errf("want float32 %v, got %v", ct.wantf32, float32Value(ct.d)) 236 | } 237 | if ct.wantf64 != 0 && ct.wantf64 != float64Value(ct.d) { 238 | errf("want float32 %v, got %v", ct.wantf64, float64Value(ct.d)) 239 | } 240 | if bp, boolTest := ct.d.(*bool); boolTest && *bp != ct.wantbool && ct.wanterr == "" { 241 | errf("want bool %v, got %v", ct.wantbool, *bp) 242 | } 243 | if !ct.wanttime.IsZero() && !ct.wanttime.Equal(getTimeValue(ct.d)) { 244 | errf("want time %v, got %v", ct.wanttime, getTimeValue(ct.d)) 245 | } 246 | if ct.wantnil && *ct.d.(**int64) != nil { 247 | errf("want nil, got %v", intPtrValue(ct.d)) 248 | } 249 | if ct.wantptr != nil { 250 | if *ct.d.(**int64) == nil { 251 | errf("want pointer to %v, got nil", *ct.wantptr) 252 | } else if *ct.wantptr != intPtrValue(ct.d) { 253 | errf("want pointer to %v, got %v", *ct.wantptr, intPtrValue(ct.d)) 254 | } 255 | } 256 | if len(ct.wantraw) != 0 { 257 | s := fmt.Sprintf("%v", ct.s) 258 | if _, ok := ct.s.([]byte); ok { 259 | s = fmt.Sprintf("%s", ct.s) 260 | } 261 | if s != string(ct.wantraw) { 262 | errf("want %q, got: %s", string(ct.wantraw), s) 263 | } 264 | } 265 | if len(ct.wantbytes) != 0 { 266 | s := fmt.Sprintf("%v", ct.s) 267 | if _, ok := ct.s.([]byte); ok { 268 | s = fmt.Sprintf("%s", ct.s) 269 | } 270 | if timeVal, ok := ct.s.(time.Time); ok { 271 | s = timeVal.Format(time.RFC3339Nano) 272 | } 273 | if s != string(ct.wantbytes) { 274 | errf("want %q, got: %s", string(ct.wantbytes), s) 275 | } 276 | } 277 | if ifptr, ok := ct.d.(*interface{}); ok { 278 | if !reflect.DeepEqual(ct.wantiface, scaniface) { 279 | errf("want interface %#v, got %#v", ct.wantiface, scaniface) 280 | continue 281 | } 282 | if srcBytes, ok := ct.s.([]byte); ok { 283 | dstBytes := (*ifptr).([]byte) 284 | if len(srcBytes) > 0 && &dstBytes[0] == &srcBytes[0] { 285 | errf("copy into interface{} didn't copy []byte data") 286 | } 287 | } 288 | } 289 | if ct.wantusrdef != 0 && ct.wantusrdef != *ct.d.(*userDefined) { 290 | errf("want userDefined %f, got %f", ct.wantusrdef, *ct.d.(*userDefined)) 291 | } 292 | } 293 | } 294 | 295 | func TestNullString(t *testing.T) { 296 | var ns sql.NullString 297 | if err := ConvertAssign(&ns, []byte("foo")); err != nil { 298 | t.Error(err) 299 | } 300 | if !ns.Valid { 301 | t.Errorf("expecting not null") 302 | } 303 | if ns.String != "foo" { 304 | t.Errorf("expecting foo; got %q", ns.String) 305 | } 306 | if err := ConvertAssign(&ns, nil); err != nil { 307 | t.Error(err) 308 | } 309 | if ns.Valid { 310 | t.Errorf("expecting null on nil") 311 | } 312 | if ns.String != "" { 313 | t.Errorf("expecting blank on nil; got %q", ns.String) 314 | } 315 | } 316 | 317 | type valueConverterTest struct { 318 | c driver.ValueConverter 319 | in, out interface{} 320 | err string 321 | } 322 | 323 | var valueConverterTests = []valueConverterTest{ 324 | {driver.DefaultParameterConverter, sql.NullString{String: "hi", Valid: true}, "hi", ""}, 325 | {driver.DefaultParameterConverter, sql.NullString{String: "", Valid: false}, nil, ""}, 326 | } 327 | 328 | func TestValueConverters(t *testing.T) { 329 | for i, tt := range valueConverterTests { 330 | out, err := tt.c.ConvertValue(tt.in) 331 | goterr := "" 332 | if err != nil { 333 | goterr = err.Error() 334 | } 335 | if goterr != tt.err { 336 | t.Errorf("test %d: %T(%T(%v)) error = %q; want error = %q", 337 | i, tt.c, tt.in, tt.in, goterr, tt.err) 338 | } 339 | if tt.err != "" { 340 | continue 341 | } 342 | if !reflect.DeepEqual(out, tt.out) { 343 | t.Errorf("test %d: %T(%T(%v)) = %v (%T); want %v (%T)", 344 | i, tt.c, tt.in, tt.in, out, out, tt.out, tt.out) 345 | } 346 | } 347 | } 348 | 349 | // Tests that assigning to sql.RawBytes doesn't allocate (and also works). 350 | func TestRawBytesAllocs(t *testing.T) { 351 | var tests = []struct { 352 | name string 353 | in interface{} 354 | want string 355 | }{ 356 | {"uint64", uint64(12345678), "12345678"}, 357 | {"uint32", uint32(1234), "1234"}, 358 | {"uint16", uint16(12), "12"}, 359 | {"uint8", uint8(1), "1"}, 360 | {"uint", uint(123), "123"}, 361 | {"int", int(123), "123"}, 362 | {"int8", int8(1), "1"}, 363 | {"int16", int16(12), "12"}, 364 | {"int32", int32(1234), "1234"}, 365 | {"int64", int64(12345678), "12345678"}, 366 | {"float32", float32(1.5), "1.5"}, 367 | {"float64", float64(64), "64"}, 368 | {"bool", false, "false"}, 369 | } 370 | 371 | buf := make(sql.RawBytes, 10) 372 | test := func(name string, in interface{}, want string) { 373 | if err := ConvertAssign(&buf, in); err != nil { 374 | t.Fatalf("%s: ConvertAssign = %v", name, err) 375 | } 376 | match := len(buf) == len(want) 377 | if match { 378 | for i, b := range buf { 379 | if want[i] != b { 380 | match = false 381 | break 382 | } 383 | } 384 | } 385 | if !match { 386 | t.Fatalf("%s: got %q (len %d); want %q (len %d)", name, buf, len(buf), want, len(want)) 387 | } 388 | } 389 | 390 | n := testing.AllocsPerRun(100, func() { 391 | for _, tt := range tests { 392 | test(tt.name, tt.in, tt.want) 393 | } 394 | }) 395 | 396 | // The numbers below are only valid for 64-bit interface word sizes, 397 | // and gc. With 32-bit words there are more convT2E allocs, and 398 | // with gccgo, only pointers currently go in interface data. 399 | // So only care on amd64 gc for now. 400 | measureAllocs := runtime.GOARCH == "amd64" && runtime.Compiler == "gc" 401 | 402 | if n > 0.5 && measureAllocs { 403 | t.Fatalf("allocs = %v; want 0", n) 404 | } 405 | 406 | // This one involves a convT2E allocation, string -> interface{} 407 | n = testing.AllocsPerRun(100, func() { 408 | test("string", "foo", "foo") 409 | }) 410 | if n > 1.5 && measureAllocs { 411 | t.Fatalf("allocs = %v; want max 1", n) 412 | } 413 | } 414 | --------------------------------------------------------------------------------