-
Notifications
You must be signed in to change notification settings - Fork 0
/
opt.go
126 lines (103 loc) · 2.15 KB
/
opt.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
package opt
import (
"bytes"
"database/sql"
"database/sql/driver"
"encoding/json"
)
var (
_ driver.Valuer = &Optional[string]{}
_ sql.Scanner = &Optional[string]{}
_ json.Marshaler = &Optional[string]{}
_ json.Unmarshaler = &Optional[string]{}
)
func New[T any](item T) Optional[T] {
return Optional[T]{
Item: item,
isPresent: true,
isNil: false,
}
}
func Nil[T any]() Optional[T] {
return Optional[T]{
isPresent: true,
isNil: true,
}
}
type Optional[T any] struct {
Item T
isPresent bool
isNil bool
}
func (e Optional[T]) OrElse(d T) T { //nolint:ireturn
if e.Valid() {
return e.Item
}
return d
}
func (e Optional[T]) Valid() bool { return e.isPresent && !e.isNil }
func (e Optional[T]) Nil() bool { return e.isNil }
func (e Optional[T]) Present() bool { return e.isPresent }
func (e Optional[T]) MarshalJSON() ([]byte, error) {
if !e.isPresent || e.isNil {
return nullBytes, nil
}
return json.Marshal(e.Item)
}
func (e *Optional[T]) UnmarshalJSON(data []byte) error {
e.isPresent = true
if string(bytes.TrimSpace(data)) == nullString {
e.isNil = true
return nil
}
err := json.Unmarshal(data, &e.Item)
if err != nil {
e.isNil = false
e.isPresent = false
return err
}
return nil
}
var (
nullString = "null"
nullBytes = []byte(nullString)
)
// Scan implements sql.Scanner interface.
func (e *Optional[T]) Scan(src any) error {
e.isPresent = true
// tunnel to sql.Null to use sql.convertAssign
n := sql.Null[T]{}
err := n.Scan(src)
if err != nil {
e.isPresent = false
return err
}
e.isNil, e.Item = !n.Valid, n.V
return nil
}
// Value implements driver.Valuer interface.
//
// int64
// float64
// bool
// []byte
// string
// time.Time
func (e Optional[T]) Value() (driver.Value, error) {
if !e.isPresent || e.isNil {
return nil, nil //nolint:nilnil
}
// if the internal value is also a valuer, call that.
// direct type assertion does not work on generics.
var a any = e.Item
if v, isValuer := (a).(driver.Valuer); isValuer {
return v.Value()
}
return e.Item, nil
}
func (e Optional[T]) Ptr() *T {
if !e.isPresent || e.isNil {
return nil
}
return &e.Item
}