Factor all errors into custom errors.go

pull/85/head
rwxrob 2 years ago
parent c197dfba66
commit 2501602891
No known key found for this signature in database
GPG Key ID: 2B9111F33082AE77

@ -60,9 +60,12 @@ var ExePath string
// (ex: .exe) and is set at init() time (see ExePath).
var ExeName string
// Commands contains the commands to lookup when Run-ing an executable
// in "multicall" mode. Each value must begin with a *Cmd and the rest
// will be assumed to be string arguments to prepend. See Run.
// Commands contains the Cmds to lookup when executing with Z.Run in
// "multicall" mode. Each value of the slice keyed to the name must
// begin with a *Cmd and the rest will be assumed to be string arguments
// to prepend. This allows the table of Commands to not only associate
// with a specific Cmd, but to also provide different arguments to that
// command. The name Commands is similar to Cmd.Commands. See Run.
var Commands map[string][]any
// Comp may be optionally assigned any implementation of
@ -167,21 +170,29 @@ func InferredUsage(cmd bonzai.Command) string {
}
// Run infers the name of the command to run from the ExeName looked up
// in the Commands delegates accordingly, prepending any arguments
// provided in the Cmd.Run. Run produces an "unmapped multicall command"
// error if no match is found. This is an alternative to the simpler,
// direct Cmd.Run method from main where only one possible Cmd will ever
// be the root and allows for BusyBox (https://www.busybox.net)
// in the Commands and delegates accordingly, prepending any arguments
// provided. This allows for BusyBox-like (https://www.busybox.net)
// multicall binaries to be used for such things as very light-weight
// Linux distributions when used "FROM SCRATCH" in containers.
// Linux distributions when used "FROM SCRATCH" in containers. Although
// it shares the same name Z.Run should not confused with Cmd.Run. In
// general, Z.Run is for "multicall" and Cmd.Run is for "monoliths".
// Run may exit with the following errors:
//
// * MultiCallCmdNotFound
// * MultiCallCmdNotCmd
// * MultiCallCmdNotCmd
// * MultiCallCmdArgNotString
//
func Run() {
if v, has := Commands[ExeName]; has {
if len(v) < 1 {
ExitError(fmt.Errorf("multicall command missing"))
ExitError(MultiCallCmdNotFound{ExeName})
return
}
cmd, iscmd := v[0].(*Cmd)
if !iscmd {
ExitError(fmt.Errorf("first value must be *Cmd"))
ExitError(MultiCallCmdNotCmd{ExeName, v[0]})
return
}
args := []string{cmd.Name}
if len(v) > 1 {
@ -189,7 +200,8 @@ func Run() {
for _, a := range v[1:] {
s, isstring := a.(string)
if !isstring {
ExitError(fmt.Errorf("only string arguments allowed"))
ExitError(MultiCallCmdArgNotString{ExeName, a})
return
}
args = append(args, s)
}
@ -198,8 +210,9 @@ func Run() {
os.Args = args
cmd.Run()
Exit()
return
}
ExitError(fmt.Errorf("unmapped multicall command: %v", ExeName))
ExitError(MultiCallCmdNotFound{ExeName})
}
// Method defines the main code to execute for a command (Cmd). By

@ -292,25 +292,28 @@ func (x *Cmd) Run() {
if len(cmd.Commands) > 0 {
fcmd := cmd.Commands[0]
if fcmd.Call == nil {
ExitError(fmt.Errorf("default commands require Call function"))
ExitError(DefCmdReqCall{cmd})
return
}
fcmd.Caller = cmd
cmd = fcmd
} else {
ExitError(x.Unimplemented())
ExitError(NoCallNoCommands{cmd})
return
}
}
if len(args) < cmd.MinArgs {
ExitError(cmd.UsageError())
}
if x.ReqConf && Conf == nil {
ExitError(cmd.ReqConfError())
}
if x.ReqVars && Vars == nil {
ExitError(cmd.ReqVarsError())
switch {
case len(args) < cmd.MinArgs:
ExitError(NotEnoughArgs{cmd})
case cmd.MaxArgs > 0 && len(args) > cmd.MaxArgs:
ExitError(TooManyArgs{cmd})
case cmd.NumArgs > 0 && len(args) != cmd.NumArgs:
ExitError(WrongNumArgs{cmd})
case cmd.ReqConf && Conf == nil:
ExitError(ConfRequired{cmd})
case cmd.ReqVars && Vars == nil:
ExitError(VarsRequired{cmd})
}
// delegate
@ -334,40 +337,6 @@ func (x *Cmd) Root() *Cmd {
return x.Caller
}
// UsageError returns an error with a single-line usage string.
func (x *Cmd) UsageError() error {
return fmt.Errorf("usage: %v %v", x.Name, UsageFunc(x))
}
// ReqConfError returns stating that the given command requires that
// Z.Conf be set to something besides null.
func (x *Cmd) ReqConfError() error {
return fmt.Errorf(
"cmd %q requires a configurer (Z.Conf must be assigned)",
x.Name,
)
}
// ReqVarsError returns stating that the given command requires that
// Z.Vars be set to something besides null.
func (x *Cmd) ReqVarsError() error {
return fmt.Errorf(
"cmd %q requires cached variables (Z.Vars must be assigned)",
x.Name,
)
}
// Unimplemented returns an error with a single-line usage string.
func (x *Cmd) Unimplemented() error {
return fmt.Errorf("%q has not yet been implemented", x.Name)
}
// MissingConfig returns an error showing the expected configuration
// entry that is missing from the given path.
func (x *Cmd) MissingConfig(path string) error {
return fmt.Errorf("missing config: %v", x.Path()+"."+path)
}
// Add creates a new Cmd and sets the name and aliases and adds to
// Commands returning a reference to the new Cmd. The name must be
// first.
@ -563,8 +532,7 @@ func (x *Cmd) Get(key string) string {
// convenience. Logs the error Z.Vars is not defined (see ReqVars).
func (x *Cmd) Set(key, val string) error {
if Vars == nil {
return fmt.Errorf(
"cmd %q requires cached vars (Z.Vars must be assigned)", x.Name)
return VarsRequired{x}
}
path := x.Path()
if path != "." {

@ -190,7 +190,7 @@ func ExampleCmd_UsageError_commands_with_Aliases() {
&Z.Cmd{Name: "bar"},
},
}
fmt.Println(x.UsageError())
fmt.Println(Z.IncorrectUsage{x})
// Output:
// usage: cmd ((f|foo)|bar)
@ -205,7 +205,7 @@ func ExampleCmd_UsageError_params_but_No_Call() {
&Z.Cmd{Name: "bar"},
},
}
fmt.Println(x.UsageError())
fmt.Println(Z.IncorrectUsage{x})
// Output:
// usage: cmd {ERROR: Params without Call: p1, p2}
}
@ -214,7 +214,7 @@ func ExampleCmd_UsageError_no_Call_nor_Commands() {
x := &Z.Cmd{
Name: `cmd`,
}
fmt.Println(x.UsageError())
fmt.Println(Z.IncorrectUsage{x})
// Output:
// usage: cmd {ERROR: neither Call nor Commands defined}

@ -0,0 +1,95 @@
package Z
import "fmt"
type NotEnoughArgs struct {
Cmd *Cmd
}
func (e NotEnoughArgs) Error() string {
return fmt.Sprintf("not enough args, %v required", e.Cmd.MinArgs)
}
type TooManyArgs struct {
Cmd *Cmd
}
func (e TooManyArgs) Error() string {
return fmt.Sprintf("too many args, %v maximum", e.Cmd.MaxArgs)
}
type WrongNumArgs struct {
Cmd *Cmd
}
func (e WrongNumArgs) Error() string {
return fmt.Sprintf("wrong number of args, %v required", e.Cmd.NumArgs)
}
type ConfRequired struct {
Cmd *Cmd
}
func (e ConfRequired) Error() string {
return fmt.Sprintf("%v requires Z.Conf", e.Cmd.Name)
}
type VarsRequired struct {
Cmd *Cmd
}
func (e VarsRequired) Error() string {
return fmt.Sprintf("%v requires Z.Vars", e.Cmd.Name)
}
type NoCallNoCommands struct {
Cmd *Cmd
}
func (e NoCallNoCommands) Error() string {
return fmt.Sprintf("%v requires either Call or Commands", e.Cmd.Name)
}
type DefCmdReqCall struct {
Cmd *Cmd
}
func (e DefCmdReqCall) Error() string {
return fmt.Sprintf("default (first) %q Commands requires Call", e.Cmd.Name)
}
type IncorrectUsage struct {
Cmd *Cmd
}
func (e IncorrectUsage) Error() string {
return fmt.Sprintf("usage: %v %v", e.Cmd.Name, UsageFunc(e.Cmd))
}
type MultiCallCmdNotFound struct {
CmdName string
}
func (e MultiCallCmdNotFound) Error() string {
return fmt.Sprintf("multicall command not found: %v", e.CmdName)
}
type MultiCallCmdNotCmd struct {
CmdName string
It any
}
func (e MultiCallCmdNotCmd) Error() string {
return fmt.Sprintf(
"multicall match for %v, but first in slice not *Z.Cmd: %T", e.CmdName, e.It)
}
type MultiCallCmdArgNotString struct {
CmdName string
It any
}
func (e MultiCallCmdArgNotString) Error() string {
return fmt.Sprintf(
"multicall match for %v, but arg not string: %T", e.CmdName, e.It)
}
Loading…
Cancel
Save