-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtype.go
More file actions
141 lines (123 loc) · 2.58 KB
/
Copy pathtype.go
File metadata and controls
141 lines (123 loc) · 2.58 KB
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
//go:build go1.18
// Copyright (c) 2020 gomoni contributors
// Under BSD 3-Clause license, see LICENSE file
package null
import (
"bytes"
"encoding/json"
"errors"
"fmt"
)
type state uint8
const (
isUndefined state = 0
isNull state = 1
isDefined state = 2
)
var (
ErrUndefined = errors.New("undefined")
ErrNull = errors.New("null")
null = []byte(`null`)
)
// Type is null and undefined aware type wrapper for parsing JSON. Unlike Go
// types it can distinguish cases "key": null and key is not present in JSON.
type Type[T any] struct {
value T
state state
}
// Value returns value, ErrUndefined or ErrNull
func (t Type[T]) Value() (T, error) {
switch t.state {
case isUndefined:
var zero T
return zero, ErrUndefined
case isNull:
var zero T
return zero, ErrNull
default:
return t.value, nil
}
}
// New creates new wrapped value
func New[T any](x T) Type[T] {
return Type[T]{
value: x,
state: isDefined,
}
}
// NewNull creates new null value.
func NewNullType[T any]() Type[T] {
return Type[T]{
state: isNull,
}
}
// NewNull creates new undefined value.
func NewUndefined[T any]() Type[T] {
return Type[T]{
state: isUndefined,
}
}
// UnmarshalJSON implements json.Unmarshaler
func (t *Type[T]) UnmarshalJSON(data []byte) error {
var zero T
t.value = zero
if bytes.Equal(data, null) {
t.state = isNull
return nil
}
if err := json.Unmarshal(data, &t.value); err != nil {
return err
}
t.state = isDefined
return nil
}
// MarshalJSON implements json.Marshaler. Marshaling undefined values results in error.
func (t Type[T]) MarshalJSON() ([]byte, error) {
switch t.state {
case isUndefined:
return nil, ErrUndefined
case isNull:
return null, nil
default:
return json.Marshal(t.value)
}
}
// fmt interfaces
// String implements fmt.GoStringer for %#v format string
func (t Type[T]) GoString() string {
typ := fmt.Sprintf("%T", t.value)
switch t.state {
case isUndefined:
return fmt.Sprintf("NewUndefined[%s]()", typ)
case isNull:
return fmt.Sprintf("NewNull[%s]()", typ)
default:
return fmt.Sprintf("New[%s](%#v)", typ, t.value)
}
}
func (t Type[T]) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
if s.Flag('#') {
fmt.Fprintf(s, t.GoString())
return
}
// ignoring s.Flag('+') as we don't want to expose internals
fallthrough
case 's':
fallthrough
case 'q':
switch t.state {
case isUndefined:
fmt.Fprintf(s, "%s", `undefined`)
case isNull:
fmt.Fprintf(s, "%s", `null`)
default:
format := "%v"
if verb == 'q' {
format = "%q"
}
fmt.Fprintf(s, format, t.value)
}
}
}