Skip to content

Commit 9ee7366

Browse files
authoredDec 12, 2023
feat: Validate UUID without creating new UUID (#141)
* feat: Validate UUID without creating new UUID * fix: update comment
1 parent b35aa6a commit 9ee7366

File tree

2 files changed

+82
-0
lines changed

2 files changed

+82
-0
lines changed
 

‎uuid.go

+53
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,59 @@ func Must(uuid UUID, err error) UUID {
186186
return uuid
187187
}
188188

189+
// Validate returns an error if s is not a properly formatted UUID in one of the following formats:
190+
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
191+
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
192+
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
193+
// {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
194+
// It returns an error if the format is invalid, otherwise nil.
195+
func Validate(s string) error {
196+
switch len(s) {
197+
// Standard UUID format
198+
case 36:
199+
200+
// UUID with "urn:uuid:" prefix
201+
case 36 + 9:
202+
if !strings.EqualFold(s[:9], "urn:uuid:") {
203+
return fmt.Errorf("invalid urn prefix: %q", s[:9])
204+
}
205+
s = s[9:]
206+
207+
// UUID enclosed in braces
208+
case 36 + 2:
209+
if s[0] != '{' || s[len(s)-1] != '}' {
210+
return fmt.Errorf("invalid bracketed UUID format")
211+
}
212+
s = s[1 : len(s)-1]
213+
214+
// UUID without hyphens
215+
case 32:
216+
for i := 0; i < len(s); i += 2 {
217+
_, ok := xtob(s[i], s[i+1])
218+
if !ok {
219+
return errors.New("invalid UUID format")
220+
}
221+
}
222+
223+
default:
224+
return invalidLengthError{len(s)}
225+
}
226+
227+
// Check for standard UUID format
228+
if len(s) == 36 {
229+
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
230+
return errors.New("invalid UUID format")
231+
}
232+
for _, x := range []int{0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34} {
233+
if _, ok := xtob(s[x], s[x+1]); !ok {
234+
return errors.New("invalid UUID format")
235+
}
236+
}
237+
}
238+
239+
return nil
240+
}
241+
189242
// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
190243
// , or "" if uuid is invalid.
191244
func (uuid UUID) String() string {

‎uuid_test.go

+29
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package uuid
66

77
import (
88
"bytes"
9+
"errors"
910
"fmt"
1011
"os"
1112
"runtime"
@@ -605,6 +606,34 @@ func FuzzFromBytes(f *testing.F) {
605606
})
606607
}
607608

609+
// TestValidate checks various scenarios for the Validate function
610+
func TestValidate(t *testing.T) {
611+
testCases := []struct {
612+
name string
613+
input string
614+
expect error
615+
}{
616+
{"Valid UUID", "123e4567-e89b-12d3-a456-426655440000", nil},
617+
{"Valid UUID with URN", "urn:uuid:123e4567-e89b-12d3-a456-426655440000", nil},
618+
{"Valid UUID with Braces", "{123e4567-e89b-12d3-a456-426655440000}", nil},
619+
{"Valid UUID No Hyphens", "123e4567e89b12d3a456426655440000", nil},
620+
{"Invalid UUID", "invalid-uuid", errors.New("invalid UUID length: 12")},
621+
{"Invalid Length", "123", fmt.Errorf("invalid UUID length: %d", len("123"))},
622+
{"Invalid URN Prefix", "urn:test:123e4567-e89b-12d3-a456-426655440000", fmt.Errorf("invalid urn prefix: %q", "urn:test:")},
623+
{"Invalid Brackets", "[123e4567-e89b-12d3-a456-426655440000]", fmt.Errorf("invalid bracketed UUID format")},
624+
{"Invalid UUID Format", "12345678gabc1234abcd1234abcd1234", fmt.Errorf("invalid UUID format")},
625+
}
626+
627+
for _, tc := range testCases {
628+
t.Run(tc.name, func(t *testing.T) {
629+
err := Validate(tc.input)
630+
if (err != nil) != (tc.expect != nil) || (err != nil && err.Error() != tc.expect.Error()) {
631+
t.Errorf("Validate(%q) = %v, want %v", tc.input, err, tc.expect)
632+
}
633+
})
634+
}
635+
}
636+
608637
var asString = "f47ac10b-58cc-0372-8567-0e02b2c3d479"
609638
var asBytes = []byte(asString)
610639

0 commit comments

Comments
 (0)
Please sign in to comment.