Commit c13536f1 authored by Steven Allen's avatar Steven Allen

include the path in path errors

This should improve UX by telling the user the path we failed to parse.
parent a1e7a849
package path
import (
"fmt"
)
// helper type so path parsing errors include the path
type pathError struct {
error error
path string
}
func (e *pathError) Error() string {
return fmt.Sprintf("invalid path %q: %s", e.path, e.error)
}
func (e *pathError) Unwrap() error {
return e.error
}
func (e *pathError) Path() string {
return e.path
}
......@@ -2,23 +2,13 @@
package path
import (
"errors"
"fmt"
"path"
"strings"
cid "github.com/ipfs/go-cid"
)
var (
// ErrBadPath is returned when a given path is incorrectly formatted
ErrBadPath = errors.New("invalid 'ipfs ref' path")
// ErrNoComponents is used when Paths after a protocol
// do not contain at least one component
ErrNoComponents = errors.New(
"path must contain at least one component")
)
// A Path represents an ipfs content path:
// * /<cid>/path/to/file
// * /ipfs/<cid>
......@@ -107,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 := cid.Decode(parts[0]); err != nil {
return "", ErrBadPath
return "", &pathError{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 "", ErrBadPath
return "", &pathError{error: fmt.Errorf("path does not begin with '/'"), path: txt}
}
//TODO: make this smarter
switch parts[1] {
case "ipfs", "ipld":
if parts[2] == "" {
return "", ErrNoComponents
return "", &pathError{error: fmt.Errorf("not enough path components"), path: txt}
}
// Validate Cid.
_, err := cid.Decode(parts[2])
if err != nil {
return "", err
return "", &pathError{error: fmt.Errorf("invalid CID: %s", err), path: txt}
}
case "ipns":
if parts[2] == "" {
return "", ErrNoComponents
return "", &pathError{error: fmt.Errorf("not enough path components"), path: txt}
}
default:
return "", ErrBadPath
return "", &pathError{error: fmt.Errorf("unknown namespace %q", parts[1]), path: txt}
}
return Path(txt), nil
......@@ -142,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 "", ErrNoComponents
return "", &pathError{error: fmt.Errorf("empty"), path: txt}
}
c, err := cid.Decode(txt)
if err != nil {
return "", err
return "", &pathError{error: err, path: txt}
}
return FromCid(c), nil
......@@ -179,13 +169,13 @@ func SplitAbsPath(fpath Path) (cid.Cid, []string, error) {
// if nothing, bail.
if len(parts) == 0 {
return cid.Cid{}, nil, ErrNoComponents
return cid.Cid{}, nil, &pathError{error: fmt.Errorf("empty"), path: string(fpath)}
}
c, err := cid.Decode(parts[0])
// first element in the path is a cid
if err != nil {
return cid.Cid{}, nil, err
return cid.Cid{}, nil, &pathError{error: fmt.Errorf("invalid CID: %s", err), path: string(fpath)}
}
return c, parts[1:], nil
......
package path
import (
"strings"
"testing"
)
......@@ -44,8 +45,8 @@ func TestNoComponents(t *testing.T) {
"/ipld/",
} {
_, err := ParsePath(s)
if err != ErrNoComponents {
t.Errorf("expected ErrNoComponents, got %s", err)
if err == nil || !strings.Contains(err.Error(), "not enough path components") || !strings.Contains(err.Error(), s) {
t.Error("wrong error")
}
}
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment