Skip to content

Commit 0e97ed3

Browse files
authored
feat: add error types for better validation (#166)
* feat: add error types * fix: broken backward compatibility
1 parent d55c313 commit 0e97ed3

File tree

2 files changed

+39
-16
lines changed

2 files changed

+39
-16
lines changed

uuid.go

+38-15
Original file line numberDiff line numberDiff line change
@@ -47,18 +47,40 @@ var (
4747
poolMu sync.Mutex
4848
poolPos = randPoolSize // protected with poolMu
4949
pool [randPoolSize]byte // protected with poolMu
50+
51+
ErrInvalidUUIDFormat = errors.New("invalid UUID format")
52+
ErrInvalidBracketedFormat = errors.New("invalid bracketed UUID format")
5053
)
5154

55+
type URNPrefixError struct { prefix string }
56+
57+
func (e URNPrefixError) Error() string {
58+
return fmt.Sprintf("invalid urn prefix: %q", e.prefix)
59+
}
60+
61+
func (e URNPrefixError) Is(target error) bool {
62+
_, ok := target.(URNPrefixError)
63+
return ok
64+
}
65+
66+
var ErrInvalidURNPrefix = URNPrefixError{}
67+
5268
type invalidLengthError struct{ len int }
5369

5470
func (err invalidLengthError) Error() string {
5571
return fmt.Sprintf("invalid UUID length: %d", err.len)
5672
}
5773

74+
func (e invalidLengthError) Is(target error) bool {
75+
_, ok := target.(invalidLengthError)
76+
return ok
77+
}
78+
79+
var ErrInvalidLength = invalidLengthError{}
80+
5881
// IsInvalidLengthError is matcher function for custom error invalidLengthError
5982
func IsInvalidLengthError(err error) bool {
60-
_, ok := err.(invalidLengthError)
61-
return ok
83+
return errors.Is(err, ErrInvalidLength)
6284
}
6385

6486
// Parse decodes s into a UUID or returns an error if it cannot be parsed. Both
@@ -79,7 +101,7 @@ func Parse(s string) (UUID, error) {
79101
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
80102
case 36 + 9:
81103
if !strings.EqualFold(s[:9], "urn:uuid:") {
82-
return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9])
104+
return uuid, URNPrefixError{s[:9]}
83105
}
84106
s = s[9:]
85107

@@ -93,7 +115,7 @@ func Parse(s string) (UUID, error) {
93115
for i := range uuid {
94116
uuid[i], ok = xtob(s[i*2], s[i*2+1])
95117
if !ok {
96-
return uuid, errors.New("invalid UUID format")
118+
return uuid, ErrInvalidUUIDFormat
97119
}
98120
}
99121
return uuid, nil
@@ -103,7 +125,8 @@ func Parse(s string) (UUID, error) {
103125
// s is now at least 36 bytes long
104126
// it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
105127
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
106-
return uuid, errors.New("invalid UUID format")
128+
return uuid, ErrInvalidUUIDFormat
129+
107130
}
108131
for i, x := range [16]int{
109132
0, 2, 4, 6,
@@ -114,7 +137,7 @@ func Parse(s string) (UUID, error) {
114137
} {
115138
v, ok := xtob(s[x], s[x+1])
116139
if !ok {
117-
return uuid, errors.New("invalid UUID format")
140+
return uuid, ErrInvalidUUIDFormat
118141
}
119142
uuid[i] = v
120143
}
@@ -128,7 +151,7 @@ func ParseBytes(b []byte) (UUID, error) {
128151
case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
129152
case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
130153
if !bytes.EqualFold(b[:9], []byte("urn:uuid:")) {
131-
return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9])
154+
return uuid, URNPrefixError{string(b[:9])}
132155
}
133156
b = b[9:]
134157
case 36 + 2: // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
@@ -138,7 +161,7 @@ func ParseBytes(b []byte) (UUID, error) {
138161
for i := 0; i < 32; i += 2 {
139162
uuid[i/2], ok = xtob(b[i], b[i+1])
140163
if !ok {
141-
return uuid, errors.New("invalid UUID format")
164+
return uuid, ErrInvalidUUIDFormat
142165
}
143166
}
144167
return uuid, nil
@@ -148,7 +171,7 @@ func ParseBytes(b []byte) (UUID, error) {
148171
// s is now at least 36 bytes long
149172
// it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
150173
if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' {
151-
return uuid, errors.New("invalid UUID format")
174+
return uuid, ErrInvalidUUIDFormat
152175
}
153176
for i, x := range [16]int{
154177
0, 2, 4, 6,
@@ -159,7 +182,7 @@ func ParseBytes(b []byte) (UUID, error) {
159182
} {
160183
v, ok := xtob(b[x], b[x+1])
161184
if !ok {
162-
return uuid, errors.New("invalid UUID format")
185+
return uuid, ErrInvalidUUIDFormat
163186
}
164187
uuid[i] = v
165188
}
@@ -205,14 +228,14 @@ func Validate(s string) error {
205228
// UUID with "urn:uuid:" prefix
206229
case 36 + 9:
207230
if !strings.EqualFold(s[:9], "urn:uuid:") {
208-
return fmt.Errorf("invalid urn prefix: %q", s[:9])
231+
return URNPrefixError{s[:9]}
209232
}
210233
s = s[9:]
211234

212235
// UUID enclosed in braces
213236
case 36 + 2:
214237
if s[0] != '{' || s[len(s)-1] != '}' {
215-
return fmt.Errorf("invalid bracketed UUID format")
238+
return ErrInvalidBracketedFormat
216239
}
217240
s = s[1 : len(s)-1]
218241

@@ -221,7 +244,7 @@ func Validate(s string) error {
221244
for i := 0; i < len(s); i += 2 {
222245
_, ok := xtob(s[i], s[i+1])
223246
if !ok {
224-
return errors.New("invalid UUID format")
247+
return ErrInvalidUUIDFormat
225248
}
226249
}
227250

@@ -232,11 +255,11 @@ func Validate(s string) error {
232255
// Check for standard UUID format
233256
if len(s) == 36 {
234257
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
235-
return errors.New("invalid UUID format")
258+
return ErrInvalidUUIDFormat
236259
}
237260
for _, x := range []int{0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34} {
238261
if _, ok := xtob(s[x], s[x+1]); !ok {
239-
return errors.New("invalid UUID format")
262+
return ErrInvalidUUIDFormat
240263
}
241264
}
242265
}

uuid_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,7 @@ func TestWrongLength(t *testing.T) {
569569
func TestIsWrongLength(t *testing.T) {
570570
_, err := Parse("12345")
571571
if !IsInvalidLengthError(err) {
572-
t.Errorf("expected error type is invalidLengthError")
572+
t.Errorf("IsInvalidLength returned incorrect type %T", err)
573573
}
574574
}
575575

0 commit comments

Comments
 (0)