diff --git a/uuid.go b/uuid.go index b745bac..dc75cee 100644 --- a/uuid.go +++ b/uuid.go @@ -47,18 +47,40 @@ var ( poolMu sync.Mutex poolPos = randPoolSize // protected with poolMu pool [randPoolSize]byte // protected with poolMu + + ErrInvalidUUIDFormat = errors.New("invalid UUID format") + ErrInvalidBracketedFormat = errors.New("invalid bracketed UUID format") ) +type URNPrefixError struct { prefix string } + +func (e URNPrefixError) Error() string { + return fmt.Sprintf("invalid urn prefix: %q", e.prefix) +} + +func (e URNPrefixError) Is(target error) bool { + _, ok := target.(URNPrefixError) + return ok +} + +var ErrInvalidURNPrefix = URNPrefixError{} + type invalidLengthError struct{ len int } func (err invalidLengthError) Error() string { return fmt.Sprintf("invalid UUID length: %d", err.len) } +func (e invalidLengthError) Is(target error) bool { + _, ok := target.(invalidLengthError) + return ok +} + +var ErrInvalidLength = invalidLengthError{} + // IsInvalidLengthError is matcher function for custom error invalidLengthError func IsInvalidLengthError(err error) bool { - _, ok := err.(invalidLengthError) - return ok + return errors.Is(err, ErrInvalidLength) } // 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) { // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx case 36 + 9: if !strings.EqualFold(s[:9], "urn:uuid:") { - return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9]) + return uuid, URNPrefixError{s[:9]} } s = s[9:] @@ -93,7 +115,7 @@ func Parse(s string) (UUID, error) { for i := range uuid { uuid[i], ok = xtob(s[i*2], s[i*2+1]) if !ok { - return uuid, errors.New("invalid UUID format") + return uuid, ErrInvalidUUIDFormat } } return uuid, nil @@ -103,7 +125,8 @@ func Parse(s string) (UUID, error) { // s is now at least 36 bytes long // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { - return uuid, errors.New("invalid UUID format") + return uuid, ErrInvalidUUIDFormat + } for i, x := range [16]int{ 0, 2, 4, 6, @@ -114,7 +137,7 @@ func Parse(s string) (UUID, error) { } { v, ok := xtob(s[x], s[x+1]) if !ok { - return uuid, errors.New("invalid UUID format") + return uuid, ErrInvalidUUIDFormat } uuid[i] = v } @@ -128,7 +151,7 @@ func ParseBytes(b []byte) (UUID, error) { case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx if !bytes.EqualFold(b[:9], []byte("urn:uuid:")) { - return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9]) + return uuid, URNPrefixError{string(b[:9])} } b = b[9:] case 36 + 2: // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} @@ -138,7 +161,7 @@ func ParseBytes(b []byte) (UUID, error) { for i := 0; i < 32; i += 2 { uuid[i/2], ok = xtob(b[i], b[i+1]) if !ok { - return uuid, errors.New("invalid UUID format") + return uuid, ErrInvalidUUIDFormat } } return uuid, nil @@ -148,7 +171,7 @@ func ParseBytes(b []byte) (UUID, error) { // s is now at least 36 bytes long // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' { - return uuid, errors.New("invalid UUID format") + return uuid, ErrInvalidUUIDFormat } for i, x := range [16]int{ 0, 2, 4, 6, @@ -159,7 +182,7 @@ func ParseBytes(b []byte) (UUID, error) { } { v, ok := xtob(b[x], b[x+1]) if !ok { - return uuid, errors.New("invalid UUID format") + return uuid, ErrInvalidUUIDFormat } uuid[i] = v } @@ -205,14 +228,14 @@ func Validate(s string) error { // UUID with "urn:uuid:" prefix case 36 + 9: if !strings.EqualFold(s[:9], "urn:uuid:") { - return fmt.Errorf("invalid urn prefix: %q", s[:9]) + return URNPrefixError{s[:9]} } s = s[9:] // UUID enclosed in braces case 36 + 2: if s[0] != '{' || s[len(s)-1] != '}' { - return fmt.Errorf("invalid bracketed UUID format") + return ErrInvalidBracketedFormat } s = s[1 : len(s)-1] @@ -221,7 +244,7 @@ func Validate(s string) error { for i := 0; i < len(s); i += 2 { _, ok := xtob(s[i], s[i+1]) if !ok { - return errors.New("invalid UUID format") + return ErrInvalidUUIDFormat } } @@ -232,11 +255,11 @@ func Validate(s string) error { // Check for standard UUID format if len(s) == 36 { if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { - return errors.New("invalid UUID format") + return ErrInvalidUUIDFormat } for _, x := range []int{0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34} { if _, ok := xtob(s[x], s[x+1]); !ok { - return errors.New("invalid UUID format") + return ErrInvalidUUIDFormat } } } diff --git a/uuid_test.go b/uuid_test.go index d848382..906ecbe 100644 --- a/uuid_test.go +++ b/uuid_test.go @@ -569,7 +569,7 @@ func TestWrongLength(t *testing.T) { func TestIsWrongLength(t *testing.T) { _, err := Parse("12345") if !IsInvalidLengthError(err) { - t.Errorf("expected error type is invalidLengthError") + t.Errorf("IsInvalidLength returned incorrect type %T", err) } }