106 lines
2.0 KiB
Go
106 lines
2.0 KiB
Go
package command
|
|
|
|
import (
|
|
"errors"
|
|
"log"
|
|
"os"
|
|
)
|
|
|
|
var (
|
|
ErrEmptyTree = errors.New("subcommand tree has no nodes")
|
|
ErrNoMatch = errors.New("did not match any subcommand")
|
|
)
|
|
|
|
func (n *node) Parse(arguments []string) error {
|
|
if n.usage == "" { // root node has zero length usage
|
|
if n.next != nil {
|
|
panic("invalid toplevel state")
|
|
}
|
|
goto match
|
|
}
|
|
|
|
if len(arguments) == 0 {
|
|
// unreachable: zero length args cause upper level to return with a help message
|
|
panic("attempted to parse with zero length args")
|
|
}
|
|
if arguments[0] != n.name {
|
|
if n.next == nil {
|
|
n.printf("%q is not a valid command", arguments[0])
|
|
return ErrNoMatch
|
|
}
|
|
n.next.prefix = n.prefix
|
|
return n.next.Parse(arguments)
|
|
}
|
|
arguments = arguments[1:]
|
|
|
|
match:
|
|
if n.child != nil {
|
|
// propagate help prefix early: flag set usage dereferences help
|
|
n.child.prefix = append(n.prefix, n.name)
|
|
}
|
|
|
|
if n.set.Parsed() {
|
|
panic("invalid set state")
|
|
}
|
|
if err := n.set.Parse(arguments); err != nil {
|
|
return FlagError{err}
|
|
}
|
|
args := n.set.Args()
|
|
|
|
if n.child != nil {
|
|
if n.f != nil {
|
|
if n.usage != "" { // root node early special case
|
|
panic("invalid subcommand tree state")
|
|
}
|
|
|
|
// special case: root node calls HandlerFunc for initialisation
|
|
if err := n.f(nil); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if len(args) == 0 {
|
|
return n.writeHelp()
|
|
}
|
|
return n.child.Parse(args)
|
|
}
|
|
|
|
if n.f == nil {
|
|
n.printf("%q has no subcommands", n.name)
|
|
return ErrEmptyTree
|
|
}
|
|
return n.f(args)
|
|
}
|
|
|
|
func (n *node) printf(format string, a ...any) {
|
|
if n.logf == nil {
|
|
log.Printf(format, a...)
|
|
} else {
|
|
n.logf(format, a...)
|
|
}
|
|
}
|
|
|
|
func (n *node) MustParse(arguments []string, handleError func(error)) {
|
|
switch err := n.Parse(arguments); err {
|
|
case nil:
|
|
return
|
|
case ErrHelp:
|
|
os.Exit(0)
|
|
case ErrNoMatch:
|
|
os.Exit(1)
|
|
case ErrEmptyTree:
|
|
os.Exit(1)
|
|
default:
|
|
var flagError FlagError
|
|
if !errors.As(err, &flagError) { // returned by HandlerFunc
|
|
handleError(err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
if flagError.Success() {
|
|
os.Exit(0)
|
|
}
|
|
os.Exit(1)
|
|
}
|
|
}
|