-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathenum_test.go
201 lines (166 loc) · 4.62 KB
/
enum_test.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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
package enum
import (
"encoding/json"
"fmt"
"testing"
)
// Old method role enum for reference.
type OldMethodRole int
const (
OldMethodRoleUnknown OldMethodRole = iota
OldMethodRoleAdmin
OldMethodRoleUser
OldMethodRoleGuest
)
// This would be auto-generated by the enumer library.
func (r OldMethodRole) String() string {
switch r {
case OldMethodRoleAdmin:
return "admin"
case OldMethodRoleUser:
return "user"
case OldMethodRoleGuest:
return "guest"
default:
return "unknown"
}
}
// Old method permission enum for referece.
type OldMethodPermission int
const (
OldMethodPermissionUnknown OldMethodPermission = iota
OldMethodPermissionRead
OldMethodPermissionWrite
)
// This would be auto-generated by the enumer library.
func (p OldMethodPermission) String() string {
switch p {
case OldMethodPermissionRead:
return "read"
case OldMethodPermissionWrite:
return "write"
default:
return "unknown"
}
}
// New method role enum.
type Role int
// Just to allow cleaner references.
//
// Lets assume the Role type above is defined in a package called "accounts".
// Without the type below, functions that would take an enum of type Role would
// need to be written like:
//
// func DoSomethingWithRole(r enum.Role[accounts.Role]) {}
//
// With the type below, the function can be written like:
//
// func DoSomethingWithRole(accounts.RoleEnum) {}
type RoleEnum Enum[Role]
var (
UnknownRole = RoleEnum(New[Role]("Unknown")) // 0
Admin = RoleEnum(New[Role]("Admin")) // 1
User = RoleEnum(New[Role]("User")) // 2
Guest = RoleEnum(New[Role]("Guest")) // 3
)
// New method permission enum.
type Permission int
type PermissionEnum Enum[Permission] // Just to allow cleaner references.
var (
UnknownPermission = PermissionEnum(New[Permission]("Unknown")) // 0
Read = PermissionEnum(New[Permission]("Read")) // 1
Write = PermissionEnum(New[Permission]("Write")) // 2
)
func acceptsRoleOnly(t *testing.T, role RoleEnum) {
t.Log(role)
}
func acceptsRoleIDOnly(t *testing.T, id Role) {
t.Log(id)
}
func acceptsPermissionOnly(t *testing.T, permission PermissionEnum) {
t.Log(permission)
}
func acceptsPermissionIDOnly(t *testing.T, id Permission) {
t.Log(id)
}
func TestEnum(t *testing.T) {
acceptsRoleOnly(t, UnknownRole)
acceptsRoleOnly(t, Admin)
acceptsRoleOnly(t, User)
acceptsRoleOnly(t, Guest)
// acceptsRoleOnly(t, UnknownPermission) // compile error
acceptsRoleIDOnly(t, UnknownRole.ID())
acceptsRoleIDOnly(t, Admin.ID())
acceptsRoleIDOnly(t, User.ID())
acceptsRoleIDOnly(t, Guest.ID())
// acceptsRoleIDOnly(t, UnknownPermission.ID()) // compile error
acceptsPermissionOnly(t, UnknownPermission)
acceptsPermissionOnly(t, Read)
acceptsPermissionOnly(t, Write)
// acceptsPermissionOnly(t, UnknownRole) // compile error
acceptsPermissionIDOnly(t, UnknownPermission.ID())
acceptsPermissionIDOnly(t, Read.ID())
acceptsPermissionIDOnly(t, Write.ID())
// acceptsPermissionIDOnly(t, UnknownRole.ID()) // compile error
}
func TestEnum_Overflow(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Errorf("expected panic, got normal execution")
}
}()
type int8Enum int8
// We can only have 128 int8 enums.
for i := 0; i <= 128; i++ {
New[int8Enum](fmt.Sprintf("Enum%d", i))
}
}
func TestEnum_MarshalUnmarshal(t *testing.T) {
data, err := json.Marshal(Guest)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
var newGuest RoleEnum
err = json.Unmarshal(data, &newGuest)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if newGuest != Guest {
t.Errorf("expected internalEnum pointer %p, got %p", Guest.internalEnum, newGuest.internalEnum)
}
if newGuest.ID() != Guest.ID() {
t.Errorf("expected ID %d, got %d", Guest.ID(), newGuest.ID())
}
if newGuest.String() != Guest.String() {
t.Errorf("expected String %s, got %s", Guest.String(), newGuest.String())
}
}
func TestEnum_Switch(t *testing.T) {
// Unsing role values, which should be the common case.
role := Admin
switch role {
case UnknownRole:
t.Errorf("expected %s, got %s", role, UnknownRole)
case Admin:
// Just do not error out. This is what we want.
case User:
t.Errorf("expected %s, got %s", role, User)
case Guest:
t.Errorf("expected %s, got %s", role, Guest)
default:
t.Errorf("expected %s, got something else", role)
}
// Using IDs.
switch roleID := role.ID(); roleID {
case Admin.ID():
// Just do not error out. This is what we want.
default:
t.Errorf("expected %d, got %d", Admin.ID(), roleID)
}
}
func TestEnum_EnumsForType(t *testing.T) {
enums := EnumsByType[Role]()
if len(enums) != 4 {
t.Errorf("expected 4, got %d", len(enums))
}
}