command: simplify errors

This commit is contained in:
Michael Muré
2024-09-05 16:25:22 +02:00
parent f731f56dd2
commit 8bf7fa173c
3 changed files with 38 additions and 79 deletions

View File

@@ -1,54 +1,11 @@
package command
import (
"errors"
"fmt"
"strings"
)
const (
separator = "/"
)
// ErrNew indicates that the wrapped error was encountered while creating
// a new Command.
var ErrNew = errors.New("failed to create Command from elems")
// ErrParse indicates that the wrapped error was encountered while
// attempting to parse a string as a Command.
var ErrParse = errors.New("failed to parse Command")
// ErrorJoin indicates that the wrapped error was encountered while
// attempting to join a new segment to a Command.
var ErrJoin = errors.New("failed to join segments to Command")
// ErrRequiresLeadingSlash is returned when a parsing a string that
// doesn't start with a [leading slash character].
//
// [leading slash character]: https://github.com/ucan-wg/spec#segment-structure
var ErrRequiresLeadingSlash = parseError("a command requires a leading slash character")
// ErrDisallowsTrailingSlash is returned when parsing a string that [ends
// with a trailing slash character].
//
// [ends with a trailing slash character]: https://github.com/ucan-wg/spec#segment-structure
var ErrDisallowsTrailingSlash = parseError("a command must not include a trailing slash")
// ErrUCANNamespaceReserved is returned to indicate that a Command's
// first segment would contain the [reserved "ucan" namespace].
//
// [reserved "ucan" namespace]: https://github.com/ucan-wg/spec#ucan-namespace
var ErrUCANNamespaceReserved = errors.New("the UCAN namespace is reserved")
// ErrRequiresLowercase is returned if a Command contains, or would contain,
// [uppercase unicode characters].
//
// [uppercase unicode characters]: https://github.com/ucan-wg/spec#segment-structure
var ErrRequiresLowercase = parseError("UCAN path segments must must not contain upper-case characters")
func parseError(msg string) error {
return fmt.Errorf("%w: %s", ErrParse, msg)
}
const separator = "/"
var _ fmt.Stringer = (*Command)(nil)
@@ -66,18 +23,8 @@ type Command struct {
// New creates a validated command from the provided list of segment
// strings. An error is returned if an invalid Command would be
// formed
func New(segments ...string) (*Command, error) {
return newCommand(ErrNew, segments...)
}
func newCommand(err error, segments ...string) (*Command, error) {
if len(segments) > 0 && segments[0] == "ucan" {
return nil, fmt.Errorf("%w: %w", err, ErrUCANNamespaceReserved)
}
cmd := Command{segments}
return &cmd, nil
func New(segments ...string) *Command {
return &Command{segments: segments}
}
// Parse verifies that the provided string contains the required
@@ -100,7 +47,16 @@ func Parse(s string) (*Command, error) {
// The leading slash will result in the first element from strings.Split
// being an empty string which is removed as strings.Join will ignore it.
return newCommand(ErrParse, strings.Split(s, "/")[1:]...)
return &Command{strings.Split(s, "/")[1:]}, nil
}
// MustParse is the same as Parse, but panic() if the parsing fail.
func MustParse(s string) *Command {
c, err := Parse(s)
if err != nil {
panic(err)
}
return c
}
// [Top] is the most powerful capability.
@@ -111,22 +67,19 @@ func Parse(s string) (*Command, error) {
//
// [Top]: https://github.com/ucan-wg/spec#-aka-top
func Top() *Command {
cmd, _ := New()
return cmd
return New()
}
// IsValid returns true if the provided string is a valid UCAN command.
func IsValid(s string) bool {
_, err := Parse(s)
return err == nil
}
// Join appends segments to the end of this command using the required
// segment separator.
func (c *Command) Join(segments ...string) (*Command, error) {
return newCommand(ErrJoin, append(c.segments, segments...)...)
func (c *Command) Join(segments ...string) *Command {
return &Command{append(c.segments, segments...)}
}
// Segments returns the ordered segments that comprise the Command as a
@@ -138,5 +91,5 @@ func (c *Command) Segments() []string {
// String returns the composed representation the command. This is also
// the required wire representation (before IPLD encoding occurs.)
func (c *Command) String() string {
return "/" + strings.Join([]string(c.segments), "/")
return "/" + strings.Join(c.segments, "/")
}

View File

@@ -0,0 +1,21 @@
package command
import "fmt"
// ErrRequiresLeadingSlash is returned when a parsing a string that
// doesn't start with a [leading slash character].
//
// [leading slash character]: https://github.com/ucan-wg/spec#segment-structure
var ErrRequiresLeadingSlash = fmt.Errorf("a command requires a leading slash character")
// ErrDisallowsTrailingSlash is returned when parsing a string that [ends
// with a trailing slash character].
//
// [ends with a trailing slash character]: https://github.com/ucan-wg/spec#segment-structure
var ErrDisallowsTrailingSlash = fmt.Errorf("a command must not include a trailing slash")
// ErrRequiresLowercase is returned if a Command contains, or would contain,
// [uppercase unicode characters].
//
// [uppercase unicode characters]: https://github.com/ucan-wg/spec#segment-structure
var ErrRequiresLowercase = fmt.Errorf("UCAN path segments must must not contain upper-case characters")

View File

@@ -73,7 +73,6 @@ func TestParseCommand(t *testing.T) {
t.Parallel()
cmd, err := command.Parse(testcase.inp)
require.ErrorIs(t, err, command.ErrParse)
require.ErrorIs(t, err, testcase.err)
require.Nil(t, cmd)
})
@@ -134,20 +133,6 @@ func invalidTestcases(t *testing.T) []errorTestcase {
},
err: command.ErrDisallowsTrailingSlash,
},
{
testcase: testcase{
name: "only reserved ucan namespace",
inp: "/ucan",
},
err: command.ErrUCANNamespaceReserved,
},
{
testcase: testcase{
name: "reserved ucan namespace prefix",
inp: "/ucan/elem0/elem1/elem2",
},
err: command.ErrUCANNamespaceReserved,
},
{
testcase: testcase{
name: "uppercase character are present",