Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented support for checkable errors #131

Merged
merged 9 commits into from
May 12, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Implemented ErrUUID and ErrUUIDInvalidFormat error types
  • Loading branch information
Patrik Lindahl committed Mar 27, 2024

Verified

This commit was signed with the committer’s verified signature.
driesvints Dries Vints
commit a3a30810802e1a41a9b1c5f61cfc7e97c1c82837
33 changes: 13 additions & 20 deletions codec.go
Original file line number Diff line number Diff line change
@@ -21,11 +21,6 @@

package uuid

import (
"errors"
"fmt"
)

// FromBytes returns a UUID generated from the raw byte slice input.
// It will return an error if the slice isn't 16 bytes long.
func FromBytes(input []byte) (UUID, error) {
@@ -44,8 +39,6 @@ func FromBytesOrNil(input []byte) UUID {
return uuid
}

var errInvalidFormat = errors.New("uuid: invalid UUID format")

func fromHexChar(c byte) byte {
switch {
case '0' <= c && c <= '9':
@@ -66,21 +59,21 @@ func (u *UUID) Parse(s string) error {
case 36: // canonical
case 34, 38:
if s[0] != '{' || s[len(s)-1] != '}' {
return fmt.Errorf("uuid: incorrect UUID format in string %q", s)
return invalidFormatf("incorrect UUID format in string %q", s)
}
s = s[1 : len(s)-1]
case 41, 45:
if s[:9] != "urn:uuid:" {
return fmt.Errorf("uuid: incorrect UUID format in string %q", s[:9])
return invalidFormatf("incorrect UUID format in string %q", s[:9])
}
s = s[9:]
default:
return fmt.Errorf("uuid: incorrect UUID length %d in string %q", len(s), s)
return invalidFormatf("incorrect UUID length %d in string %q", len(s), s)
}
// canonical
if len(s) == 36 {
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
return fmt.Errorf("uuid: incorrect UUID format in string %q", s)
return invalidFormatf("incorrect UUID format in string %q", s)
}
for i, x := range [16]byte{
0, 2, 4, 6,
@@ -92,7 +85,7 @@ func (u *UUID) Parse(s string) error {
v1 := fromHexChar(s[x])
v2 := fromHexChar(s[x+1])
if v1|v2 == 255 {
return errInvalidFormat
return invalidFormat()
}
u[i] = (v1 << 4) | v2
}
@@ -103,7 +96,7 @@ func (u *UUID) Parse(s string) error {
v1 := fromHexChar(s[i])
v2 := fromHexChar(s[i+1])
if v1|v2 == 255 {
return errInvalidFormat
return invalidFormat()
}
u[i/2] = (v1 << 4) | v2
}
@@ -175,20 +168,20 @@ func (u *UUID) UnmarshalText(b []byte) error {
case 36: // canonical
case 34, 38:
if b[0] != '{' || b[len(b)-1] != '}' {
return fmt.Errorf("uuid: incorrect UUID format in string %q", b)
return invalidFormatf("incorrect UUID format in string %q", b)
}
b = b[1 : len(b)-1]
case 41, 45:
if string(b[:9]) != "urn:uuid:" {
return fmt.Errorf("uuid: incorrect UUID format in string %q", b[:9])
return invalidFormatf("incorrect UUID format in string %q", b[:9])
}
b = b[9:]
default:
return fmt.Errorf("uuid: incorrect UUID length %d in string %q", len(b), b)
return invalidFormatf("incorrect UUID length %d in string %q", len(b), b)
}
if len(b) == 36 {
if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' {
return fmt.Errorf("uuid: incorrect UUID format in string %q", b)
return invalidFormatf("incorrect UUID format in string %q", b)
}
for i, x := range [16]byte{
0, 2, 4, 6,
@@ -200,7 +193,7 @@ func (u *UUID) UnmarshalText(b []byte) error {
v1 := fromHexChar(b[x])
v2 := fromHexChar(b[x+1])
if v1|v2 == 255 {
return errInvalidFormat
return invalidFormat()
}
u[i] = (v1 << 4) | v2
}
@@ -210,7 +203,7 @@ func (u *UUID) UnmarshalText(b []byte) error {
v1 := fromHexChar(b[i])
v2 := fromHexChar(b[i+1])
if v1|v2 == 255 {
return errInvalidFormat
return invalidFormat()
}
u[i/2] = (v1 << 4) | v2
}
@@ -226,7 +219,7 @@ func (u UUID) MarshalBinary() ([]byte, error) {
// It will return an error if the slice isn't 16 bytes long.
func (u *UUID) UnmarshalBinary(data []byte) error {
if len(data) != Size {
return fmt.Errorf("uuid: UUID must be exactly 16 bytes long, got %d bytes", len(data))
return invalidFormatf("uuid: UUID must be exactly 16 bytes long, got %d bytes", len(data))
}
copy(u[:], data)

45 changes: 45 additions & 0 deletions error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package uuid

import (
"fmt"
)

// ErrUUID is the base error type for errors generated by this package
type ErrUUID struct{}

func (e *ErrUUID) Error() string {
return "uuid"
}

// ErrUUIDInvalidFormat is a specialized error that is returned when there is
// a problem parsing UUID data
type ErrUUIDInvalidFormat struct {
err error
}

func (e *ErrUUIDInvalidFormat) Error() string {
return e.err.Error()
}

func (e *ErrUUIDInvalidFormat) Unwrap() error {
return e.err
}

func (e *ErrUUIDInvalidFormat) Is(target error) bool {
_, ok := target.(*ErrUUIDInvalidFormat)
return ok
}

func invalidFormat() error {
return &ErrUUIDInvalidFormat{
err: fmt.Errorf("%w: invalid UUID format", &ErrUUID{}),
}
}

func invalidFormatf(format string, a ...any) error {
formattedError := fmt.Errorf(format, a...)

return &ErrUUIDInvalidFormat{
err: fmt.Errorf("%w: %v", &ErrUUID{}, formattedError),
}
}
54 changes: 54 additions & 0 deletions error_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package uuid

import (
"errors"
"fmt"
"reflect"
"testing"
)

func TestError(t *testing.T) {
tcs := []struct {
err error
expected string
expectedTarget error
}{
{
err: fmt.Errorf("%w: sample error: %v", &ErrUUID{}, 123),
expected: "uuid: sample error: 123",
expectedTarget: &ErrUUID{},
},
{
err: invalidFormat(),
expected: "uuid: invalid UUID format",
expectedTarget: &ErrUUIDInvalidFormat{},
},
{
err: invalidFormatf("sample error: %v", 123),
expected: "uuid: sample error: 123",
expectedTarget: &ErrUUIDInvalidFormat{},
},
{
err: fmt.Errorf("uuid error: %w", invalidFormatf("sample error: %v", 123)),
expected: "uuid error: uuid: sample error: 123",
expectedTarget: &ErrUUIDInvalidFormat{},
},
}
for i, tc := range tcs {
t.Run(fmt.Sprintf("Test case %d", i), func(t *testing.T) {
if !errors.Is(tc.err, &ErrUUID{}) {
t.Error("expected error to be of a wrapped type of Error")
}
if !errors.Is(tc.err, tc.expectedTarget) {
t.Errorf("expected error to be of type %v, but was %v", reflect.TypeOf(tc.expectedTarget), reflect.TypeOf(tc.err))
}
if tc.err.Error() != tc.expected {
t.Errorf("expected err.Error() to be '%s' but was '%s'", tc.expected, tc.err.Error())
}
uuidErr := &ErrUUID{}
if !errors.As(tc.err, &uuidErr) {
t.Error("expected errors.As() to work")
}
})
}
}
2 changes: 1 addition & 1 deletion generator.go
Original file line number Diff line number Diff line change
@@ -452,5 +452,5 @@ func defaultHWAddrFunc() (net.HardwareAddr, error) {
return iface.HardwareAddr, nil
}
}
return []byte{}, fmt.Errorf("uuid: no HW address found")
return []byte{}, fmt.Errorf("%w: no HW address found", &ErrUUID{})
}
2 changes: 1 addition & 1 deletion sql.go
Original file line number Diff line number Diff line change
@@ -56,7 +56,7 @@ func (u *UUID) Scan(src interface{}) error {
return err
}

return fmt.Errorf("uuid: cannot convert %T to UUID", src)
return fmt.Errorf("%w: cannot convert %T to UUID", &ErrUUID{}, src)
}

// NullUUID can be used with the standard sql package to represent a
6 changes: 3 additions & 3 deletions uuid.go
Original file line number Diff line number Diff line change
@@ -100,7 +100,7 @@ func (t Timestamp) Time() (time.Time, error) {
// Returns an error if the UUID is any version other than 1.
func TimestampFromV1(u UUID) (Timestamp, error) {
if u.Version() != 1 {
err := fmt.Errorf("uuid: %s is version %d, not version 1", u, u.Version())
err := fmt.Errorf("%w: %s is version %d, not version 1", &ErrUUID{}, u, u.Version())
return 0, err
}

@@ -121,7 +121,7 @@ func TimestampFromV1(u UUID) (Timestamp, error) {
// releases until the spec is final.
func TimestampFromV6(u UUID) (Timestamp, error) {
if u.Version() != 6 {
return 0, fmt.Errorf("uuid: %s is version %d, not version 6", u, u.Version())
return 0, fmt.Errorf("%w: %s is version %d, not version 6", &ErrUUID{}, u, u.Version())
}

hi := binary.BigEndian.Uint32(u[0:4])
@@ -141,7 +141,7 @@ func TimestampFromV6(u UUID) (Timestamp, error) {
// releases until the spec is final.
func TimestampFromV7(u UUID) (Timestamp, error) {
if u.Version() != 7 {
return 0, fmt.Errorf("uuid: %s is version %d, not version 6", u, u.Version())
return 0, fmt.Errorf("%w: %s is version %d, not version 6", &ErrUUID{}, u, u.Version())
}

t := 0 |