1
1
package enum
2
2
3
3
import (
4
+ "database/sql"
5
+ "database/sql/driver"
6
+ "encoding"
7
+ "encoding/json"
8
+ "fmt"
4
9
"reflect"
5
10
6
11
"golang.org/x/exp/constraints"
7
12
)
8
13
9
- // Enum represents a named Enum that is associaterd with a value . Enum values
10
- // are auto-generated starting form 0 and monotonically increasing in
14
+ // Enum represents a named Enum that is associaterd with an ID . Enum IDs
15
+ // are auto-generated starting from 0 and monotonically increasing in
11
16
// declaration order.
12
17
type Enum [T constraints.Integer ] interface {
13
18
Name () string
14
- Value () T
19
+ ID () T
20
+
21
+ // JSON support.
22
+ json.Marshaler
23
+ json.Unmarshaler
24
+
25
+ // Text support.
26
+ encoding.TextMarshaler
27
+ encoding.TextUnmarshaler
28
+
29
+ // SQL support.
30
+ driver.Valuer
31
+ sql.Scanner
15
32
16
33
// Makes sure no external package can implement this interface.
17
34
unimplementable ()
@@ -31,7 +48,7 @@ func getTypeName[T any]() string {
31
48
return tType .PkgPath () + "." + tType .Name ()
32
49
}
33
50
34
- // New returns a new Enum associated with the given name and type T.
51
+ // New returns a new Enum associated with the given name and type T.
35
52
func New [T constraints.Integer ](name string ) Enum [T ] {
36
53
typeName := getTypeName [T ]()
37
54
@@ -48,20 +65,113 @@ func New[T constraints.Integer](name string) Enum[T] {
48
65
}
49
66
50
67
// internalEnum is the only concrete implementation of the Enum interface. It
51
- // holds the name and typed value .
52
- type internalEnum [T comparable ] struct {
53
- name string
54
- value T
68
+ // holds the name and typed ID .
69
+ type internalEnum [T constraints. Integer ] struct {
70
+ name string
71
+ id T
55
72
}
56
73
57
74
// Name returns the name associated with this Enum instance.
58
75
func (e internalEnum [T ]) Name () string {
59
76
return e .name
60
77
}
61
78
62
- // Value returns the value associated with this Enum instance.
63
- func (e internalEnum [T ]) Value () T {
64
- return e .value
79
+ // ID returns the numeric ID associated with this Enum instance.
80
+ func (e internalEnum [T ]) ID () T {
81
+ return e .id
82
+ }
83
+
84
+ // MarshalJSON implements the json.Marshaler interface.
85
+ func (e internalEnum [T ]) MarshalJSON () ([]byte , error ) {
86
+ return json .Marshal (e .Name ())
87
+ }
88
+
89
+ func getIDForName [T constraints.Integer ](name string ) (T , error ) {
90
+ typeName := getTypeName [T ]()
91
+
92
+ var defaultID T
93
+
94
+ anySet , ok := setByTypeName [typeName ]
95
+ if ! ok {
96
+ return defaultID , fmt .Errorf ("no enum set associated with type %s" , typeName )
97
+ }
98
+
99
+ s := anySet .(* internalSet [T ])
100
+
101
+ id , ok := s .ids [name ]
102
+ if ! ok {
103
+ return defaultID , fmt .Errorf ("name %s could not be found in enum set for type %s" , name , typeName )
104
+ }
105
+
106
+ return T (id ), nil
107
+ }
108
+
109
+ // UnmarshalJSON implements the json.Unmarshaler interface.
110
+ func (e * internalEnum [T ]) UnmarshalJSON (data []byte ) error {
111
+ var name string
112
+ if err := json .Unmarshal (data , & name ); err != nil {
113
+ return fmt .Errorf ("source should be a string, got %s" , data )
114
+ }
115
+
116
+ id , err := getIDForName [T ](name )
117
+ if err != nil {
118
+ return err
119
+ }
120
+
121
+ e .name = name
122
+ e .id = T (id )
123
+
124
+ return nil
125
+ }
126
+
127
+ // MarshalText implements the encoding.TextMarshaler interface.
128
+ func (e internalEnum [T ]) MarshalText () ([]byte , error ) {
129
+ return []byte (e .Name ()), nil
130
+ }
131
+
132
+ // UnmarshalText implements the encoding.TextUnmarshaler interface.
133
+ func (e * internalEnum [T ]) UnmarshalText (text []byte ) error {
134
+ name := string (text )
135
+
136
+ id , err := getIDForName [T ](name )
137
+ if err != nil {
138
+ return err
139
+ }
140
+
141
+ e .name = name
142
+ e .id = T (id )
143
+
144
+ return nil
145
+ }
146
+
147
+ func (e internalEnum [T ]) Value () (driver.Value , error ) {
148
+ return e .Name (), nil
149
+ }
150
+
151
+ func (e * internalEnum [T ]) Scan (value any ) error {
152
+ if value == nil {
153
+ return nil
154
+ }
155
+
156
+ name , ok := value .(string )
157
+ if ! ok {
158
+ bytes , ok := value .([]byte )
159
+ if ! ok {
160
+ return fmt .Errorf ("value is not a byte slice" )
161
+ }
162
+
163
+ name = string (bytes [:])
164
+ }
165
+
166
+ id , err := getIDForName [T ](name )
167
+ if err != nil {
168
+ return err
169
+ }
170
+
171
+ e .name = name
172
+ e .id = T (id )
173
+
174
+ return nil
65
175
}
66
176
67
177
// unimplementanle makes sure we implement the Enum interface.
0 commit comments