command: use NUL for magic usage string
Test / Create distribution (push) Successful in 52s
Test / Sandbox (push) Successful in 2m51s
Test / Hakurei (push) Successful in 4m44s
Test / Sandbox (race detector) (push) Successful in 5m46s
Test / Hakurei (race detector) (push) Successful in 7m5s
Test / ShareFS (push) Successful in 7m5s
Test / Flake checks (push) Successful in 1m8s
Test / Create distribution (push) Successful in 52s
Test / Sandbox (push) Successful in 2m51s
Test / Hakurei (push) Successful in 4m44s
Test / Sandbox (race detector) (push) Successful in 5m46s
Test / Hakurei (race detector) (push) Successful in 7m5s
Test / ShareFS (push) Successful in 7m5s
Test / Flake checks (push) Successful in 1m8s
This change also improves documentation. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
@@ -13,6 +13,7 @@ func New(output io.Writer, logf LogFunc, name string, early HandlerFunc) Command
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newNode initialises a subcommand tree and returns its address.
|
||||||
func newNode(output io.Writer, logf LogFunc, name, usage string) *node {
|
func newNode(output io.Writer, logf LogFunc, name, usage string) *node {
|
||||||
n := &node{
|
n := &node{
|
||||||
name: name, usage: usage,
|
name: name, usage: usage,
|
||||||
|
|||||||
+16
-4
@@ -18,8 +18,14 @@ func TestBuild(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("direct zero length", func(t *testing.T) {
|
t.Run("direct zero length", func(t *testing.T) {
|
||||||
wantPanic := "invalid subcommand"
|
wantPanic := "invalid subcommand"
|
||||||
t.Run("zero length name", func(t *testing.T) { defer checkRecover(t, "Command", wantPanic); c.Command("", "usage", stubHandler) })
|
t.Run("zero length name", func(t *testing.T) {
|
||||||
t.Run("zero length usage", func(t *testing.T) { defer checkRecover(t, "Command", wantPanic); c.Command("name", "", stubHandler) })
|
defer checkRecover(t, "Command", wantPanic)
|
||||||
|
c.Command("", "usage", stubHandler)
|
||||||
|
})
|
||||||
|
t.Run("zero length usage", func(t *testing.T) {
|
||||||
|
defer checkRecover(t, "Command", wantPanic)
|
||||||
|
c.Command("name", "", stubHandler)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("direct adopt unique names", func(t *testing.T) {
|
t.Run("direct adopt unique names", func(t *testing.T) {
|
||||||
@@ -34,8 +40,14 @@ func TestBuild(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("zero length", func(t *testing.T) {
|
t.Run("zero length", func(t *testing.T) {
|
||||||
wantPanic := "invalid subcommand tree"
|
wantPanic := "invalid subcommand tree"
|
||||||
t.Run("zero length name", func(t *testing.T) { defer checkRecover(t, "New", wantPanic); c.New("", "usage") })
|
t.Run("zero length name", func(t *testing.T) {
|
||||||
t.Run("zero length usage", func(t *testing.T) { defer checkRecover(t, "New", wantPanic); c.New("name", "") })
|
defer checkRecover(t, "New", wantPanic)
|
||||||
|
c.New("", "usage")
|
||||||
|
})
|
||||||
|
t.Run("zero length usage", func(t *testing.T) {
|
||||||
|
defer checkRecover(t, "New", wantPanic)
|
||||||
|
c.New("name", "")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("direct adopt unique names", func(t *testing.T) {
|
t.Run("direct adopt unique names", func(t *testing.T) {
|
||||||
|
|||||||
+26
-6
@@ -6,36 +6,43 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UsageInternal causes the command to be hidden from help text when set as the usage string.
|
// UsageInternal is a special usage string that hides the command from the
|
||||||
const UsageInternal = "internal"
|
// generated help message.
|
||||||
|
const UsageInternal = "\x00"
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// HandlerFunc is called when matching a directly handled subcommand tree.
|
// HandlerFunc is called when matching a directly handled subcommand tree.
|
||||||
HandlerFunc = func(args []string) error
|
HandlerFunc = func(args []string) error
|
||||||
|
|
||||||
// LogFunc is the function signature of a printf function.
|
// LogFunc is the function signature of a printf function. The zero value
|
||||||
|
// implies [log.Printf].
|
||||||
LogFunc = func(format string, a ...any)
|
LogFunc = func(format string, a ...any)
|
||||||
|
|
||||||
// FlagDefiner is a deferred flag definer value, usually encapsulating the default value.
|
// FlagDefiner is a deferred flag definer value, usually encapsulating the
|
||||||
|
// default value.
|
||||||
FlagDefiner interface {
|
FlagDefiner interface {
|
||||||
// Define defines the flag in set.
|
// Define defines the flag in set.
|
||||||
Define(b *strings.Builder, set *flag.FlagSet, p any, name, usage string)
|
Define(b *strings.Builder, set *flag.FlagSet, p any, name, usage string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A Flag is satisfied by command objects capable of receiving flags.
|
||||||
Flag[T any] interface {
|
Flag[T any] interface {
|
||||||
// Flag defines a generic flag type in Node's flag set.
|
// Flag defines a generic flag type in Node's flag set.
|
||||||
Flag(p any, name string, value FlagDefiner, usage string) T
|
Flag(p any, name string, value FlagDefiner, usage string) T
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A Command is the root of a command tree.
|
||||||
Command interface {
|
Command interface {
|
||||||
Parse(arguments []string) error
|
Parse(arguments []string) error
|
||||||
|
|
||||||
// MustParse determines exit outcomes for Parse errors
|
// MustParse determines exit outcomes for Parse errors and calls
|
||||||
// and calls handleError if [HandlerFunc] returns a non-nil error.
|
// handleError if [HandlerFunc] returns a non-nil error.
|
||||||
MustParse(arguments []string, handleError func(error))
|
MustParse(arguments []string, handleError func(error))
|
||||||
|
|
||||||
baseNode[Command]
|
baseNode[Command]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A Node is a subcommand under a [Command].
|
||||||
Node baseNode[Node]
|
Node baseNode[Node]
|
||||||
|
|
||||||
baseNode[T any] interface {
|
baseNode[T any] interface {
|
||||||
@@ -53,3 +60,16 @@ type (
|
|||||||
Flag[T]
|
Flag[T]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// rootNode satisfies baseNode for [Command].
|
||||||
|
type rootNode struct{ *node }
|
||||||
|
|
||||||
|
func (r rootNode) Command(name, usage string, f HandlerFunc) Command {
|
||||||
|
r.node.Command(name, usage, f)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r rootNode) Flag(p any, name string, value FlagDefiner, usage string) Command {
|
||||||
|
r.node.Flag(p, name, value, usage)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// A node represents a command.
|
||||||
type node struct {
|
type node struct {
|
||||||
child, next *node
|
child, next *node
|
||||||
name, usage string
|
name, usage string
|
||||||
@@ -13,13 +14,16 @@ type node struct {
|
|||||||
out io.Writer
|
out io.Writer
|
||||||
logf LogFunc
|
logf LogFunc
|
||||||
|
|
||||||
|
// Names of commands preceding node.
|
||||||
prefix []string
|
prefix []string
|
||||||
|
// Short user-facing representations of flags received by node.
|
||||||
suffix strings.Builder
|
suffix strings.Builder
|
||||||
|
|
||||||
f HandlerFunc
|
f HandlerFunc
|
||||||
set *flag.FlagSet
|
set *flag.FlagSet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// adopt adds v as the last child of n.
|
||||||
func (n *node) adopt(v *node) bool {
|
func (n *node) adopt(v *node) bool {
|
||||||
if n.child != nil {
|
if n.child != nil {
|
||||||
return n.child.append(v)
|
return n.child.append(v)
|
||||||
@@ -28,6 +32,7 @@ func (n *node) adopt(v *node) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// append adds v as the last sibling of n.
|
||||||
func (n *node) append(v *node) bool {
|
func (n *node) append(v *node) bool {
|
||||||
if n.name == v.name {
|
if n.name == v.name {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
package command
|
|
||||||
|
|
||||||
// the top level node wants [Command] returned for its builder methods
|
|
||||||
type rootNode struct{ *node }
|
|
||||||
|
|
||||||
func (r rootNode) Command(name, usage string, f HandlerFunc) Command {
|
|
||||||
r.node.Command(name, usage, f)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r rootNode) Flag(p any, name string, value FlagDefiner, usage string) Command {
|
|
||||||
r.node.Flag(p, name, value, usage)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user