From f9291b64bce3d93ee00473aa7d284b728473c85b Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Thu, 23 Feb 2023 13:43:25 +0100 Subject: [PATCH 1/2] feat: expose ErrInvalidPath --- error.go | 16 ++++++++++------ path.go | 20 ++++++++++---------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/error.go b/error.go index ca2e841..dafc446 100644 --- a/error.go +++ b/error.go @@ -4,20 +4,24 @@ import ( "fmt" ) -// helper type so path parsing errors include the path -type pathError struct { +type ErrInvalidPath struct { error error path string } -func (e *pathError) Error() string { +func (e ErrInvalidPath) Error() string { return fmt.Sprintf("invalid path %q: %s", e.path, e.error) } -func (e *pathError) Unwrap() error { +func (e ErrInvalidPath) Unwrap() error { return e.error } -func (e *pathError) Path() string { - return e.path +func (e ErrInvalidPath) Is(err error) bool { + switch err.(type) { + case ErrInvalidPath: + return true + default: + return false + } } diff --git a/path.go b/path.go index e70d963..6d53ade 100644 --- a/path.go +++ b/path.go @@ -97,33 +97,33 @@ func ParsePath(txt string) (Path, error) { // we expect this to start with a hash, and be an 'ipfs' path if parts[0] != "" { if _, err := decodeCid(parts[0]); err != nil { - return "", &pathError{error: err, path: txt} + return "", &ErrInvalidPath{error: err, path: txt} } // The case when the path starts with hash without a protocol prefix return Path("/ipfs/" + txt), nil } if len(parts) < 3 { - return "", &pathError{error: fmt.Errorf("invalid ipfs path"), path: txt} + return "", &ErrInvalidPath{error: fmt.Errorf("invalid ipfs path"), path: txt} } //TODO: make this smarter switch parts[1] { case "ipfs", "ipld": if parts[2] == "" { - return "", &pathError{error: fmt.Errorf("not enough path components"), path: txt} + return "", &ErrInvalidPath{error: fmt.Errorf("not enough path components"), path: txt} } // Validate Cid. _, err := decodeCid(parts[2]) if err != nil { - return "", &pathError{error: fmt.Errorf("invalid CID: %s", err), path: txt} + return "", &ErrInvalidPath{error: fmt.Errorf("invalid CID: %w", err), path: txt} } case "ipns": if parts[2] == "" { - return "", &pathError{error: fmt.Errorf("not enough path components"), path: txt} + return "", &ErrInvalidPath{error: fmt.Errorf("not enough path components"), path: txt} } default: - return "", &pathError{error: fmt.Errorf("unknown namespace %q", parts[1]), path: txt} + return "", &ErrInvalidPath{error: fmt.Errorf("unknown namespace %q", parts[1]), path: txt} } return Path(txt), nil @@ -132,12 +132,12 @@ func ParsePath(txt string) (Path, error) { // ParseCidToPath takes a CID in string form and returns a valid ipfs Path. func ParseCidToPath(txt string) (Path, error) { if txt == "" { - return "", &pathError{error: fmt.Errorf("empty"), path: txt} + return "", &ErrInvalidPath{error: fmt.Errorf("empty"), path: txt} } c, err := decodeCid(txt) if err != nil { - return "", &pathError{error: err, path: txt} + return "", &ErrInvalidPath{error: err, path: txt} } return FromCid(c), nil @@ -169,13 +169,13 @@ func SplitAbsPath(fpath Path) (cid.Cid, []string, error) { // if nothing, bail. if len(parts) == 0 { - return cid.Cid{}, nil, &pathError{error: fmt.Errorf("empty"), path: string(fpath)} + return cid.Cid{}, nil, &ErrInvalidPath{error: fmt.Errorf("empty"), path: string(fpath)} } c, err := decodeCid(parts[0]) // first element in the path is a cid if err != nil { - return cid.Cid{}, nil, &pathError{error: fmt.Errorf("invalid CID: %s", err), path: string(fpath)} + return cid.Cid{}, nil, &ErrInvalidPath{error: fmt.Errorf("invalid CID: %w", err), path: string(fpath)} } return c, parts[1:], nil From 986db2269d7d78de09c9595f3f88e536948d979c Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Thu, 23 Feb 2023 13:49:33 +0100 Subject: [PATCH 2/2] feat: add test to ensure error.Is works --- error_test.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 error_test.go diff --git a/error_test.go b/error_test.go new file mode 100644 index 0000000..07aab64 --- /dev/null +++ b/error_test.go @@ -0,0 +1,16 @@ +package path + +import ( + "errors" + "testing" +) + +func TestErrorIs(t *testing.T) { + if !errors.Is(ErrInvalidPath{path: "foo", error: errors.New("bar")}, ErrInvalidPath{}) { + t.Fatal("error must be error") + } + + if !errors.Is(&ErrInvalidPath{path: "foo", error: errors.New("bar")}, ErrInvalidPath{}) { + t.Fatal("pointer to error must be error") + } +}