-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathenum.go
152 lines (117 loc) · 2.97 KB
/
enum.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 revisor
import (
"errors"
"fmt"
"slices"
"strings"
)
type Enum struct {
Declare string `json:"declare,omitempty"`
Match string `json:"match,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Values map[string]EnumConstraint `json:"values"`
}
type EnumConstraint struct {
Forbidden bool `json:"forbidden,omitempty"`
Deprecated *Deprecation `json:"deprecated,omitempty"`
Description string `json:"description,omitempty"`
}
type mergedEnum struct {
Values map[string][]EnumConstraint
Allowed []string
}
func mergedEnumAllowedValues(m *mergedEnum) []string {
var vals []string
valueFn := func(v string, cs []EnumConstraint) (string, bool) {
var deprecated bool
for _, c := range cs {
if c.Forbidden {
return "", false
}
deprecated = deprecated || c.Deprecated != nil
}
if deprecated {
return fmt.Sprintf("%q (deprecated)", v), true
}
return fmt.Sprintf("%q", v), true
}
for v, cs := range m.Values {
s, ok := valueFn(v, cs)
if ok {
vals = append(vals, s)
}
}
slices.Sort(vals)
return vals
}
type enumSet struct {
extensions []Enum
enums map[string]*mergedEnum
}
func newEnumSet() *enumSet {
return &enumSet{
enums: make(map[string]*mergedEnum),
}
}
func (s *enumSet) Register(e Enum) error {
if e.Declare != "" && e.Match != "" {
return fmt.Errorf(
"the enum %q cannot both declare and match an enum",
e.Declare)
}
if e.Declare == "" && e.Match == "" {
return errors.New("an enum must declare or match an existing enum")
}
if e.Match != "" {
s.extensions = append(s.extensions, e)
return nil
}
_, declared := s.enums[e.Declare]
if declared {
return errors.New("the enum %q has already been declared")
}
m := mergedEnum{
Values: make(map[string][]EnumConstraint, len(e.Values)),
}
for k, c := range e.Values {
m.Values[k] = []EnumConstraint{c}
}
s.enums[e.Declare] = &m
return nil
}
func (s *enumSet) Resolve() error {
for _, e := range s.extensions {
m, declared := s.enums[e.Match]
if !declared {
return fmt.Errorf("the enum %q hasn't been declared and cannot be matched", e.Match)
}
for k, c := range e.Values {
m.Values[k] = append(m.Values[k], c)
}
}
for _, m := range s.enums {
m.Allowed = mergedEnumAllowedValues(m)
}
return nil
}
func (s *enumSet) ValidValue(enum string, value string) (*Deprecation, error) {
m, declared := s.enums[enum]
if !declared {
return nil, fmt.Errorf("unknown enum %q", enum)
}
constraints, hasValue := m.Values[value]
if !hasValue {
return nil, fmt.Errorf("must be one of: %s", strings.Join(m.Allowed, ", "))
}
var deprecation *Deprecation
for _, c := range constraints {
if c.Deprecated != nil && deprecation == nil {
deprecation = c.Deprecated
}
if c.Forbidden {
return nil, fmt.Errorf("%q is no longer allowed", value)
}
}
return deprecation, nil
}