2024-08-30 08:21:24 -04:00
|
|
|
package command
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
2024-09-05 16:25:22 +02:00
|
|
|
const separator = "/"
|
2024-08-30 08:21:24 -04:00
|
|
|
|
|
|
|
|
var _ fmt.Stringer = (*Command)(nil)
|
|
|
|
|
|
|
|
|
|
// Command is a concrete messages (a "verb") that MUST be unambiguously
|
|
|
|
|
// interpretable by the Subject of a UCAN.
|
|
|
|
|
//
|
|
|
|
|
// A [Command] is composed of a leading slash which is optionally followed
|
|
|
|
|
// by one or more slash-separated Segments of lowercase characters.
|
|
|
|
|
//
|
|
|
|
|
// [Command]: https://github.com/ucan-wg/spec#command
|
|
|
|
|
type Command struct {
|
|
|
|
|
segments []string
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-19 11:16:33 +02:00
|
|
|
// 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 {
|
|
|
|
|
return Command{segments: segments}
|
2024-08-30 08:21:24 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Parse verifies that the provided string contains the required
|
|
|
|
|
// [segment structure] and, if valid, returns the resulting
|
|
|
|
|
// Command.
|
|
|
|
|
//
|
|
|
|
|
// [segment structure]: https://github.com/ucan-wg/spec#segment-structure
|
2024-09-19 11:16:33 +02:00
|
|
|
func Parse(s string) (Command, error) {
|
2024-08-30 08:21:24 -04:00
|
|
|
if !strings.HasPrefix(s, "/") {
|
2024-09-19 11:16:33 +02:00
|
|
|
return Command{}, ErrRequiresLeadingSlash
|
2024-08-30 08:21:24 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(s) > 1 && strings.HasSuffix(s, "/") {
|
2024-09-19 11:16:33 +02:00
|
|
|
return Command{}, ErrDisallowsTrailingSlash
|
2024-08-30 08:21:24 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if s != strings.ToLower(s) {
|
2024-09-19 11:16:33 +02:00
|
|
|
return Command{}, ErrRequiresLowercase
|
2024-08-30 08:21:24 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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.
|
2024-09-19 11:16:33 +02:00
|
|
|
return Command{strings.Split(s, "/")[1:]}, nil
|
2024-09-05 16:25:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MustParse is the same as Parse, but panic() if the parsing fail.
|
2024-09-19 11:16:33 +02:00
|
|
|
func MustParse(s string) Command {
|
2024-09-05 16:25:22 +02:00
|
|
|
c, err := Parse(s)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
return c
|
2024-08-30 08:21:24 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// [Top] is the most powerful capability.
|
|
|
|
|
//
|
|
|
|
|
// This function returns a Command that is a wildcard and therefore represents the
|
2024-09-19 11:16:33 +02:00
|
|
|
// most powerful ability. As such, it should be handled with care and used sparingly.
|
2024-08-30 08:21:24 -04:00
|
|
|
//
|
|
|
|
|
// [Top]: https://github.com/ucan-wg/spec#-aka-top
|
2024-09-19 11:16:33 +02:00
|
|
|
func Top() Command {
|
2024-09-05 16:25:22 +02:00
|
|
|
return New()
|
2024-08-30 08:21:24 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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.
|
2024-09-19 11:16:33 +02:00
|
|
|
func (c Command) Join(segments ...string) Command {
|
|
|
|
|
return Command{append(c.segments, segments...)}
|
2024-08-30 08:21:24 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Segments returns the ordered segments that comprise the Command as a
|
|
|
|
|
// slice of strings.
|
2024-09-19 11:16:33 +02:00
|
|
|
func (c Command) Segments() []string {
|
2024-08-30 08:21:24 -04:00
|
|
|
return c.segments
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// String returns the composed representation the command. This is also
|
|
|
|
|
// the required wire representation (before IPLD encoding occurs.)
|
2024-09-19 11:16:33 +02:00
|
|
|
func (c Command) String() string {
|
2024-09-05 16:25:22 +02:00
|
|
|
return "/" + strings.Join(c.segments, "/")
|
2024-08-30 08:21:24 -04:00
|
|
|
}
|