Codebase list golang-gopkg-guregu-null.v3 / 03ed326
New upstream version 3.1+git20160228.0.41961ce Diego M. Rodriguez 7 years ago
22 changed file(s) with 3220 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 Copyright (c) 2014, Greg Roseberry
1 All rights reserved.
2
3 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
4
5 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
6
7 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.
8
9 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.
0 ## null [![GoDoc](https://godoc.org/github.com/guregu/null?status.svg)](https://godoc.org/github.com/guregu/null) [![Coverage](http://gocover.io/_badge/github.com/guregu/null)](http://gocover.io/github.com/guregu/null)
1 `import "gopkg.in/guregu/null.v3"`
2
3 null is a library with reasonable options for dealing with nullable SQL and JSON values
4
5 There are two packages: `null` and its subpackage `zero`.
6
7 Types in `null` will only be considered null on null input, and will JSON encode to `null`. If you need zero and null be considered separate values, use these.
8
9 Types in `zero` are treated like zero values in Go: blank string input will produce a null `zero.String`, and null Strings will JSON encode to `""`. Zero values of these types will be considered null to SQL. If you need zero and null treated the same, use these.
10
11 All types implement `sql.Scanner` and `driver.Valuer`, so you can use this library in place of `sql.NullXXX`. All types also implement: `encoding.TextMarshaler`, `encoding.TextUnmarshaler`, `json.Marshaler`, and `json.Unmarshaler`.
12
13 ### null package
14
15 `import "gopkg.in/guregu/null.v3"`
16
17 #### null.String
18 Nullable string.
19
20 Marshals to JSON null if SQL source data is null. Zero (blank) input will not produce a null String. Can unmarshal from `sql.NullString` JSON input or string input.
21
22 #### null.Int
23 Nullable int64.
24
25 Marshals to JSON null if SQL source data is null. Zero input will not produce a null Int. Can unmarshal from `sql.NullInt64` JSON input.
26
27 #### null.Float
28 Nullable float64.
29
30 Marshals to JSON null if SQL source data is null. Zero input will not produce a null Float. Can unmarshal from `sql.NullFloat64` JSON input.
31
32 #### null.Bool
33 Nullable bool.
34
35 Marshals to JSON null if SQL source data is null. False input will not produce a null Bool. Can unmarshal from `sql.NullBool` JSON input.
36
37 #### null.Time
38
39 Marshals to JSON null if SQL source data is null. Uses `time.Time`'s marshaler. Can unmarshal from `pq.NullTime` and similar JSON input.
40
41 ### zero package
42
43 `import "gopkg.in/guregu/null.v3/zero"`
44
45 #### zero.String
46 Nullable string.
47
48 Will marshal to a blank string if null. Blank string input produces a null String. Null values and zero values are considered equivalent. Can unmarshal from `sql.NullString` JSON input.
49
50 #### zero.Int
51 Nullable int64.
52
53 Will marshal to 0 if null. 0 produces a null Int. Null values and zero values are considered equivalent. Can unmarshal from `sql.NullInt64` JSON input.
54
55 #### zero.Float
56 Nullable float64.
57
58 Will marshal to 0 if null. 0.0 produces a null Float. Null values and zero values are considered equivalent. Can unmarshal from `sql.NullFloat64` JSON input.
59
60 #### zero.Bool
61 Nullable bool.
62
63 Will marshal to false if null. `false` produces a null Float. Null values and zero values are considered equivalent. Can unmarshal from `sql.NullBool` JSON input.
64
65 #### zero.Time
66
67 Will marshal to the zero time if null. Uses `time.Time`'s marshaler. Can unmarshal from `pq.NullTime` and similar JSON input.
68
69
70 ### Bugs
71 `json`'s `",omitempty"` struct tag does not work correctly right now. It will never omit a null or empty String. This might be [fixed eventually](https://github.com/golang/go/issues/4357).
72
73 ### License
74 BSD
0 package null
1
2 import (
3 "database/sql"
4 "encoding/json"
5 "errors"
6 "fmt"
7 "reflect"
8 )
9
10 // Bool is a nullable bool.
11 // It does not consider false values to be null.
12 // It will decode to null, not false, if null.
13 type Bool struct {
14 sql.NullBool
15 }
16
17 // NewBool creates a new Bool
18 func NewBool(b bool, valid bool) Bool {
19 return Bool{
20 NullBool: sql.NullBool{
21 Bool: b,
22 Valid: valid,
23 },
24 }
25 }
26
27 // BoolFrom creates a new Bool that will always be valid.
28 func BoolFrom(b bool) Bool {
29 return NewBool(b, true)
30 }
31
32 // BoolFromPtr creates a new Bool that will be null if f is nil.
33 func BoolFromPtr(b *bool) Bool {
34 if b == nil {
35 return NewBool(false, false)
36 }
37 return NewBool(*b, true)
38 }
39
40 // UnmarshalJSON implements json.Unmarshaler.
41 // It supports number and null input.
42 // 0 will not be considered a null Bool.
43 // It also supports unmarshalling a sql.NullBool.
44 func (b *Bool) UnmarshalJSON(data []byte) error {
45 var err error
46 var v interface{}
47 if err = json.Unmarshal(data, &v); err != nil {
48 return err
49 }
50 switch x := v.(type) {
51 case bool:
52 b.Bool = x
53 case map[string]interface{}:
54 err = json.Unmarshal(data, &b.NullBool)
55 case nil:
56 b.Valid = false
57 return nil
58 default:
59 err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Bool", reflect.TypeOf(v).Name())
60 }
61 b.Valid = err == nil
62 return err
63 }
64
65 // UnmarshalText implements encoding.TextUnmarshaler.
66 // It will unmarshal to a null Bool if the input is a blank or not an integer.
67 // It will return an error if the input is not an integer, blank, or "null".
68 func (b *Bool) UnmarshalText(text []byte) error {
69 str := string(text)
70 switch str {
71 case "", "null":
72 b.Valid = false
73 return nil
74 case "true":
75 b.Bool = true
76 case "false":
77 b.Bool = false
78 default:
79 b.Valid = false
80 return errors.New("invalid input:" + str)
81 }
82 b.Valid = true
83 return nil
84 }
85
86 // MarshalJSON implements json.Marshaler.
87 // It will encode null if this Bool is null.
88 func (b Bool) MarshalJSON() ([]byte, error) {
89 if !b.Valid {
90 return []byte("null"), nil
91 }
92 if !b.Bool {
93 return []byte("false"), nil
94 }
95 return []byte("true"), nil
96 }
97
98 // MarshalText implements encoding.TextMarshaler.
99 // It will encode a blank string if this Bool is null.
100 func (b Bool) MarshalText() ([]byte, error) {
101 if !b.Valid {
102 return []byte{}, nil
103 }
104 if !b.Bool {
105 return []byte("false"), nil
106 }
107 return []byte("true"), nil
108 }
109
110 // SetValid changes this Bool's value and also sets it to be non-null.
111 func (b *Bool) SetValid(v bool) {
112 b.Bool = v
113 b.Valid = true
114 }
115
116 // Ptr returns a pointer to this Bool's value, or a nil pointer if this Bool is null.
117 func (b Bool) Ptr() *bool {
118 if !b.Valid {
119 return nil
120 }
121 return &b.Bool
122 }
123
124 // IsZero returns true for invalid Bools, for future omitempty support (Go 1.4?)
125 // A non-null Bool with a 0 value will not be considered zero.
126 func (b Bool) IsZero() bool {
127 return !b.Valid
128 }
0 package null
1
2 import (
3 "encoding/json"
4 "testing"
5 )
6
7 var (
8 boolJSON = []byte(`true`)
9 falseJSON = []byte(`false`)
10 nullBoolJSON = []byte(`{"Bool":true,"Valid":true}`)
11 )
12
13 func TestBoolFrom(t *testing.T) {
14 b := BoolFrom(true)
15 assertBool(t, b, "BoolFrom()")
16
17 zero := BoolFrom(false)
18 if !zero.Valid {
19 t.Error("BoolFrom(false)", "is invalid, but should be valid")
20 }
21 }
22
23 func TestBoolFromPtr(t *testing.T) {
24 n := true
25 bptr := &n
26 b := BoolFromPtr(bptr)
27 assertBool(t, b, "BoolFromPtr()")
28
29 null := BoolFromPtr(nil)
30 assertNullBool(t, null, "BoolFromPtr(nil)")
31 }
32
33 func TestUnmarshalBool(t *testing.T) {
34 var b Bool
35 err := json.Unmarshal(boolJSON, &b)
36 maybePanic(err)
37 assertBool(t, b, "bool json")
38
39 var nb Bool
40 err = json.Unmarshal(nullBoolJSON, &nb)
41 maybePanic(err)
42 assertBool(t, nb, "sq.NullBool json")
43
44 var null Bool
45 err = json.Unmarshal(nullJSON, &null)
46 maybePanic(err)
47 assertNullBool(t, null, "null json")
48
49 var badType Bool
50 err = json.Unmarshal(intJSON, &badType)
51 if err == nil {
52 panic("err should not be nil")
53 }
54 assertNullBool(t, badType, "wrong type json")
55
56 var invalid Bool
57 err = invalid.UnmarshalJSON(invalidJSON)
58 if _, ok := err.(*json.SyntaxError); !ok {
59 t.Errorf("expected json.SyntaxError, not %T", err)
60 }
61 }
62
63 func TestTextUnmarshalBool(t *testing.T) {
64 var b Bool
65 err := b.UnmarshalText([]byte("true"))
66 maybePanic(err)
67 assertBool(t, b, "UnmarshalText() bool")
68
69 var zero Bool
70 err = zero.UnmarshalText([]byte("false"))
71 maybePanic(err)
72 assertFalseBool(t, zero, "UnmarshalText() false")
73
74 var blank Bool
75 err = blank.UnmarshalText([]byte(""))
76 maybePanic(err)
77 assertNullBool(t, blank, "UnmarshalText() empty bool")
78
79 var null Bool
80 err = null.UnmarshalText([]byte("null"))
81 maybePanic(err)
82 assertNullBool(t, null, `UnmarshalText() "null"`)
83
84 var invalid Bool
85 err = invalid.UnmarshalText([]byte(":D"))
86 if err == nil {
87 panic("err should not be nil")
88 }
89 assertNullBool(t, invalid, "invalid json")
90 }
91
92 func TestMarshalBool(t *testing.T) {
93 b := BoolFrom(true)
94 data, err := json.Marshal(b)
95 maybePanic(err)
96 assertJSONEquals(t, data, "true", "non-empty json marshal")
97
98 zero := NewBool(false, true)
99 data, err = json.Marshal(zero)
100 maybePanic(err)
101 assertJSONEquals(t, data, "false", "zero json marshal")
102
103 // invalid values should be encoded as null
104 null := NewBool(false, false)
105 data, err = json.Marshal(null)
106 maybePanic(err)
107 assertJSONEquals(t, data, "null", "null json marshal")
108 }
109
110 func TestMarshalBoolText(t *testing.T) {
111 b := BoolFrom(true)
112 data, err := b.MarshalText()
113 maybePanic(err)
114 assertJSONEquals(t, data, "true", "non-empty text marshal")
115
116 zero := NewBool(false, true)
117 data, err = zero.MarshalText()
118 maybePanic(err)
119 assertJSONEquals(t, data, "false", "zero text marshal")
120
121 // invalid values should be encoded as null
122 null := NewBool(false, false)
123 data, err = null.MarshalText()
124 maybePanic(err)
125 assertJSONEquals(t, data, "", "null text marshal")
126 }
127
128 func TestBoolPointer(t *testing.T) {
129 b := BoolFrom(true)
130 ptr := b.Ptr()
131 if *ptr != true {
132 t.Errorf("bad %s bool: %#v ≠ %v\n", "pointer", ptr, true)
133 }
134
135 null := NewBool(false, false)
136 ptr = null.Ptr()
137 if ptr != nil {
138 t.Errorf("bad %s bool: %#v ≠ %s\n", "nil pointer", ptr, "nil")
139 }
140 }
141
142 func TestBoolIsZero(t *testing.T) {
143 b := BoolFrom(true)
144 if b.IsZero() {
145 t.Errorf("IsZero() should be false")
146 }
147
148 null := NewBool(false, false)
149 if !null.IsZero() {
150 t.Errorf("IsZero() should be true")
151 }
152
153 zero := NewBool(false, true)
154 if zero.IsZero() {
155 t.Errorf("IsZero() should be false")
156 }
157 }
158
159 func TestBoolSetValid(t *testing.T) {
160 change := NewBool(false, false)
161 assertNullBool(t, change, "SetValid()")
162 change.SetValid(true)
163 assertBool(t, change, "SetValid()")
164 }
165
166 func TestBoolScan(t *testing.T) {
167 var b Bool
168 err := b.Scan(true)
169 maybePanic(err)
170 assertBool(t, b, "scanned bool")
171
172 var null Bool
173 err = null.Scan(nil)
174 maybePanic(err)
175 assertNullBool(t, null, "scanned null")
176 }
177
178 func assertBool(t *testing.T, b Bool, from string) {
179 if b.Bool != true {
180 t.Errorf("bad %s bool: %v ≠ %v\n", from, b.Bool, true)
181 }
182 if !b.Valid {
183 t.Error(from, "is invalid, but should be valid")
184 }
185 }
186
187 func assertFalseBool(t *testing.T, b Bool, from string) {
188 if b.Bool != false {
189 t.Errorf("bad %s bool: %v ≠ %v\n", from, b.Bool, false)
190 }
191 if !b.Valid {
192 t.Error(from, "is invalid, but should be valid")
193 }
194 }
195
196 func assertNullBool(t *testing.T, b Bool, from string) {
197 if b.Valid {
198 t.Error(from, "is valid, but should be invalid")
199 }
200 }
0 package null
1
2 import (
3 "database/sql"
4 "encoding/json"
5 "fmt"
6 "reflect"
7 "strconv"
8 )
9
10 // Float is a nullable float64.
11 // It does not consider zero values to be null.
12 // It will decode to null, not zero, if null.
13 type Float struct {
14 sql.NullFloat64
15 }
16
17 // NewFloat creates a new Float
18 func NewFloat(f float64, valid bool) Float {
19 return Float{
20 NullFloat64: sql.NullFloat64{
21 Float64: f,
22 Valid: valid,
23 },
24 }
25 }
26
27 // FloatFrom creates a new Float that will always be valid.
28 func FloatFrom(f float64) Float {
29 return NewFloat(f, true)
30 }
31
32 // FloatFromPtr creates a new Float that be null if f is nil.
33 func FloatFromPtr(f *float64) Float {
34 if f == nil {
35 return NewFloat(0, false)
36 }
37 return NewFloat(*f, true)
38 }
39
40 // UnmarshalJSON implements json.Unmarshaler.
41 // It supports number and null input.
42 // 0 will not be considered a null Float.
43 // It also supports unmarshalling a sql.NullFloat64.
44 func (f *Float) UnmarshalJSON(data []byte) error {
45 var err error
46 var v interface{}
47 if err = json.Unmarshal(data, &v); err != nil {
48 return err
49 }
50 switch x := v.(type) {
51 case float64:
52 f.Float64 = float64(x)
53 case map[string]interface{}:
54 err = json.Unmarshal(data, &f.NullFloat64)
55 case nil:
56 f.Valid = false
57 return nil
58 default:
59 err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Float", reflect.TypeOf(v).Name())
60 }
61 f.Valid = err == nil
62 return err
63 }
64
65 // UnmarshalText implements encoding.TextUnmarshaler.
66 // It will unmarshal to a null Float if the input is a blank or not an integer.
67 // It will return an error if the input is not an integer, blank, or "null".
68 func (f *Float) UnmarshalText(text []byte) error {
69 str := string(text)
70 if str == "" || str == "null" {
71 f.Valid = false
72 return nil
73 }
74 var err error
75 f.Float64, err = strconv.ParseFloat(string(text), 64)
76 f.Valid = err == nil
77 return err
78 }
79
80 // MarshalJSON implements json.Marshaler.
81 // It will encode null if this Float is null.
82 func (f Float) MarshalJSON() ([]byte, error) {
83 if !f.Valid {
84 return []byte("null"), nil
85 }
86 return []byte(strconv.FormatFloat(f.Float64, 'f', -1, 64)), nil
87 }
88
89 // MarshalText implements encoding.TextMarshaler.
90 // It will encode a blank string if this Float is null.
91 func (f Float) 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 Float's value and also sets it to be non-null.
99 func (f *Float) SetValid(n float64) {
100 f.Float64 = n
101 f.Valid = true
102 }
103
104 // Ptr returns a pointer to this Float's value, or a nil pointer if this Float is null.
105 func (f Float) Ptr() *float64 {
106 if !f.Valid {
107 return nil
108 }
109 return &f.Float64
110 }
111
112 // IsZero returns true for invalid Floats, for future omitempty support (Go 1.4?)
113 // A non-null Float with a 0 value will not be considered zero.
114 func (f Float) IsZero() bool {
115 return !f.Valid
116 }
0 package null
1
2 import (
3 "encoding/json"
4 "testing"
5 )
6
7 var (
8 floatJSON = []byte(`1.2345`)
9 nullFloatJSON = []byte(`{"Float64":1.2345,"Valid":true}`)
10 )
11
12 func TestFloatFrom(t *testing.T) {
13 f := FloatFrom(1.2345)
14 assertFloat(t, f, "FloatFrom()")
15
16 zero := FloatFrom(0)
17 if !zero.Valid {
18 t.Error("FloatFrom(0)", "is invalid, but should be valid")
19 }
20 }
21
22 func TestFloatFromPtr(t *testing.T) {
23 n := float64(1.2345)
24 iptr := &n
25 f := FloatFromPtr(iptr)
26 assertFloat(t, f, "FloatFromPtr()")
27
28 null := FloatFromPtr(nil)
29 assertNullFloat(t, null, "FloatFromPtr(nil)")
30 }
31
32 func TestUnmarshalFloat(t *testing.T) {
33 var f Float
34 err := json.Unmarshal(floatJSON, &f)
35 maybePanic(err)
36 assertFloat(t, f, "float json")
37
38 var nf Float
39 err = json.Unmarshal(nullFloatJSON, &nf)
40 maybePanic(err)
41 assertFloat(t, nf, "sq.NullFloat64 json")
42
43 var null Float
44 err = json.Unmarshal(nullJSON, &null)
45 maybePanic(err)
46 assertNullFloat(t, null, "null json")
47
48 var badType Float
49 err = json.Unmarshal(boolJSON, &badType)
50 if err == nil {
51 panic("err should not be nil")
52 }
53 assertNullFloat(t, badType, "wrong type json")
54
55 var invalid Float
56 err = invalid.UnmarshalJSON(invalidJSON)
57 if _, ok := err.(*json.SyntaxError); !ok {
58 t.Errorf("expected json.SyntaxError, not %T", err)
59 }
60 }
61
62 func TestTextUnmarshalFloat(t *testing.T) {
63 var f Float
64 err := f.UnmarshalText([]byte("1.2345"))
65 maybePanic(err)
66 assertFloat(t, f, "UnmarshalText() float")
67
68 var blank Float
69 err = blank.UnmarshalText([]byte(""))
70 maybePanic(err)
71 assertNullFloat(t, blank, "UnmarshalText() empty float")
72
73 var null Float
74 err = null.UnmarshalText([]byte("null"))
75 maybePanic(err)
76 assertNullFloat(t, null, `UnmarshalText() "null"`)
77 }
78
79 func TestMarshalFloat(t *testing.T) {
80 f := FloatFrom(1.2345)
81 data, err := json.Marshal(f)
82 maybePanic(err)
83 assertJSONEquals(t, data, "1.2345", "non-empty json marshal")
84
85 // invalid values should be encoded as null
86 null := NewFloat(0, false)
87 data, err = json.Marshal(null)
88 maybePanic(err)
89 assertJSONEquals(t, data, "null", "null json marshal")
90 }
91
92 func TestMarshalFloatText(t *testing.T) {
93 f := FloatFrom(1.2345)
94 data, err := f.MarshalText()
95 maybePanic(err)
96 assertJSONEquals(t, data, "1.2345", "non-empty text marshal")
97
98 // invalid values should be encoded as null
99 null := NewFloat(0, false)
100 data, err = null.MarshalText()
101 maybePanic(err)
102 assertJSONEquals(t, data, "", "null text marshal")
103 }
104
105 func TestFloatPointer(t *testing.T) {
106 f := FloatFrom(1.2345)
107 ptr := f.Ptr()
108 if *ptr != 1.2345 {
109 t.Errorf("bad %s float: %#v ≠ %v\n", "pointer", ptr, 1.2345)
110 }
111
112 null := NewFloat(0, false)
113 ptr = null.Ptr()
114 if ptr != nil {
115 t.Errorf("bad %s float: %#v ≠ %s\n", "nil pointer", ptr, "nil")
116 }
117 }
118
119 func TestFloatIsZero(t *testing.T) {
120 f := FloatFrom(1.2345)
121 if f.IsZero() {
122 t.Errorf("IsZero() should be false")
123 }
124
125 null := NewFloat(0, false)
126 if !null.IsZero() {
127 t.Errorf("IsZero() should be true")
128 }
129
130 zero := NewFloat(0, true)
131 if zero.IsZero() {
132 t.Errorf("IsZero() should be false")
133 }
134 }
135
136 func TestFloatSetValid(t *testing.T) {
137 change := NewFloat(0, false)
138 assertNullFloat(t, change, "SetValid()")
139 change.SetValid(1.2345)
140 assertFloat(t, change, "SetValid()")
141 }
142
143 func TestFloatScan(t *testing.T) {
144 var f Float
145 err := f.Scan(1.2345)
146 maybePanic(err)
147 assertFloat(t, f, "scanned float")
148
149 var null Float
150 err = null.Scan(nil)
151 maybePanic(err)
152 assertNullFloat(t, null, "scanned null")
153 }
154
155 func assertFloat(t *testing.T, f Float, from string) {
156 if f.Float64 != 1.2345 {
157 t.Errorf("bad %s float: %f ≠ %f\n", from, f.Float64, 1.2345)
158 }
159 if !f.Valid {
160 t.Error(from, "is invalid, but should be valid")
161 }
162 }
163
164 func assertNullFloat(t *testing.T, f Float, from string) {
165 if f.Valid {
166 t.Error(from, "is valid, but should be invalid")
167 }
168 }
0 package null
1
2 import (
3 "database/sql"
4 "encoding/json"
5 "fmt"
6 "reflect"
7 "strconv"
8 )
9
10 // Int is an nullable int64.
11 // It does not consider zero values to be null.
12 // It will decode to null, not zero, if null.
13 type Int struct {
14 sql.NullInt64
15 }
16
17 // NewInt creates a new Int
18 func NewInt(i int64, valid bool) Int {
19 return Int{
20 NullInt64: sql.NullInt64{
21 Int64: i,
22 Valid: valid,
23 },
24 }
25 }
26
27 // IntFrom creates a new Int that will always be valid.
28 func IntFrom(i int64) Int {
29 return NewInt(i, true)
30 }
31
32 // IntFromPtr creates a new Int that be null if i is nil.
33 func IntFromPtr(i *int64) Int {
34 if i == nil {
35 return NewInt(0, false)
36 }
37 return NewInt(*i, true)
38 }
39
40 // UnmarshalJSON implements json.Unmarshaler.
41 // It supports number and null input.
42 // 0 will not be considered a null Int.
43 // It also supports unmarshalling a sql.NullInt64.
44 func (i *Int) UnmarshalJSON(data []byte) error {
45 var err error
46 var v interface{}
47 if err = json.Unmarshal(data, &v); err != nil {
48 return err
49 }
50 switch v.(type) {
51 case float64:
52 // Unmarshal again, directly to int64, to avoid intermediate float64
53 err = json.Unmarshal(data, &i.Int64)
54 case map[string]interface{}:
55 err = json.Unmarshal(data, &i.NullInt64)
56 case nil:
57 i.Valid = false
58 return nil
59 default:
60 err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Int", reflect.TypeOf(v).Name())
61 }
62 i.Valid = err == nil
63 return err
64 }
65
66 // UnmarshalText implements encoding.TextUnmarshaler.
67 // It will unmarshal to a null Int if the input is a blank or not an integer.
68 // It will return an error if the input is not an integer, blank, or "null".
69 func (i *Int) UnmarshalText(text []byte) error {
70 str := string(text)
71 if str == "" || str == "null" {
72 i.Valid = false
73 return nil
74 }
75 var err error
76 i.Int64, err = strconv.ParseInt(string(text), 10, 64)
77 i.Valid = err == nil
78 return err
79 }
80
81 // MarshalJSON implements json.Marshaler.
82 // It will encode null if this Int is null.
83 func (i Int) MarshalJSON() ([]byte, error) {
84 if !i.Valid {
85 return []byte("null"), nil
86 }
87 return []byte(strconv.FormatInt(i.Int64, 10)), nil
88 }
89
90 // MarshalText implements encoding.TextMarshaler.
91 // It will encode a blank string if this Int is null.
92 func (i Int) MarshalText() ([]byte, error) {
93 if !i.Valid {
94 return []byte{}, nil
95 }
96 return []byte(strconv.FormatInt(i.Int64, 10)), nil
97 }
98
99 // SetValid changes this Int's value and also sets it to be non-null.
100 func (i *Int) SetValid(n int64) {
101 i.Int64 = n
102 i.Valid = true
103 }
104
105 // Ptr returns a pointer to this Int's value, or a nil pointer if this Int is null.
106 func (i Int) Ptr() *int64 {
107 if !i.Valid {
108 return nil
109 }
110 return &i.Int64
111 }
112
113 // IsZero returns true for invalid Ints, for future omitempty support (Go 1.4?)
114 // A non-null Int with a 0 value will not be considered zero.
115 func (i Int) IsZero() bool {
116 return !i.Valid
117 }
0 package null
1
2 import (
3 "encoding/json"
4 "math"
5 "strconv"
6 "testing"
7 )
8
9 var (
10 intJSON = []byte(`12345`)
11 nullIntJSON = []byte(`{"Int64":12345,"Valid":true}`)
12 )
13
14 func TestIntFrom(t *testing.T) {
15 i := IntFrom(12345)
16 assertInt(t, i, "IntFrom()")
17
18 zero := IntFrom(0)
19 if !zero.Valid {
20 t.Error("IntFrom(0)", "is invalid, but should be valid")
21 }
22 }
23
24 func TestIntFromPtr(t *testing.T) {
25 n := int64(12345)
26 iptr := &n
27 i := IntFromPtr(iptr)
28 assertInt(t, i, "IntFromPtr()")
29
30 null := IntFromPtr(nil)
31 assertNullInt(t, null, "IntFromPtr(nil)")
32 }
33
34 func TestUnmarshalInt(t *testing.T) {
35 var i Int
36 err := json.Unmarshal(intJSON, &i)
37 maybePanic(err)
38 assertInt(t, i, "int json")
39
40 var ni Int
41 err = json.Unmarshal(nullIntJSON, &ni)
42 maybePanic(err)
43 assertInt(t, ni, "sq.NullInt64 json")
44
45 var null Int
46 err = json.Unmarshal(nullJSON, &null)
47 maybePanic(err)
48 assertNullInt(t, null, "null json")
49
50 var badType Int
51 err = json.Unmarshal(boolJSON, &badType)
52 if err == nil {
53 panic("err should not be nil")
54 }
55 assertNullInt(t, badType, "wrong type json")
56
57 var invalid Int
58 err = invalid.UnmarshalJSON(invalidJSON)
59 if _, ok := err.(*json.SyntaxError); !ok {
60 t.Errorf("expected json.SyntaxError, not %T", err)
61 }
62 assertNullInt(t, invalid, "invalid json")
63 }
64
65 func TestUnmarshalNonIntegerNumber(t *testing.T) {
66 var i Int
67 err := json.Unmarshal(floatJSON, &i)
68 if err == nil {
69 panic("err should be present; non-integer number coerced to int")
70 }
71 }
72
73 func TestUnmarshalInt64Overflow(t *testing.T) {
74 int64Overflow := uint64(math.MaxInt64)
75
76 // Max int64 should decode successfully
77 var i Int
78 err := json.Unmarshal([]byte(strconv.FormatUint(int64Overflow, 10)), &i)
79 maybePanic(err)
80
81 // Attempt to overflow
82 int64Overflow++
83 err = json.Unmarshal([]byte(strconv.FormatUint(int64Overflow, 10)), &i)
84 if err == nil {
85 panic("err should be present; decoded value overflows int64")
86 }
87 }
88
89 func TestTextUnmarshalInt(t *testing.T) {
90 var i Int
91 err := i.UnmarshalText([]byte("12345"))
92 maybePanic(err)
93 assertInt(t, i, "UnmarshalText() int")
94
95 var blank Int
96 err = blank.UnmarshalText([]byte(""))
97 maybePanic(err)
98 assertNullInt(t, blank, "UnmarshalText() empty int")
99
100 var null Int
101 err = null.UnmarshalText([]byte("null"))
102 maybePanic(err)
103 assertNullInt(t, null, `UnmarshalText() "null"`)
104 }
105
106 func TestMarshalInt(t *testing.T) {
107 i := IntFrom(12345)
108 data, err := json.Marshal(i)
109 maybePanic(err)
110 assertJSONEquals(t, data, "12345", "non-empty json marshal")
111
112 // invalid values should be encoded as null
113 null := NewInt(0, false)
114 data, err = json.Marshal(null)
115 maybePanic(err)
116 assertJSONEquals(t, data, "null", "null json marshal")
117 }
118
119 func TestMarshalIntText(t *testing.T) {
120 i := IntFrom(12345)
121 data, err := i.MarshalText()
122 maybePanic(err)
123 assertJSONEquals(t, data, "12345", "non-empty text marshal")
124
125 // invalid values should be encoded as null
126 null := NewInt(0, false)
127 data, err = null.MarshalText()
128 maybePanic(err)
129 assertJSONEquals(t, data, "", "null text marshal")
130 }
131
132 func TestIntPointer(t *testing.T) {
133 i := IntFrom(12345)
134 ptr := i.Ptr()
135 if *ptr != 12345 {
136 t.Errorf("bad %s int: %#v ≠ %d\n", "pointer", ptr, 12345)
137 }
138
139 null := NewInt(0, false)
140 ptr = null.Ptr()
141 if ptr != nil {
142 t.Errorf("bad %s int: %#v ≠ %s\n", "nil pointer", ptr, "nil")
143 }
144 }
145
146 func TestIntIsZero(t *testing.T) {
147 i := IntFrom(12345)
148 if i.IsZero() {
149 t.Errorf("IsZero() should be false")
150 }
151
152 null := NewInt(0, false)
153 if !null.IsZero() {
154 t.Errorf("IsZero() should be true")
155 }
156
157 zero := NewInt(0, true)
158 if zero.IsZero() {
159 t.Errorf("IsZero() should be false")
160 }
161 }
162
163 func TestIntSetValid(t *testing.T) {
164 change := NewInt(0, false)
165 assertNullInt(t, change, "SetValid()")
166 change.SetValid(12345)
167 assertInt(t, change, "SetValid()")
168 }
169
170 func TestIntScan(t *testing.T) {
171 var i Int
172 err := i.Scan(12345)
173 maybePanic(err)
174 assertInt(t, i, "scanned int")
175
176 var null Int
177 err = null.Scan(nil)
178 maybePanic(err)
179 assertNullInt(t, null, "scanned null")
180 }
181
182 func assertInt(t *testing.T, i Int, from string) {
183 if i.Int64 != 12345 {
184 t.Errorf("bad %s int: %d ≠ %d\n", from, i.Int64, 12345)
185 }
186 if !i.Valid {
187 t.Error(from, "is invalid, but should be valid")
188 }
189 }
190
191 func assertNullInt(t *testing.T, i Int, from string) {
192 if i.Valid {
193 t.Error(from, "is valid, but should be invalid")
194 }
195 }
0 // Package null contains SQL types that consider zero input and null input as separate values,
1 // with convenient support for JSON and text marshaling.
2 // Types in this package will always encode to their null value if null.
3 // Use the zero subpackage if you want zero values and null to be treated the same.
4 package null
5
6 import (
7 "database/sql"
8 "encoding/json"
9 "fmt"
10 "reflect"
11 )
12
13 // String is a nullable string. It supports SQL and JSON serialization.
14 // It will marshal to null if null. Blank string input will be considered null.
15 type String struct {
16 sql.NullString
17 }
18
19 // StringFrom creates a new String that will never be blank.
20 func StringFrom(s string) String {
21 return NewString(s, true)
22 }
23
24 // StringFromPtr creates a new String that be null if s is nil.
25 func StringFromPtr(s *string) String {
26 if s == nil {
27 return NewString("", false)
28 }
29 return NewString(*s, true)
30 }
31
32 // NewString creates a new String
33 func NewString(s string, valid bool) String {
34 return String{
35 NullString: sql.NullString{
36 String: s,
37 Valid: valid,
38 },
39 }
40 }
41
42 // UnmarshalJSON implements json.Unmarshaler.
43 // It supports string and null input. Blank string input does not produce a null String.
44 // It also supports unmarshalling a sql.NullString.
45 func (s *String) UnmarshalJSON(data []byte) error {
46 var err error
47 var v interface{}
48 if err = json.Unmarshal(data, &v); err != nil {
49 return err
50 }
51 switch x := v.(type) {
52 case string:
53 s.String = x
54 case map[string]interface{}:
55 err = json.Unmarshal(data, &s.NullString)
56 case nil:
57 s.Valid = false
58 return nil
59 default:
60 err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.String", reflect.TypeOf(v).Name())
61 }
62 s.Valid = err == nil
63 return err
64 }
65
66 // MarshalJSON implements json.Marshaler.
67 // It will encode null if this String is null.
68 func (s String) MarshalJSON() ([]byte, error) {
69 if !s.Valid {
70 return []byte("null"), nil
71 }
72 return json.Marshal(s.String)
73 }
74
75 // MarshalText implements encoding.TextMarshaler.
76 // It will encode a blank string when this String is null.
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 // It will unmarshal to a null String if the input is a blank string.
86 func (s *String) UnmarshalText(text []byte) error {
87 s.String = string(text)
88 s.Valid = s.String != ""
89 return nil
90 }
91
92 // SetValid changes this String's value and also sets it to be non-null.
93 func (s *String) SetValid(v string) {
94 s.String = v
95 s.Valid = true
96 }
97
98 // Ptr returns a pointer to this String's value, or a nil pointer if this String is null.
99 func (s String) Ptr() *string {
100 if !s.Valid {
101 return nil
102 }
103 return &s.String
104 }
105
106 // IsZero returns true for null strings, for potential future omitempty support.
107 func (s String) IsZero() bool {
108 return !s.Valid
109 }
0 package null
1
2 import (
3 "encoding/json"
4 "testing"
5 )
6
7 var (
8 stringJSON = []byte(`"test"`)
9 blankStringJSON = []byte(`""`)
10 nullStringJSON = []byte(`{"String":"test","Valid":true}`)
11
12 nullJSON = []byte(`null`)
13 invalidJSON = []byte(`:)`)
14 )
15
16 type stringInStruct struct {
17 Test String `json:"test,omitempty"`
18 }
19
20 func TestStringFrom(t *testing.T) {
21 str := StringFrom("test")
22 assertStr(t, str, "StringFrom() string")
23
24 zero := StringFrom("")
25 if !zero.Valid {
26 t.Error("StringFrom(0)", "is invalid, but should be valid")
27 }
28 }
29
30 func TestStringFromPtr(t *testing.T) {
31 s := "test"
32 sptr := &s
33 str := StringFromPtr(sptr)
34 assertStr(t, str, "StringFromPtr() string")
35
36 null := StringFromPtr(nil)
37 assertNullStr(t, null, "StringFromPtr(nil)")
38 }
39
40 func TestUnmarshalString(t *testing.T) {
41 var str String
42 err := json.Unmarshal(stringJSON, &str)
43 maybePanic(err)
44 assertStr(t, str, "string json")
45
46 var ns String
47 err = json.Unmarshal(nullStringJSON, &ns)
48 maybePanic(err)
49 assertStr(t, ns, "sql.NullString json")
50
51 var blank String
52 err = json.Unmarshal(blankStringJSON, &blank)
53 maybePanic(err)
54 if !blank.Valid {
55 t.Error("blank string should be valid")
56 }
57
58 var null String
59 err = json.Unmarshal(nullJSON, &null)
60 maybePanic(err)
61 assertNullStr(t, null, "null json")
62
63 var badType String
64 err = json.Unmarshal(boolJSON, &badType)
65 if err == nil {
66 panic("err should not be nil")
67 }
68 assertNullStr(t, badType, "wrong type json")
69
70 var invalid String
71 err = invalid.UnmarshalJSON(invalidJSON)
72 if _, ok := err.(*json.SyntaxError); !ok {
73 t.Errorf("expected json.SyntaxError, not %T", err)
74 }
75 assertNullStr(t, invalid, "invalid json")
76 }
77
78 func TestTextUnmarshalString(t *testing.T) {
79 var str String
80 err := str.UnmarshalText([]byte("test"))
81 maybePanic(err)
82 assertStr(t, str, "UnmarshalText() string")
83
84 var null String
85 err = null.UnmarshalText([]byte(""))
86 maybePanic(err)
87 assertNullStr(t, null, "UnmarshalText() empty string")
88 }
89
90 func TestMarshalString(t *testing.T) {
91 str := StringFrom("test")
92 data, err := json.Marshal(str)
93 maybePanic(err)
94 assertJSONEquals(t, data, `"test"`, "non-empty json marshal")
95 data, err = str.MarshalText()
96 maybePanic(err)
97 assertJSONEquals(t, data, "test", "non-empty text marshal")
98
99 // empty values should be encoded as an empty string
100 zero := StringFrom("")
101 data, err = json.Marshal(zero)
102 maybePanic(err)
103 assertJSONEquals(t, data, `""`, "empty json marshal")
104 data, err = zero.MarshalText()
105 maybePanic(err)
106 assertJSONEquals(t, data, "", "string marshal text")
107
108 null := StringFromPtr(nil)
109 data, err = json.Marshal(null)
110 maybePanic(err)
111 assertJSONEquals(t, data, `null`, "null json marshal")
112 data, err = null.MarshalText()
113 maybePanic(err)
114 assertJSONEquals(t, data, "", "string marshal text")
115 }
116
117 // Tests omitempty... broken until Go 1.4
118 // func TestMarshalStringInStruct(t *testing.T) {
119 // obj := stringInStruct{Test: StringFrom("")}
120 // data, err := json.Marshal(obj)
121 // maybePanic(err)
122 // assertJSONEquals(t, data, `{}`, "null string in struct")
123 // }
124
125 func TestStringPointer(t *testing.T) {
126 str := StringFrom("test")
127 ptr := str.Ptr()
128 if *ptr != "test" {
129 t.Errorf("bad %s string: %#v ≠ %s\n", "pointer", ptr, "test")
130 }
131
132 null := NewString("", false)
133 ptr = null.Ptr()
134 if ptr != nil {
135 t.Errorf("bad %s string: %#v ≠ %s\n", "nil pointer", ptr, "nil")
136 }
137 }
138
139 func TestStringIsZero(t *testing.T) {
140 str := StringFrom("test")
141 if str.IsZero() {
142 t.Errorf("IsZero() should be false")
143 }
144
145 blank := StringFrom("")
146 if blank.IsZero() {
147 t.Errorf("IsZero() should be false")
148 }
149
150 empty := NewString("", true)
151 if empty.IsZero() {
152 t.Errorf("IsZero() should be false")
153 }
154
155 null := StringFromPtr(nil)
156 if !null.IsZero() {
157 t.Errorf("IsZero() should be true")
158 }
159 }
160
161 func TestStringSetValid(t *testing.T) {
162 change := NewString("", false)
163 assertNullStr(t, change, "SetValid()")
164 change.SetValid("test")
165 assertStr(t, change, "SetValid()")
166 }
167
168 func TestStringScan(t *testing.T) {
169 var str String
170 err := str.Scan("test")
171 maybePanic(err)
172 assertStr(t, str, "scanned string")
173
174 var null String
175 err = null.Scan(nil)
176 maybePanic(err)
177 assertNullStr(t, null, "scanned null")
178 }
179
180 func maybePanic(err error) {
181 if err != nil {
182 panic(err)
183 }
184 }
185
186 func assertStr(t *testing.T, s String, from string) {
187 if s.String != "test" {
188 t.Errorf("bad %s string: %s ≠ %s\n", from, s.String, "test")
189 }
190 if !s.Valid {
191 t.Error(from, "is invalid, but should be valid")
192 }
193 }
194
195 func assertNullStr(t *testing.T, s String, from string) {
196 if s.Valid {
197 t.Error(from, "is valid, but should be invalid")
198 }
199 }
200
201 func assertJSONEquals(t *testing.T, data []byte, cmp string, from string) {
202 if string(data) != cmp {
203 t.Errorf("bad %s data: %s ≠ %s\n", from, data, cmp)
204 }
205 }
0 package null
1
2 import (
3 "database/sql/driver"
4 "encoding/json"
5 "fmt"
6 "reflect"
7 "time"
8 )
9
10 // Time is a nullable time.Time. It supports SQL and JSON serialization.
11 // It will marshal to null if null.
12 type Time struct {
13 Time time.Time
14 Valid bool
15 }
16
17 // Scan implements the Scanner interface.
18 func (t *Time) Scan(value interface{}) error {
19 var err error
20 switch x := value.(type) {
21 case time.Time:
22 t.Time = x
23 case nil:
24 t.Valid = false
25 return nil
26 default:
27 err = fmt.Errorf("null: cannot scan type %T into null.Time: %v", value, value)
28 }
29 t.Valid = err == nil
30 return err
31 }
32
33 // Value implements the driver Valuer interface.
34 func (t Time) Value() (driver.Value, error) {
35 if !t.Valid {
36 return nil, nil
37 }
38 return t.Time, nil
39 }
40
41 // NewTime creates a new Time.
42 func NewTime(t time.Time, valid bool) Time {
43 return Time{
44 Time: t,
45 Valid: valid,
46 }
47 }
48
49 // TimeFrom creates a new Time that will always be valid.
50 func TimeFrom(t time.Time) Time {
51 return NewTime(t, true)
52 }
53
54 // TimeFromPtr creates a new Time that will be null if t is nil.
55 func TimeFromPtr(t *time.Time) Time {
56 if t == nil {
57 return NewTime(time.Time{}, false)
58 }
59 return NewTime(*t, true)
60 }
61
62 // MarshalJSON implements json.Marshaler.
63 // It will encode null if this time is null.
64 func (t Time) MarshalJSON() ([]byte, error) {
65 if !t.Valid {
66 return []byte("null"), nil
67 }
68 return t.Time.MarshalJSON()
69 }
70
71 // UnmarshalJSON implements json.Unmarshaler.
72 // It supports string, object (e.g. pq.NullTime and friends)
73 // and null input.
74 func (t *Time) UnmarshalJSON(data []byte) error {
75 var err error
76 var v interface{}
77 if err = json.Unmarshal(data, &v); err != nil {
78 return err
79 }
80 switch x := v.(type) {
81 case string:
82 err = t.Time.UnmarshalJSON(data)
83 case map[string]interface{}:
84 ti, tiOK := x["Time"].(string)
85 valid, validOK := x["Valid"].(bool)
86 if !tiOK || !validOK {
87 return fmt.Errorf(`json: unmarshalling object into Go value of type null.Time requires key "Time" to be of type string and key "Valid" to be of type bool; found %T and %T, respectively`, x["Time"], x["Valid"])
88 }
89 err = t.Time.UnmarshalText([]byte(ti))
90 t.Valid = valid
91 return err
92 case nil:
93 t.Valid = false
94 return nil
95 default:
96 err = fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Time", reflect.TypeOf(v).Name())
97 }
98 t.Valid = err == nil
99 return err
100 }
101
102 func (t Time) MarshalText() ([]byte, error) {
103 if !t.Valid {
104 return []byte("null"), nil
105 }
106 return t.Time.MarshalText()
107 }
108
109 func (t *Time) UnmarshalText(text []byte) error {
110 str := string(text)
111 if str == "" || str == "null" {
112 t.Valid = false
113 return nil
114 }
115 if err := t.Time.UnmarshalText(text); err != nil {
116 return err
117 }
118 t.Valid = true
119 return nil
120 }
121
122 // SetValid changes this Time's value and sets it to be non-null.
123 func (t *Time) SetValid(v time.Time) {
124 t.Time = v
125 t.Valid = true
126 }
127
128 // Ptr returns a pointer to this Time's value, or a nil pointer if this Time is null.
129 func (t Time) Ptr() *time.Time {
130 if !t.Valid {
131 return nil
132 }
133 return &t.Time
134 }
0 package null
1
2 import (
3 "encoding/json"
4 "testing"
5 "time"
6 )
7
8 var (
9 timeString = "2012-12-21T21:21:21Z"
10 timeJSON = []byte(`"` + timeString + `"`)
11 nullTimeJSON = []byte(`null`)
12 timeValue, _ = time.Parse(time.RFC3339, timeString)
13 timeObject = []byte(`{"Time":"2012-12-21T21:21:21Z","Valid":true}`)
14 nullObject = []byte(`{"Time":"0001-01-01T00:00:00Z","Valid":false}`)
15 badObject = []byte(`{"hello": "world"}`)
16 )
17
18 func TestUnmarshalTimeJSON(t *testing.T) {
19 var ti Time
20 err := json.Unmarshal(timeJSON, &ti)
21 maybePanic(err)
22 assertTime(t, ti, "UnmarshalJSON() json")
23
24 var null Time
25 err = json.Unmarshal(nullTimeJSON, &null)
26 maybePanic(err)
27 assertNullTime(t, null, "null time json")
28
29 var fromObject Time
30 err = json.Unmarshal(timeObject, &fromObject)
31 maybePanic(err)
32 assertTime(t, fromObject, "time from object json")
33
34 var nullFromObj Time
35 err = json.Unmarshal(nullObject, &nullFromObj)
36 maybePanic(err)
37 assertNullTime(t, nullFromObj, "null from object json")
38
39 var invalid Time
40 err = invalid.UnmarshalJSON(invalidJSON)
41 if _, ok := err.(*json.SyntaxError); !ok {
42 t.Errorf("expected json.SyntaxError, not %T", err)
43 }
44 assertNullTime(t, invalid, "invalid from object json")
45
46 var bad Time
47 err = json.Unmarshal(badObject, &bad)
48 if err == nil {
49 t.Errorf("expected error: bad object")
50 }
51 assertNullTime(t, bad, "bad from object json")
52
53 var wrongType Time
54 err = json.Unmarshal(intJSON, &wrongType)
55 if err == nil {
56 t.Errorf("expected error: wrong type JSON")
57 }
58 assertNullTime(t, wrongType, "wrong type object json")
59 }
60
61 func TestUnmarshalTimeText(t *testing.T) {
62 ti := TimeFrom(timeValue)
63 txt, err := ti.MarshalText()
64 maybePanic(err)
65 assertJSONEquals(t, txt, timeString, "marshal text")
66
67 var unmarshal Time
68 err = unmarshal.UnmarshalText(txt)
69 maybePanic(err)
70 assertTime(t, unmarshal, "unmarshal text")
71
72 var null Time
73 err = null.UnmarshalText(nullJSON)
74 maybePanic(err)
75 assertNullTime(t, null, "unmarshal null text")
76 txt, err = null.MarshalText()
77 maybePanic(err)
78 assertJSONEquals(t, txt, string(nullJSON), "marshal null text")
79
80 var invalid Time
81 err = invalid.UnmarshalText([]byte("hello world"))
82 if err == nil {
83 t.Error("expected error")
84 }
85 assertNullTime(t, invalid, "bad string")
86 }
87
88 func TestMarshalTime(t *testing.T) {
89 ti := TimeFrom(timeValue)
90 data, err := json.Marshal(ti)
91 maybePanic(err)
92 assertJSONEquals(t, data, string(timeJSON), "non-empty json marshal")
93
94 ti.Valid = false
95 data, err = json.Marshal(ti)
96 maybePanic(err)
97 assertJSONEquals(t, data, string(nullJSON), "null json marshal")
98 }
99
100 func TestTimeFrom(t *testing.T) {
101 ti := TimeFrom(timeValue)
102 assertTime(t, ti, "TimeFrom() time.Time")
103 }
104
105 func TestTimeFromPtr(t *testing.T) {
106 ti := TimeFromPtr(&timeValue)
107 assertTime(t, ti, "TimeFromPtr() time")
108
109 null := TimeFromPtr(nil)
110 assertNullTime(t, null, "TimeFromPtr(nil)")
111 }
112
113 func TestTimeSetValid(t *testing.T) {
114 var ti time.Time
115 change := NewTime(ti, false)
116 assertNullTime(t, change, "SetValid()")
117 change.SetValid(timeValue)
118 assertTime(t, change, "SetValid()")
119 }
120
121 func TestTimePointer(t *testing.T) {
122 ti := TimeFrom(timeValue)
123 ptr := ti.Ptr()
124 if *ptr != timeValue {
125 t.Errorf("bad %s time: %#v ≠ %v\n", "pointer", ptr, timeValue)
126 }
127
128 var nt time.Time
129 null := NewTime(nt, false)
130 ptr = null.Ptr()
131 if ptr != nil {
132 t.Errorf("bad %s time: %#v ≠ %s\n", "nil pointer", ptr, "nil")
133 }
134 }
135
136 func TestTimeScanValue(t *testing.T) {
137 var ti Time
138 err := ti.Scan(timeValue)
139 maybePanic(err)
140 assertTime(t, ti, "scanned time")
141 if v, err := ti.Value(); v != timeValue || err != nil {
142 t.Error("bad value or err:", v, err)
143 }
144
145 var null Time
146 err = null.Scan(nil)
147 maybePanic(err)
148 assertNullTime(t, null, "scanned null")
149 if v, err := null.Value(); v != nil || err != nil {
150 t.Error("bad value or err:", v, err)
151 }
152
153 var wrong Time
154 err = wrong.Scan(int64(42))
155 if err == nil {
156 t.Error("expected error")
157 }
158 assertNullTime(t, wrong, "scanned wrong")
159 }
160
161 func assertTime(t *testing.T, ti Time, from string) {
162 if ti.Time != timeValue {
163 t.Errorf("bad %v time: %v ≠ %v\n", from, ti.Time, timeValue)
164 }
165 if !ti.Valid {
166 t.Error(from, "is invalid, but should be valid")
167 }
168 }
169
170 func assertNullTime(t *testing.T, ti Time, from string) {
171 if ti.Valid {
172 t.Error(from, "is valid, but should be invalid")
173 }
174 }
0 package zero
1
2 import (
3 "database/sql"
4 "encoding/json"
5 "errors"
6 "fmt"
7 "reflect"
8 )
9
10 // Bool is a nullable bool. False input is considered null.
11 // JSON marshals to false if null.
12 // Considered null to SQL unmarshaled from a false value.
13 type Bool struct {
14 sql.NullBool
15 }
16
17 // NewBool creates a new Bool
18 func NewBool(b bool, valid bool) Bool {
19 return Bool{
20 NullBool: sql.NullBool{
21 Bool: b,
22 Valid: valid,
23 },
24 }
25 }
26
27 // BoolFrom creates a new Bool that will be null if false.
28 func BoolFrom(b bool) Bool {
29 return NewBool(b, b)
30 }
31
32 // BoolFromPtr creates a new Bool that be null if b is nil.
33 func BoolFromPtr(b *bool) Bool {
34 if b == nil {
35 return NewBool(false, false)
36 }
37 return NewBool(*b, true)
38 }
39
40 // UnmarshalJSON implements json.Unmarshaler.
41 // "false" will be considered a null Bool.
42 // It also supports unmarshalling a sql.NullBool.
43 func (b *Bool) UnmarshalJSON(data []byte) error {
44 var err error
45 var v interface{}
46 if err = json.Unmarshal(data, &v); err != nil {
47 return err
48 }
49 switch x := v.(type) {
50 case bool:
51 b.Bool = x
52 case map[string]interface{}:
53 err = json.Unmarshal(data, &b.NullBool)
54 case nil:
55 b.Valid = false
56 return nil
57 default:
58 err = fmt.Errorf("json: cannot unmarshal %v into Go value of type zero.Bool", reflect.TypeOf(v).Name())
59 }
60 b.Valid = (err == nil) && b.Bool
61 return err
62 }
63
64 // UnmarshalText implements encoding.TextUnmarshaler.
65 // It will unmarshal to a null Bool if the input is a false or not a bool.
66 // It will return an error if the input is not a float, blank, or "null".
67 func (b *Bool) UnmarshalText(text []byte) error {
68 str := string(text)
69 switch str {
70 case "", "null":
71 b.Valid = false
72 return nil
73 case "true":
74 b.Bool = true
75 case "false":
76 b.Bool = false
77 default:
78 b.Valid = false
79 return errors.New("invalid input:" + str)
80 }
81 b.Valid = b.Bool
82 return nil
83 }
84
85 // MarshalJSON implements json.Marshaler.
86 // It will encode null if this Bool is null.
87 func (b Bool) MarshalJSON() ([]byte, error) {
88 if !b.Valid || !b.Bool {
89 return []byte("false"), nil
90 }
91 return []byte("true"), nil
92 }
93
94 // MarshalText implements encoding.TextMarshaler.
95 // It will encode a zero if this Bool is null.
96 func (b Bool) MarshalText() ([]byte, error) {
97 if !b.Valid || !b.Bool {
98 return []byte("false"), nil
99 }
100 return []byte("true"), nil
101 }
102
103 // SetValid changes this Bool's value and also sets it to be non-null.
104 func (b *Bool) SetValid(v bool) {
105 b.Bool = v
106 b.Valid = true
107 }
108
109 // Ptr returns a poBooler to this Bool's value, or a nil poBooler if this Bool is null.
110 func (b Bool) Ptr() *bool {
111 if !b.Valid {
112 return nil
113 }
114 return &b.Bool
115 }
116
117 // IsZero returns true for null or zero Bools, for future omitempty support (Go 1.4?)
118 func (b Bool) IsZero() bool {
119 return !b.Valid || !b.Bool
120 }
0 package zero
1
2 import (
3 "encoding/json"
4 "testing"
5 )
6
7 var (
8 boolJSON = []byte(`true`)
9 falseJSON = []byte(`false`)
10 nullBoolJSON = []byte(`{"Bool":true,"Valid":true}`)
11 )
12
13 func TestBoolFrom(t *testing.T) {
14 b := BoolFrom(true)
15 assertBool(t, b, "BoolFrom()")
16
17 zero := BoolFrom(false)
18 if zero.Valid {
19 t.Error("BoolFrom(false)", "is valid, but should be invalid")
20 }
21 }
22
23 func TestBoolFromPtr(t *testing.T) {
24 v := true
25 bptr := &v
26 b := BoolFromPtr(bptr)
27 assertBool(t, b, "BoolFromPtr()")
28
29 null := BoolFromPtr(nil)
30 assertNullBool(t, null, "BoolFromPtr(nil)")
31 }
32
33 func TestUnmarshalBool(t *testing.T) {
34 var b Bool
35 err := json.Unmarshal(boolJSON, &b)
36 maybePanic(err)
37 assertBool(t, b, "float json")
38
39 var nb Bool
40 err = json.Unmarshal(nullBoolJSON, &nb)
41 maybePanic(err)
42 assertBool(t, nb, "sql.NullBool json")
43
44 var zero Bool
45 err = json.Unmarshal(falseJSON, &zero)
46 maybePanic(err)
47 assertNullBool(t, zero, "zero json")
48
49 var null Bool
50 err = json.Unmarshal(nullJSON, &null)
51 maybePanic(err)
52 assertNullBool(t, null, "null json")
53
54 var invalid Bool
55 err = invalid.UnmarshalJSON(invalidJSON)
56 if _, ok := err.(*json.SyntaxError); !ok {
57 t.Errorf("expected json.SyntaxError, not %T: %v", err, err)
58 }
59 assertNullBool(t, invalid, "invalid json")
60
61 var badType Bool
62 err = json.Unmarshal(intJSON, &badType)
63 if err == nil {
64 panic("err should not be nil")
65 }
66 assertNullBool(t, badType, "wrong type json")
67 }
68
69 func TestTextUnmarshalBool(t *testing.T) {
70 var b Bool
71 err := b.UnmarshalText(boolJSON)
72 maybePanic(err)
73 assertBool(t, b, "UnmarshalText() bool")
74
75 var zero Bool
76 err = zero.UnmarshalText(falseJSON)
77 maybePanic(err)
78 assertNullBool(t, zero, "UnmarshalText() zero bool")
79
80 var blank Bool
81 err = blank.UnmarshalText([]byte(""))
82 maybePanic(err)
83 assertNullBool(t, blank, "UnmarshalText() empty bool")
84
85 var null Bool
86 err = null.UnmarshalText(nullJSON)
87 maybePanic(err)
88 assertNullBool(t, null, `UnmarshalText() "null"`)
89
90 var invalid Bool
91 err = invalid.UnmarshalText(invalidJSON)
92 if err == nil {
93 panic("err should not be nil")
94 }
95 }
96
97 func TestMarshalBool(t *testing.T) {
98 b := BoolFrom(true)
99 data, err := json.Marshal(b)
100 maybePanic(err)
101 assertJSONEquals(t, data, "true", "non-empty json marshal")
102
103 // invalid values should be encoded as false
104 null := NewBool(false, false)
105 data, err = json.Marshal(null)
106 maybePanic(err)
107 assertJSONEquals(t, data, "false", "null json marshal")
108 }
109
110 func TestMarshalBoolText(t *testing.T) {
111 b := BoolFrom(true)
112 data, err := b.MarshalText()
113 maybePanic(err)
114 assertJSONEquals(t, data, "true", "non-empty text marshal")
115
116 // invalid values should be encoded as zero
117 null := NewBool(false, false)
118 data, err = null.MarshalText()
119 maybePanic(err)
120 assertJSONEquals(t, data, "false", "null text marshal")
121 }
122
123 func TestBoolPointer(t *testing.T) {
124 b := BoolFrom(true)
125 ptr := b.Ptr()
126 if *ptr != true {
127 t.Errorf("bad %s bool: %#v ≠ %v\n", "pointer", ptr, true)
128 }
129
130 null := NewBool(false, false)
131 ptr = null.Ptr()
132 if ptr != nil {
133 t.Errorf("bad %s bool: %#v ≠ %s\n", "nil pointer", ptr, "nil")
134 }
135 }
136
137 func TestBoolIsZero(t *testing.T) {
138 b := BoolFrom(true)
139 if b.IsZero() {
140 t.Errorf("IsZero() should be false")
141 }
142
143 null := NewBool(false, false)
144 if !null.IsZero() {
145 t.Errorf("IsZero() should be true")
146 }
147
148 zero := NewBool(false, true)
149 if !zero.IsZero() {
150 t.Errorf("IsZero() should be true")
151 }
152 }
153
154 func TestBoolSetValid(t *testing.T) {
155 change := NewBool(false, false)
156 assertNullBool(t, change, "SetValid()")
157 change.SetValid(true)
158 assertBool(t, change, "SetValid()")
159 }
160
161 func TestBoolScan(t *testing.T) {
162 var b Bool
163 err := b.Scan(true)
164 maybePanic(err)
165 assertBool(t, b, "scanned bool")
166
167 var null Bool
168 err = null.Scan(nil)
169 maybePanic(err)
170 assertNullBool(t, null, "scanned null")
171 }
172
173 func assertBool(t *testing.T, b Bool, from string) {
174 if b.Bool != true {
175 t.Errorf("bad %s bool: %d ≠ %v\n", from, b.Bool, true)
176 }
177 if !b.Valid {
178 t.Error(from, "is invalid, but should be valid")
179 }
180 }
181
182 func assertNullBool(t *testing.T, b Bool, from string) {
183 if b.Valid {
184 t.Error(from, "is valid, but should be invalid")
185 }
186 }
0 package zero
1
2 import (
3 "database/sql"
4 "encoding/json"
5 "fmt"
6 "reflect"
7 "strconv"
8 )
9
10 // Float is a nullable float64. Zero input will be considered null.
11 // JSON marshals to zero if null.
12 // Considered null to SQL if zero.
13 type Float struct {
14 sql.NullFloat64
15 }
16
17 // NewFloat creates a new Float
18 func NewFloat(f float64, valid bool) Float {
19 return Float{
20 NullFloat64: sql.NullFloat64{
21 Float64: f,
22 Valid: valid,
23 },
24 }
25 }
26
27 // FloatFrom creates a new Float that will be null if zero.
28 func FloatFrom(f float64) Float {
29 return NewFloat(f, f != 0)
30 }
31
32 // FloatFromPtr creates a new Float that be null if f is nil.
33 func FloatFromPtr(f *float64) Float {
34 if f == nil {
35 return NewFloat(0, false)
36 }
37 return NewFloat(*f, true)
38 }
39
40 // UnmarshalJSON implements json.Unmarshaler.
41 // It supports number and null input.
42 // 0 will be considered a null Float.
43 // It also supports unmarshalling a sql.NullFloat64.
44 func (f *Float) UnmarshalJSON(data []byte) error {
45 var err error
46 var v interface{}
47 if err = json.Unmarshal(data, &v); err != nil {
48 return err
49 }
50 switch x := v.(type) {
51 case float64:
52 f.Float64 = x
53 case map[string]interface{}:
54 err = json.Unmarshal(data, &f.NullFloat64)
55 case nil:
56 f.Valid = false
57 return nil
58 default:
59 err = fmt.Errorf("json: cannot unmarshal %v into Go value of type zero.Float", reflect.TypeOf(v).Name())
60 }
61 f.Valid = (err == nil) && (f.Float64 != 0)
62 return err
63 }
64
65 // UnmarshalText implements encoding.TextUnmarshaler.
66 // It will unmarshal to a null Float if the input is a blank, zero, or not a float.
67 // It will return an error if the input is not a float, blank, or "null".
68 func (f *Float) UnmarshalText(text []byte) error {
69 str := string(text)
70 if str == "" || str == "null" {
71 f.Valid = false
72 return nil
73 }
74 var err error
75 f.Float64, err = strconv.ParseFloat(string(text), 64)
76 f.Valid = (err == nil) && (f.Float64 != 0)
77 return err
78 }
79
80 // MarshalJSON implements json.Marshaler.
81 // It will encode null if this Float is null.
82 func (f Float) MarshalJSON() ([]byte, error) {
83 n := f.Float64
84 if !f.Valid {
85 n = 0
86 }
87 return []byte(strconv.FormatFloat(n, 'f', -1, 64)), nil
88 }
89
90 // MarshalText implements encoding.TextMarshaler.
91 // It will encode a zero if this Float is null.
92 func (f Float) MarshalText() ([]byte, error) {
93 n := f.Float64
94 if !f.Valid {
95 n = 0
96 }
97 return []byte(strconv.FormatFloat(n, 'f', -1, 64)), nil
98 }
99
100 // SetValid changes this Float's value and also sets it to be non-null.
101 func (f *Float) SetValid(v float64) {
102 f.Float64 = v
103 f.Valid = true
104 }
105
106 // Ptr returns a poFloater to this Float's value, or a nil poFloater if this Float is null.
107 func (f Float) Ptr() *float64 {
108 if !f.Valid {
109 return nil
110 }
111 return &f.Float64
112 }
113
114 // IsZero returns true for null or zero Floats, for future omitempty support (Go 1.4?)
115 func (f Float) IsZero() bool {
116 return !f.Valid || f.Float64 == 0
117 }
0 package zero
1
2 import (
3 "encoding/json"
4 "testing"
5 )
6
7 var (
8 floatJSON = []byte(`1.2345`)
9 nullFloatJSON = []byte(`{"Float64":1.2345,"Valid":true}`)
10 )
11
12 func TestFloatFrom(t *testing.T) {
13 f := FloatFrom(1.2345)
14 assertFloat(t, f, "FloatFrom()")
15
16 zero := FloatFrom(0)
17 if zero.Valid {
18 t.Error("FloatFrom(0)", "is valid, but should be invalid")
19 }
20 }
21
22 func TestFloatFromPtr(t *testing.T) {
23 n := float64(1.2345)
24 iptr := &n
25 f := FloatFromPtr(iptr)
26 assertFloat(t, f, "FloatFromPtr()")
27
28 null := FloatFromPtr(nil)
29 assertNullFloat(t, null, "FloatFromPtr(nil)")
30 }
31
32 func TestUnmarshalFloat(t *testing.T) {
33 var f Float
34 err := json.Unmarshal(floatJSON, &f)
35 maybePanic(err)
36 assertFloat(t, f, "float json")
37
38 var nf Float
39 err = json.Unmarshal(nullFloatJSON, &nf)
40 maybePanic(err)
41 assertFloat(t, nf, "sql.NullFloat64 json")
42
43 var zero Float
44 err = json.Unmarshal(zeroJSON, &zero)
45 maybePanic(err)
46 assertNullFloat(t, zero, "zero json")
47
48 var null Float
49 err = json.Unmarshal(nullJSON, &null)
50 maybePanic(err)
51 assertNullFloat(t, null, "null json")
52
53 var badType Float
54 err = json.Unmarshal(boolJSON, &badType)
55 if err == nil {
56 panic("err should not be nil")
57 }
58 assertNullFloat(t, badType, "wrong type json")
59
60 var invalid Float
61 err = invalid.UnmarshalJSON(invalidJSON)
62 if _, ok := err.(*json.SyntaxError); !ok {
63 t.Errorf("expected json.SyntaxError, not %T", err)
64 }
65 assertNullFloat(t, invalid, "invalid json")
66 }
67
68 func TestTextUnmarshalFloat(t *testing.T) {
69 var f Float
70 err := f.UnmarshalText([]byte("1.2345"))
71 maybePanic(err)
72 assertFloat(t, f, "UnmarshalText() float")
73
74 var zero Float
75 err = zero.UnmarshalText([]byte("0"))
76 maybePanic(err)
77 assertNullFloat(t, zero, "UnmarshalText() zero float")
78
79 var blank Float
80 err = blank.UnmarshalText([]byte(""))
81 maybePanic(err)
82 assertNullFloat(t, blank, "UnmarshalText() empty float")
83
84 var null Float
85 err = null.UnmarshalText([]byte("null"))
86 maybePanic(err)
87 assertNullFloat(t, null, `UnmarshalText() "null"`)
88 }
89
90 func TestMarshalFloat(t *testing.T) {
91 f := FloatFrom(1.2345)
92 data, err := json.Marshal(f)
93 maybePanic(err)
94 assertJSONEquals(t, data, "1.2345", "non-empty json marshal")
95
96 // invalid values should be encoded as 0
97 null := NewFloat(0, false)
98 data, err = json.Marshal(null)
99 maybePanic(err)
100 assertJSONEquals(t, data, "0", "null json marshal")
101 }
102
103 func TestMarshalFloatText(t *testing.T) {
104 f := FloatFrom(1.2345)
105 data, err := f.MarshalText()
106 maybePanic(err)
107 assertJSONEquals(t, data, "1.2345", "non-empty text marshal")
108
109 // invalid values should be encoded as zero
110 null := NewFloat(0, false)
111 data, err = null.MarshalText()
112 maybePanic(err)
113 assertJSONEquals(t, data, "0", "null text marshal")
114 }
115
116 func TestFloatPointer(t *testing.T) {
117 f := FloatFrom(1.2345)
118 ptr := f.Ptr()
119 if *ptr != 1.2345 {
120 t.Errorf("bad %s Float: %#v ≠ %v\n", "pointer", ptr, 1.2345)
121 }
122
123 null := NewFloat(0, false)
124 ptr = null.Ptr()
125 if ptr != nil {
126 t.Errorf("bad %s Float: %#v ≠ %s\n", "nil pointer", ptr, "nil")
127 }
128 }
129
130 func TestFloatIsZero(t *testing.T) {
131 f := FloatFrom(1.2345)
132 if f.IsZero() {
133 t.Errorf("IsZero() should be false")
134 }
135
136 null := NewFloat(0, false)
137 if !null.IsZero() {
138 t.Errorf("IsZero() should be true")
139 }
140
141 zero := NewFloat(0, true)
142 if !zero.IsZero() {
143 t.Errorf("IsZero() should be true")
144 }
145 }
146
147 func TestFloatSetValid(t *testing.T) {
148 change := NewFloat(0, false)
149 assertNullFloat(t, change, "SetValid()")
150 change.SetValid(1.2345)
151 assertFloat(t, change, "SetValid()")
152 }
153
154 func TestFloatScan(t *testing.T) {
155 var f Float
156 err := f.Scan(1.2345)
157 maybePanic(err)
158 assertFloat(t, f, "scanned float")
159
160 var null Float
161 err = null.Scan(nil)
162 maybePanic(err)
163 assertNullFloat(t, null, "scanned null")
164 }
165
166 func assertFloat(t *testing.T, f Float, from string) {
167 if f.Float64 != 1.2345 {
168 t.Errorf("bad %s float: %f ≠ %f\n", from, f.Float64, 1.2345)
169 }
170 if !f.Valid {
171 t.Error(from, "is invalid, but should be valid")
172 }
173 }
174
175 func assertNullFloat(t *testing.T, f Float, from string) {
176 if f.Valid {
177 t.Error(from, "is valid, but should be invalid")
178 }
179 }
0 package zero
1
2 import (
3 "database/sql"
4 "encoding/json"
5 "fmt"
6 "reflect"
7 "strconv"
8 )
9
10 // Int is a nullable int64.
11 // JSON marshals to zero if null.
12 // Considered null to SQL if zero.
13 type Int struct {
14 sql.NullInt64
15 }
16
17 // NewInt creates a new Int
18 func NewInt(i int64, valid bool) Int {
19 return Int{
20 NullInt64: sql.NullInt64{
21 Int64: i,
22 Valid: valid,
23 },
24 }
25 }
26
27 // IntFrom creates a new Int that will be null if zero.
28 func IntFrom(i int64) Int {
29 return NewInt(i, i != 0)
30 }
31
32 // IntFromPtr creates a new Int that be null if i is nil.
33 func IntFromPtr(i *int64) Int {
34 if i == nil {
35 return NewInt(0, false)
36 }
37 n := NewInt(*i, true)
38 return n
39 }
40
41 // UnmarshalJSON implements json.Unmarshaler.
42 // It supports number and null input.
43 // 0 will be considered a null Int.
44 // It also supports unmarshalling a sql.NullInt64.
45 func (i *Int) UnmarshalJSON(data []byte) error {
46 var err error
47 var v interface{}
48 if err = json.Unmarshal(data, &v); err != nil {
49 return err
50 }
51 switch v.(type) {
52 case float64:
53 // Unmarshal again, directly to int64, to avoid intermediate float64
54 err = json.Unmarshal(data, &i.Int64)
55 case map[string]interface{}:
56 err = json.Unmarshal(data, &i.NullInt64)
57 case nil:
58 i.Valid = false
59 return nil
60 default:
61 err = fmt.Errorf("json: cannot unmarshal %v into Go value of type zero.Int", reflect.TypeOf(v).Name())
62 }
63 i.Valid = (err == nil) && (i.Int64 != 0)
64 return err
65 }
66
67 // UnmarshalText implements encoding.TextUnmarshaler.
68 // It will unmarshal to a null Int if the input is a blank, zero, or not an integer.
69 // It will return an error if the input is not an integer, blank, or "null".
70 func (i *Int) UnmarshalText(text []byte) error {
71 str := string(text)
72 if str == "" || str == "null" {
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) && (i.Int64 != 0)
79 return err
80 }
81
82 // MarshalJSON implements json.Marshaler.
83 // It will encode 0 if this Int is null.
84 func (i Int) MarshalJSON() ([]byte, error) {
85 n := i.Int64
86 if !i.Valid {
87 n = 0
88 }
89 return []byte(strconv.FormatInt(n, 10)), nil
90 }
91
92 // MarshalText implements encoding.TextMarshaler.
93 // It will encode a zero if this Int is null.
94 func (i Int) MarshalText() ([]byte, error) {
95 n := i.Int64
96 if !i.Valid {
97 n = 0
98 }
99 return []byte(strconv.FormatInt(n, 10)), nil
100 }
101
102 // SetValid changes this Int's value and also sets it to be non-null.
103 func (i *Int) SetValid(n int64) {
104 i.Int64 = n
105 i.Valid = true
106 }
107
108 // Ptr returns a pointer to this Int's value, or a nil pointer if this Int is null.
109 func (i Int) Ptr() *int64 {
110 if !i.Valid {
111 return nil
112 }
113 return &i.Int64
114 }
115
116 // IsZero returns true for null or zero Ints, for future omitempty support (Go 1.4?)
117 func (i Int) IsZero() bool {
118 return !i.Valid || i.Int64 == 0
119 }
0 package zero
1
2 import (
3 "encoding/json"
4 "math"
5 "strconv"
6 "testing"
7 )
8
9 var (
10 intJSON = []byte(`12345`)
11 nullIntJSON = []byte(`{"Int64":12345,"Valid":true}`)
12 zeroJSON = []byte(`0`)
13 )
14
15 func TestIntFrom(t *testing.T) {
16 i := IntFrom(12345)
17 assertInt(t, i, "IntFrom()")
18
19 zero := IntFrom(0)
20 if zero.Valid {
21 t.Error("IntFrom(0)", "is valid, but should be invalid")
22 }
23 }
24
25 func TestIntFromPtr(t *testing.T) {
26 n := int64(12345)
27 iptr := &n
28 i := IntFromPtr(iptr)
29 assertInt(t, i, "IntFromPtr()")
30
31 null := IntFromPtr(nil)
32 assertNullInt(t, null, "IntFromPtr(nil)")
33 }
34
35 func TestUnmarshalInt(t *testing.T) {
36 var i Int
37 err := json.Unmarshal(intJSON, &i)
38 maybePanic(err)
39 assertInt(t, i, "int json")
40
41 var ni Int
42 err = json.Unmarshal(nullIntJSON, &ni)
43 maybePanic(err)
44 assertInt(t, ni, "sql.NullInt64 json")
45
46 var zero Int
47 err = json.Unmarshal(zeroJSON, &zero)
48 maybePanic(err)
49 assertNullInt(t, zero, "zero json")
50
51 var null Int
52 err = json.Unmarshal(nullJSON, &null)
53 maybePanic(err)
54 assertNullInt(t, null, "null json")
55
56 var badType Int
57 err = json.Unmarshal(boolJSON, &badType)
58 if err == nil {
59 panic("err should not be nil")
60 }
61 assertNullInt(t, badType, "wrong type json")
62
63 var invalid Int
64 err = invalid.UnmarshalJSON(invalidJSON)
65 if _, ok := err.(*json.SyntaxError); !ok {
66 t.Errorf("expected json.SyntaxError, not %T", err)
67 }
68 assertNullInt(t, invalid, "invalid json")
69 }
70
71 func TestUnmarshalNonIntegerNumber(t *testing.T) {
72 var i Int
73 err := json.Unmarshal(floatJSON, &i)
74 if err == nil {
75 panic("err should be present; non-integer number coerced to int")
76 }
77 }
78
79 func TestUnmarshalInt64Overflow(t *testing.T) {
80 int64Overflow := uint64(math.MaxInt64)
81
82 // Max int64 should decode successfully
83 var i Int
84 err := json.Unmarshal([]byte(strconv.FormatUint(int64Overflow, 10)), &i)
85 maybePanic(err)
86
87 // Attempt to overflow
88 int64Overflow++
89 err = json.Unmarshal([]byte(strconv.FormatUint(int64Overflow, 10)), &i)
90 if err == nil {
91 panic("err should be present; decoded value overflows int64")
92 }
93 }
94
95 func TestTextUnmarshalInt(t *testing.T) {
96 var i Int
97 err := i.UnmarshalText([]byte("12345"))
98 maybePanic(err)
99 assertInt(t, i, "UnmarshalText() int")
100
101 var zero Int
102 err = zero.UnmarshalText([]byte("0"))
103 maybePanic(err)
104 assertNullInt(t, zero, "UnmarshalText() zero int")
105
106 var blank Int
107 err = blank.UnmarshalText([]byte(""))
108 maybePanic(err)
109 assertNullInt(t, blank, "UnmarshalText() empty int")
110
111 var null Int
112 err = null.UnmarshalText([]byte("null"))
113 maybePanic(err)
114 assertNullInt(t, null, `UnmarshalText() "null"`)
115 }
116
117 func TestMarshalInt(t *testing.T) {
118 i := IntFrom(12345)
119 data, err := json.Marshal(i)
120 maybePanic(err)
121 assertJSONEquals(t, data, "12345", "non-empty json marshal")
122
123 // invalid values should be encoded as 0
124 null := NewInt(0, false)
125 data, err = json.Marshal(null)
126 maybePanic(err)
127 assertJSONEquals(t, data, "0", "null json marshal")
128 }
129
130 func TestMarshalIntText(t *testing.T) {
131 i := IntFrom(12345)
132 data, err := i.MarshalText()
133 maybePanic(err)
134 assertJSONEquals(t, data, "12345", "non-empty text marshal")
135
136 // invalid values should be encoded as zero
137 null := NewInt(0, false)
138 data, err = null.MarshalText()
139 maybePanic(err)
140 assertJSONEquals(t, data, "0", "null text marshal")
141 }
142
143 func TestIntPointer(t *testing.T) {
144 i := IntFrom(12345)
145 ptr := i.Ptr()
146 if *ptr != 12345 {
147 t.Errorf("bad %s int: %#v ≠ %d\n", "pointer", ptr, 12345)
148 }
149
150 null := NewInt(0, false)
151 ptr = null.Ptr()
152 if ptr != nil {
153 t.Errorf("bad %s int: %#v ≠ %s\n", "nil pointer", ptr, "nil")
154 }
155 }
156
157 func TestIntIsZero(t *testing.T) {
158 i := IntFrom(12345)
159 if i.IsZero() {
160 t.Errorf("IsZero() should be false")
161 }
162
163 null := NewInt(0, false)
164 if !null.IsZero() {
165 t.Errorf("IsZero() should be true")
166 }
167
168 zero := NewInt(0, true)
169 if !zero.IsZero() {
170 t.Errorf("IsZero() should be true")
171 }
172 }
173
174 func TestIntScan(t *testing.T) {
175 var i Int
176 err := i.Scan(12345)
177 maybePanic(err)
178 assertInt(t, i, "scanned int")
179
180 var null Int
181 err = null.Scan(nil)
182 maybePanic(err)
183 assertNullInt(t, null, "scanned null")
184 }
185
186 func TestIntSetValid(t *testing.T) {
187 change := NewInt(0, false)
188 assertNullInt(t, change, "SetValid()")
189 change.SetValid(12345)
190 assertInt(t, change, "SetValid()")
191 }
192
193 func assertInt(t *testing.T, i Int, from string) {
194 if i.Int64 != 12345 {
195 t.Errorf("bad %s int: %d ≠ %d\n", from, i.Int64, 12345)
196 }
197 if !i.Valid {
198 t.Error(from, "is invalid, but should be valid")
199 }
200 }
201
202 func assertNullInt(t *testing.T, i Int, from string) {
203 if i.Valid {
204 t.Error(from, "is valid, but should be invalid")
205 }
206 }
0 // Package zero contains SQL types that consider zero input and null input to be equivalent
1 // with convenient support for JSON and text marshaling.
2 // Types in this package will JSON marshal to their zero value, even if null.
3 // Use the null parent package if you don't want this.
4 package zero
5
6 import (
7 "database/sql"
8 "encoding/json"
9 "fmt"
10 "reflect"
11 )
12
13 // String is a nullable string.
14 // JSON marshals to a blank string if null.
15 // Considered null to SQL if zero.
16 type String struct {
17 sql.NullString
18 }
19
20 // NewString creates a new String
21 func NewString(s string, valid bool) String {
22 return String{
23 NullString: sql.NullString{
24 String: s,
25 Valid: valid,
26 },
27 }
28 }
29
30 // StringFrom creates a new String that will be null if s is blank.
31 func StringFrom(s string) String {
32 return NewString(s, s != "")
33 }
34
35 // StringFromPtr creates a new String that be null if s is nil or blank.
36 // It will make s point to the String's value.
37 func StringFromPtr(s *string) String {
38 if s == nil {
39 return NewString("", false)
40 }
41 return NewString(*s, *s != "")
42 }
43
44 // UnmarshalJSON implements json.Unmarshaler.
45 // It supports string and null input. Blank string input produces a null String.
46 // It also supports unmarshalling a sql.NullString.
47 func (s *String) UnmarshalJSON(data []byte) error {
48 var err error
49 var v interface{}
50 if err = json.Unmarshal(data, &v); err != nil {
51 return err
52 }
53 switch x := v.(type) {
54 case string:
55 s.String = x
56 case map[string]interface{}:
57 err = json.Unmarshal(data, &s.NullString)
58 case nil:
59 s.Valid = false
60 return nil
61 default:
62 err = fmt.Errorf("json: cannot unmarshal %v into Go value of type zero.String", reflect.TypeOf(v).Name())
63 }
64 s.Valid = (err == nil) && (s.String != "")
65 return err
66 }
67
68 // MarshalText implements encoding.TextMarshaler.
69 // It will encode a blank string when this String is null.
70 func (s String) MarshalText() ([]byte, error) {
71 if !s.Valid {
72 return []byte{}, nil
73 }
74 return []byte(s.String), nil
75 }
76
77 // UnmarshalText implements encoding.TextUnmarshaler.
78 // It will unmarshal to a null String if the input is a blank string.
79 func (s *String) UnmarshalText(text []byte) error {
80 s.String = string(text)
81 s.Valid = s.String != ""
82 return nil
83 }
84
85 // SetValid changes this String's value and also sets it to be non-null.
86 func (s *String) SetValid(v string) {
87 s.String = v
88 s.Valid = true
89 }
90
91 // Ptr returns a pointer to this String's value, or a nil pointer if this String is null.
92 func (s String) Ptr() *string {
93 if !s.Valid {
94 return nil
95 }
96 return &s.String
97 }
98
99 // IsZero returns true for null or empty strings, for potential future omitempty support.
100 func (s String) IsZero() bool {
101 return !s.Valid || s.String == ""
102 }
0 package zero
1
2 import (
3 "encoding/json"
4 "testing"
5 )
6
7 var (
8 stringJSON = []byte(`"test"`)
9 blankStringJSON = []byte(`""`)
10 nullStringJSON = []byte(`{"String":"test","Valid":true}`)
11
12 nullJSON = []byte(`null`)
13 invalidJSON = []byte(`:)`)
14 )
15
16 type stringInStruct struct {
17 Test String `json:"test,omitempty"`
18 }
19
20 func TestStringFrom(t *testing.T) {
21 str := StringFrom("test")
22 assertStr(t, str, "StringFrom() string")
23
24 null := StringFrom("")
25 assertNullStr(t, null, "StringFrom() empty string")
26 }
27
28 func TestUnmarshalString(t *testing.T) {
29 var str String
30 err := json.Unmarshal(stringJSON, &str)
31 maybePanic(err)
32 assertStr(t, str, "string json")
33
34 var ns String
35 err = json.Unmarshal(nullStringJSON, &ns)
36 maybePanic(err)
37 assertStr(t, ns, "sql.NullString json")
38
39 var blank String
40 err = json.Unmarshal(blankStringJSON, &blank)
41 maybePanic(err)
42 assertNullStr(t, blank, "blank string json")
43
44 var null String
45 err = json.Unmarshal(nullJSON, &null)
46 maybePanic(err)
47 assertNullStr(t, null, "null json")
48
49 var badType String
50 err = json.Unmarshal(boolJSON, &badType)
51 if err == nil {
52 panic("err should not be nil")
53 }
54 assertNullStr(t, badType, "wrong type json")
55
56 var invalid String
57 err = invalid.UnmarshalJSON(invalidJSON)
58 if _, ok := err.(*json.SyntaxError); !ok {
59 t.Errorf("expected json.SyntaxError, not %T", err)
60 }
61 assertNullStr(t, invalid, "invalid json")
62 }
63
64 func TestTextUnmarshalString(t *testing.T) {
65 var str String
66 err := str.UnmarshalText([]byte("test"))
67 maybePanic(err)
68 assertStr(t, str, "UnmarshalText() string")
69
70 var null String
71 err = null.UnmarshalText([]byte(""))
72 maybePanic(err)
73 assertNullStr(t, null, "UnmarshalText() empty string")
74 }
75
76 func TestMarshalString(t *testing.T) {
77 str := StringFrom("test")
78 data, err := json.Marshal(str)
79 maybePanic(err)
80 assertJSONEquals(t, data, `"test"`, "non-empty json marshal")
81
82 // invalid values should be encoded as an empty string
83 null := StringFrom("")
84 data, err = json.Marshal(null)
85 maybePanic(err)
86 assertJSONEquals(t, data, `""`, "empty json marshal")
87 }
88
89 // Tests omitempty... broken until Go 1.4
90 // func TestMarshalStringInStruct(t *testing.T) {
91 // obj := stringInStruct{Test: StringFrom("")}
92 // data, err := json.Marshal(obj)
93 // maybePanic(err)
94 // assertJSONEquals(t, data, `{}`, "null string in struct")
95 // }
96
97 func TestStringPointer(t *testing.T) {
98 str := StringFrom("test")
99 ptr := str.Ptr()
100 if *ptr != "test" {
101 t.Errorf("bad %s string: %#v ≠ %s\n", "pointer", ptr, "test")
102 }
103
104 null := StringFrom("")
105 ptr = null.Ptr()
106 if ptr != nil {
107 t.Errorf("bad %s string: %#v ≠ %s\n", "nil pointer", ptr, "nil")
108 }
109 }
110
111 func TestStringFromPointer(t *testing.T) {
112 test := "test"
113 testptr := &test
114 str := StringFromPtr(testptr)
115 assertStr(t, str, "StringFromPtr()")
116
117 testptr = nil
118 null := StringFromPtr(testptr)
119 assertNullStr(t, null, "StringFromPtr()")
120
121 ptr := null.Ptr()
122 if ptr != nil {
123 t.Errorf("bad %s string: %#v ≠ %s\n", "nil pointer", ptr, "nil")
124 }
125 }
126
127 func TestStringIsZero(t *testing.T) {
128 str := StringFrom("test")
129 if str.IsZero() {
130 t.Errorf("IsZero() should be false")
131 }
132
133 null := StringFrom("")
134 if !null.IsZero() {
135 t.Errorf("IsZero() should be true")
136 }
137
138 empty := NewString("", true)
139 if !empty.IsZero() {
140 t.Errorf("IsZero() should be true")
141 }
142 }
143
144 func TestStringScan(t *testing.T) {
145 var str String
146 err := str.Scan("test")
147 maybePanic(err)
148 assertStr(t, str, "scanned string")
149
150 var null String
151 err = null.Scan(nil)
152 maybePanic(err)
153 assertNullStr(t, null, "scanned null")
154 }
155
156 func TestStringSetValid(t *testing.T) {
157 change := NewString("", false)
158 assertNullStr(t, change, "SetValid()")
159 change.SetValid("test")
160 assertStr(t, change, "SetValid()")
161 }
162
163 func maybePanic(err error) {
164 if err != nil {
165 panic(err)
166 }
167 }
168
169 func assertStr(t *testing.T, s String, from string) {
170 if s.String != "test" {
171 t.Errorf("bad %s string: %s ≠ %s\n", from, s.String, "test")
172 }
173 if !s.Valid {
174 t.Error(from, "is invalid, but should be valid")
175 }
176 }
177
178 func assertNullStr(t *testing.T, s String, from string) {
179 if s.Valid {
180 t.Error(from, "is valid, but should be invalid")
181 }
182 }
183
184 func assertJSONEquals(t *testing.T, data []byte, cmp string, from string) {
185 if string(data) != cmp {
186 t.Errorf("bad %s data: %s ≠ %s\n", from, data, cmp)
187 }
188 }
0 package zero
1
2 import (
3 "database/sql/driver"
4 "encoding/json"
5 "fmt"
6 "reflect"
7 "time"
8 )
9
10 // Time is a nullable time.Time.
11 // JSON marshals to the zero value for time.Time if null.
12 // Considered to be null to SQL if zero.
13 type Time struct {
14 Time time.Time
15 Valid bool
16 }
17
18 // Scan implements Scanner interface.
19 func (t *Time) Scan(value interface{}) error {
20 var err error
21 switch x := value.(type) {
22 case time.Time:
23 t.Time = x
24 case nil:
25 t.Valid = false
26 return nil
27 default:
28 err = fmt.Errorf("null: cannot scan type %T into null.Time: %v", value, value)
29 }
30 t.Valid = err == nil
31 return err
32 }
33
34 // Value implements the driver Valuer interface.
35 func (t Time) Value() (driver.Value, error) {
36 if !t.Valid {
37 return nil, nil
38 }
39 return t.Time, nil
40 }
41
42 // NewTime creates a new Time.
43 func NewTime(t time.Time, valid bool) Time {
44 return Time{
45 Time: t,
46 Valid: valid,
47 }
48 }
49
50 // TimeFrom creates a new Time that will
51 // be null if t is the zero value.
52 func TimeFrom(t time.Time) Time {
53 return NewTime(t, !t.IsZero())
54 }
55
56 // TimeFromPtr creates a new Time that will
57 // be null if t is nil or *t is the zero value.
58 func TimeFromPtr(t *time.Time) Time {
59 if t == nil {
60 return NewTime(time.Time{}, false)
61 }
62 return TimeFrom(*t)
63 }
64
65 // MarshalJSON implements json.Marshaler.
66 // It will encode the zero value of time.Time
67 // if this time is invalid.
68 func (t Time) MarshalJSON() ([]byte, error) {
69 if !t.Valid {
70 return (time.Time{}).MarshalJSON()
71 }
72 return t.Time.MarshalJSON()
73 }
74
75 // UnmarshalJSON implements json.Unmarshaler.
76 // It supports string, object (e.g. pq.NullTime and friends)
77 // and null input.
78 func (t *Time) UnmarshalJSON(data []byte) error {
79 var err error
80 var v interface{}
81 if err = json.Unmarshal(data, &v); err != nil {
82 return err
83 }
84 switch x := v.(type) {
85 case string:
86 var ti time.Time
87 if err = ti.UnmarshalJSON(data); err != nil {
88 return err
89 }
90 *t = TimeFrom(ti)
91 return nil
92 case map[string]interface{}:
93 ti, tiOK := x["Time"].(string)
94 valid, validOK := x["Valid"].(bool)
95 if !tiOK || !validOK {
96 return fmt.Errorf(`json: unmarshalling object into Go value of type null.Time requires key "Time" to be of type string and key "Valid" to be of type bool; found %T and %T, respectively`, x["Time"], x["Valid"])
97 }
98 err = t.Time.UnmarshalText([]byte(ti))
99 t.Valid = valid
100 return err
101 case nil:
102 t.Valid = false
103 return nil
104 default:
105 return fmt.Errorf("json: cannot unmarshal %v into Go value of type null.Time", reflect.TypeOf(v).Name())
106 }
107 }
108
109 func (t Time) MarshalText() ([]byte, error) {
110 ti := t.Time
111 if !t.Valid {
112 ti = time.Time{}
113 }
114 return ti.MarshalText()
115 }
116
117 func (t *Time) UnmarshalText(text []byte) error {
118 str := string(text)
119 if str == "" || str == "null" {
120 t.Valid = false
121 return nil
122 }
123 if err := t.Time.UnmarshalText(text); err != nil {
124 return err
125 }
126 t.Valid = true
127 return nil
128 }
129
130 // SetValid changes this Time's value and
131 // sets it to be non-null.
132 func (t *Time) SetValid(v time.Time) {
133 t.Time = v
134 t.Valid = true
135 }
136
137 // Ptr returns a pointer to this Time's value,
138 // or a nil pointer if this Time is zero.
139 func (t Time) Ptr() *time.Time {
140 if !t.Valid {
141 return nil
142 }
143 return &t.Time
144 }
0 package zero
1
2 import (
3 "encoding/json"
4 "testing"
5 "time"
6 )
7
8 var (
9 timeString = "2012-12-21T21:21:21Z"
10 timeJSON = []byte(`"` + timeString + `"`)
11 zeroTimeStr = "0001-01-01T00:00:00Z"
12 zeroTimeJSON = []byte(`"0001-01-01T00:00:00Z"`)
13 blankTimeJSON = []byte(`null`)
14 timeValue, _ = time.Parse(time.RFC3339, timeString)
15 timeObject = []byte(`{"Time":"2012-12-21T21:21:21Z","Valid":true}`)
16 nullObject = []byte(`{"Time":"0001-01-01T00:00:00Z","Valid":false}`)
17 badObject = []byte(`{"hello": "world"}`)
18 )
19
20 func TestUnmarshalTimeJSON(t *testing.T) {
21 var ti Time
22 err := json.Unmarshal(timeObject, &ti)
23 maybePanic(err)
24 assertTime(t, ti, "UnmarshalJSON() json")
25
26 var blank Time
27 err = json.Unmarshal(blankTimeJSON, &blank)
28 maybePanic(err)
29 assertNullTime(t, blank, "blank time json")
30
31 var zero Time
32 err = json.Unmarshal(zeroTimeJSON, &zero)
33 maybePanic(err)
34 assertNullTime(t, zero, "zero time json")
35
36 var fromObject Time
37 err = json.Unmarshal(timeObject, &fromObject)
38 maybePanic(err)
39 assertTime(t, fromObject, "map time json")
40
41 var null Time
42 err = json.Unmarshal(nullObject, &null)
43 maybePanic(err)
44 assertNullTime(t, null, "map null time json")
45
46 var nullFromObj Time
47 err = json.Unmarshal(nullObject, &nullFromObj)
48 maybePanic(err)
49 assertNullTime(t, nullFromObj, "null from object json")
50
51 var invalid Time
52 err = invalid.UnmarshalJSON(invalidJSON)
53 if _, ok := err.(*json.SyntaxError); !ok {
54 t.Errorf("expected json.SyntaxError, not %T", err)
55 }
56 assertNullTime(t, invalid, "invalid from object json")
57
58 var bad Time
59 err = json.Unmarshal(badObject, &bad)
60 if err == nil {
61 t.Errorf("expected error: bad object")
62 }
63 assertNullTime(t, bad, "bad from object json")
64
65 var wrongType Time
66 err = json.Unmarshal(intJSON, &wrongType)
67 if err == nil {
68 t.Errorf("expected error: wrong type JSON")
69 }
70 assertNullTime(t, wrongType, "wrong type object json")
71
72 var wrongString Time
73 err = json.Unmarshal(stringJSON, &wrongString)
74 if err == nil {
75 t.Errorf("expected error: wrong string JSON")
76 }
77 assertNullTime(t, wrongString, "wrong string object json")
78 }
79
80 func TestMarshalTime(t *testing.T) {
81 ti := TimeFrom(timeValue)
82 data, err := json.Marshal(ti)
83 maybePanic(err)
84 assertJSONEquals(t, data, string(timeJSON), "non-empty json marshal")
85
86 null := TimeFromPtr(nil)
87 data, err = json.Marshal(null)
88 maybePanic(err)
89 assertJSONEquals(t, data, string(zeroTimeJSON), "empty json marshal")
90 }
91
92 func TestUnmarshalTimeText(t *testing.T) {
93 ti := TimeFrom(timeValue)
94 txt, err := ti.MarshalText()
95 maybePanic(err)
96 assertJSONEquals(t, txt, timeString, "marshal text")
97
98 var unmarshal Time
99 err = unmarshal.UnmarshalText(txt)
100 maybePanic(err)
101 assertTime(t, unmarshal, "unmarshal text")
102
103 var null Time
104 err = null.UnmarshalText(nullJSON)
105 maybePanic(err)
106 assertNullTime(t, null, "unmarshal null text")
107 txt, err = null.MarshalText()
108 maybePanic(err)
109 assertJSONEquals(t, txt, zeroTimeStr, "marshal null text")
110
111 var invalid Time
112 err = invalid.UnmarshalText([]byte("hello world"))
113 if err == nil {
114 t.Error("expected error")
115 }
116 assertNullTime(t, invalid, "bad string")
117 }
118
119 func TestTimeFrom(t *testing.T) {
120 ti := TimeFrom(timeValue)
121 assertTime(t, ti, "TimeFrom() time.Time")
122
123 var nt time.Time
124 null := TimeFrom(nt)
125 assertNullTime(t, null, "TimeFrom() empty time.Time")
126 }
127
128 func TestTimeFromPtr(t *testing.T) {
129 ti := TimeFromPtr(&timeValue)
130 assertTime(t, ti, "TimeFromPtr() time")
131
132 null := TimeFromPtr(nil)
133 assertNullTime(t, null, "TimeFromPtr(nil)")
134 }
135
136 func TestTimeSetValid(t *testing.T) {
137 var ti time.Time
138 change := TimeFrom(ti)
139 assertNullTime(t, change, "SetValid()")
140 change.SetValid(timeValue)
141 assertTime(t, change, "SetValid()")
142 }
143
144 func TestTimePointer(t *testing.T) {
145 ti := TimeFrom(timeValue)
146 ptr := ti.Ptr()
147 if *ptr != timeValue {
148 t.Errorf("bad %s time: %#v ≠ %v\n", "pointer", ptr, timeValue)
149 }
150
151 var nt time.Time
152 null := TimeFrom(nt)
153 ptr = null.Ptr()
154 if ptr != nil {
155 t.Errorf("bad %s time: %#v ≠ %s\n", "nil pointer", ptr, "nil")
156 }
157 }
158
159 func TestTimeScan(t *testing.T) {
160 var ti Time
161 err := ti.Scan(timeValue)
162 maybePanic(err)
163 assertTime(t, ti, "scanned time")
164
165 var null Time
166 err = null.Scan(nil)
167 maybePanic(err)
168 assertNullTime(t, null, "scanned null")
169
170 var wrong Time
171 err = wrong.Scan(int64(42))
172 if err == nil {
173 t.Error("expected error")
174 }
175 assertNullTime(t, wrong, "scanned wrong")
176 }
177
178 func TestTimeValue(t *testing.T) {
179 ti := TimeFrom(timeValue)
180 v, err := ti.Value()
181 maybePanic(err)
182 if ti.Time != timeValue {
183 t.Errorf("bad time.Time value: %v ≠ %v", ti.Time, timeValue)
184 }
185
186 var nt time.Time
187 zero := TimeFrom(nt)
188 v, err = zero.Value()
189 maybePanic(err)
190 if v != nil {
191 t.Errorf("bad %s time.Time value: %v ≠ %v", "zero", v, nil)
192 }
193 }
194
195 func assertTime(t *testing.T, ti Time, from string) {
196 if ti.Time != timeValue {
197 t.Errorf("bad %v time: %v ≠ %v\n", from, ti.Time, timeValue)
198 }
199 if !ti.Valid {
200 t.Error(from, "is invalid, but should be valid")
201 }
202 }
203
204 func assertNullTime(t *testing.T, ti Time, from string) {
205 if ti.Valid {
206 t.Error(from, "is valid, but should be invalid")
207 }
208 }