forked from tee8z/nullable
-
Notifications
You must be signed in to change notification settings - Fork 1
/
uint.go
152 lines (132 loc) · 2.93 KB
/
uint.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package nullable
import (
"context"
"database/sql/driver"
"encoding/json"
"strconv"
"gorm.io/gorm/clause"
"gorm.io/gorm"
"gorm.io/gorm/schema"
)
// Uint SQL type that can retrieve NULL value
type Uint struct {
realValue uint
isValid bool
}
// NewUint creates a new nullable unsigned integer
func NewUint(value *uint) Uint {
if value == nil {
return Uint{
realValue: 0,
isValid: false,
}
}
return Uint{
realValue: *value,
isValid: true,
}
}
// Get either nil or unsigned integer
func (n Uint) Get() *uint {
if !n.isValid {
return nil
}
return &n.realValue
}
// Set either nil or unsigned integer
func (n *Uint) Set(value *uint) {
n.isValid = (value != nil)
if n.isValid {
n.realValue = *value
} else {
n.realValue = 0
}
}
// MarshalJSON converts current value to JSON
func (n Uint) MarshalJSON() ([]byte, error) {
return json.Marshal(n.Get())
}
// UnmarshalJSON writes JSON to this type
func (n *Uint) UnmarshalJSON(data []byte) error {
dataString := string(data)
if len(dataString) == 0 || dataString == "null" {
n.isValid = false
n.realValue = 0
return nil
}
var parsed uint
if err := json.Unmarshal(data, &parsed); err != nil {
return err
}
n.isValid = true
n.realValue = parsed
return nil
}
// Scan implements scanner interface
func (n *Uint) Scan(value interface{}) error {
if value == nil {
n.realValue, n.isValid = 0, false
return nil
}
var scanned string
if err := convertAssign(&scanned, value); err != nil {
return err
}
radix := 10
if len(scanned) == 64 {
radix = 2
}
parsed, err := strconv.ParseUint(scanned, radix, 64)
if err != nil {
return err
}
n.realValue = uint(parsed)
n.isValid = true
return nil
}
// Value implements the driver Valuer interface.
func (n Uint) Value() (driver.Value, error) {
if !n.isValid {
return nil, nil
}
return strconv.FormatUint(uint64(n.realValue), 10), nil
}
// GormValue implements the driver Valuer interface via GORM.
func (n Uint) GormValue(ctx context.Context, db *gorm.DB) clause.Expr {
switch db.Dialector.Name() {
case "sqlite", "mysql":
// MySQL and SQLite are using Value() instead of GormValue()
value, err := n.Value()
if err != nil {
db.AddError(err)
return clause.Expr{}
}
return clause.Expr{SQL: "?", Vars: []interface{}{value}}
case "postgres":
if !n.isValid {
return clause.Expr{SQL: "?", Vars: []interface{}{nil}}
}
value := strconv.FormatUint(uint64(n.realValue), 2)
leadingZero := 64 - len(value)
for leadingZero > 0 {
value = "0" + value
leadingZero--
}
return clause.Expr{SQL: "?", Vars: []interface{}{value}}
}
return clause.Expr{}
}
// GormDataType gorm common data type
func (Uint) GormDataType() string {
return "uint_null"
}
// GormDBDataType gorm db data type
func (Uint) GormDBDataType(db *gorm.DB, field *schema.Field) string {
switch db.Dialector.Name() {
case "sqlite", "mysql":
return "BIGINT UNSIGNED"
case "postgres":
return "bit(64)"
}
return ""
}