Skip to content

Commit

Permalink
Initial cut
Browse files Browse the repository at this point in the history
  • Loading branch information
dearchap committed Sep 20, 2022
1 parent 880a802 commit c4780b8
Show file tree
Hide file tree
Showing 8 changed files with 282 additions and 375 deletions.
268 changes: 32 additions & 236 deletions app.go
Expand Up @@ -7,7 +7,6 @@ import (
"io"
"os"
"path/filepath"
"reflect"
"sort"
"time"
)
Expand Down Expand Up @@ -112,6 +111,8 @@ type App struct {
Suggest bool

didSetup bool

rootCommand *Command
}

type SuggestFlagFunc func(flags []Flag, provided string, hideHelp bool) string
Expand Down Expand Up @@ -266,132 +267,35 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) {
// always appends the completion flag at the end of the command
shellComplete, arguments := checkShellCompleteFlag(a, arguments)

set, err := a.newFlagSet()
if err != nil {
return err
}

err = parseIter(set, a, arguments[1:], shellComplete)
nerr := normalizeFlags(a.Flags, set)
cCtx := NewContext(a, set, &Context{Context: ctx})
if nerr != nil {
_, _ = fmt.Fprintln(a.Writer, nerr)
if !a.HideHelp {
_ = ShowAppHelp(cCtx)
}
return nerr
}
cCtx := NewContext(a, nil, &Context{Context: ctx})
cCtx.shellComplete = shellComplete

if checkCompletions(cCtx) {
return nil
}

if err != nil {
if a.OnUsageError != nil {
err := a.OnUsageError(cCtx, err, false)
a.handleExitCoder(cCtx, err)
return err
}
_, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
if a.Suggest {
if suggestion, err := a.suggestFlagFromError(err, ""); err == nil {
fmt.Fprintf(a.Writer, suggestion)
}
}
if !a.HideHelp {
_ = ShowAppHelp(cCtx)
}
return err
}

if a.After != nil && !cCtx.shellComplete {
defer func() {
if afterErr := a.After(cCtx); afterErr != nil {
if err != nil {
err = newMultiError(err, afterErr)
} else {
err = afterErr
}
}
}()
}

if !a.HideHelp && checkHelp(cCtx) {
_ = ShowAppHelp(cCtx)
return nil
}

if !a.HideVersion && checkVersion(cCtx) {
ShowVersion(cCtx)
return nil
}

cerr := cCtx.checkRequiredFlags(a.Flags)
if cerr != nil {
_ = ShowAppHelp(cCtx)
return cerr
}

if a.Before != nil && !cCtx.shellComplete {
beforeErr := a.Before(cCtx)
if beforeErr != nil {
a.handleExitCoder(cCtx, beforeErr)
err = beforeErr
return err
}
}

var c *Command
args := cCtx.Args()
if args.Present() {
name := args.First()
if a.validCommandName(name) {
c = a.Command(name)
} else {
hasDefault := a.DefaultCommand != ""
isFlagName := checkStringSliceIncludes(name, cCtx.FlagNames())

var (
isDefaultSubcommand = false
defaultHasSubcommands = false
)

if hasDefault {
dc := a.Command(a.DefaultCommand)
defaultHasSubcommands = len(dc.Subcommands) > 0
for _, dcSub := range dc.Subcommands {
if checkStringSliceIncludes(name, dcSub.Names()) {
isDefaultSubcommand = true
break
}
}
}

if isFlagName || (hasDefault && (defaultHasSubcommands && isDefaultSubcommand)) {
argsWithDefault := a.argsWithDefaultCommand(args)
if !reflect.DeepEqual(args, argsWithDefault) {
c = a.Command(argsWithDefault.First())
}
}
}
} else if a.DefaultCommand != "" {
c = a.Command(a.DefaultCommand)
}

if c != nil {
return c.Run(cCtx)
}

if a.Action == nil {
a.Action = helpCommand.Action
}

// Run default Action
err = a.Action(cCtx)

a.handleExitCoder(cCtx, err)
return err
a.rootCommand = &Command{
HelpName: a.HelpName,
Subcommands: a.Commands,
flagCategories: a.flagCategories,
Flags: a.Flags,
Name: a.Name,
//Action: a.Action, // dont set this now
UseShortOptionHandling: a.UseShortOptionHandling,
Before: a.Before,
After: a.After,
HideHelp: a.HideHelp,
HideHelpCommand: a.HideHelpCommand,
OnUsageError: a.OnUsageError,
CustomHelpTemplate: a.CustomAppHelpTemplate,
Usage: a.Usage,
UsageText: a.UsageText,
Description: a.Description,
ArgsUsage: a.ArgsUsage,
BashComplete: a.BashComplete,
categories: a.categories,
helpAction: helpCommand.Action,
isRoot: true,
}
cCtx.Command = a.rootCommand

return a.rootCommand.Run(cCtx, arguments)
}

func (a *App) suggestFlagFromError(err error, command string) (string, error) {
Expand All @@ -401,15 +305,17 @@ func (a *App) suggestFlagFromError(err error, command string) (string, error) {
}

flags := a.Flags
hideHelp := a.HideHelp
if command != "" {
cmd := a.Command(command)
if cmd == nil {
return "", err
}
flags = cmd.Flags
hideHelp = hideHelp || cmd.HideHelp
}

suggestion := SuggestFlag(flags, flag, a.HideHelp)
suggestion := SuggestFlag(flags, flag, hideHelp)
if len(suggestion) == 0 {
return "", err
}
Expand All @@ -429,116 +335,6 @@ func (a *App) RunAndExitOnError() {
}
}

// RunAsSubcommand invokes the subcommand given the context, parses ctx.Args() to
// generate command-specific flags
func (a *App) RunAsSubcommand(ctx *Context) (err error) {
// Setup also handles HideHelp and HideHelpCommand
a.Setup()

var newCmds []*Command
for _, c := range a.Commands {
if c.HelpName == "" {
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
}
newCmds = append(newCmds, c)
}
a.Commands = newCmds

set, err := a.newFlagSet()
if err != nil {
return err
}

err = parseIter(set, a, ctx.Args().Tail(), ctx.shellComplete)
nerr := normalizeFlags(a.Flags, set)
cCtx := NewContext(a, set, ctx)

if nerr != nil {
_, _ = fmt.Fprintln(a.Writer, nerr)
_, _ = fmt.Fprintln(a.Writer)
if len(a.Commands) > 0 {
_ = ShowSubcommandHelp(cCtx)
} else {
_ = ShowCommandHelp(ctx, cCtx.Args().First())
}
return nerr
}

if checkCompletions(cCtx) {
return nil
}

if err != nil {
if a.OnUsageError != nil {
err = a.OnUsageError(cCtx, err, true)
a.handleExitCoder(cCtx, err)
return err
}
_, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
if a.Suggest {
if suggestion, err := a.suggestFlagFromError(err, cCtx.Command.Name); err == nil {
fmt.Fprintf(a.Writer, suggestion)
}
}
_ = ShowSubcommandHelp(cCtx)
return err
}

if len(a.Commands) > 0 {
if checkSubcommandHelp(cCtx) {
return nil
}
} else {
if checkCommandHelp(ctx, cCtx.Args().First()) {
return nil
}
}

cerr := cCtx.checkRequiredFlags(a.Flags)
if cerr != nil {
_ = ShowSubcommandHelp(cCtx)
return cerr
}

if a.After != nil && !cCtx.shellComplete {
defer func() {
afterErr := a.After(cCtx)
if afterErr != nil {
a.handleExitCoder(cCtx, err)
if err != nil {
err = newMultiError(err, afterErr)
} else {
err = afterErr
}
}
}()
}

if a.Before != nil && !cCtx.shellComplete {
beforeErr := a.Before(cCtx)
if beforeErr != nil {
a.handleExitCoder(cCtx, beforeErr)
err = beforeErr
return err
}
}

args := cCtx.Args()
if args.Present() {
name := args.First()
c := a.Command(name)
if c != nil {
return c.Run(cCtx)
}
}

// Run default Action
err = a.Action(cCtx)

a.handleExitCoder(cCtx, err)
return err
}

// Command returns the named command on App. Returns nil if the command does not exist
func (a *App) Command(name string) *Command {
for _, c := range a.Commands {
Expand Down
24 changes: 4 additions & 20 deletions app_test.go
Expand Up @@ -678,24 +678,6 @@ func TestApp_RunAsSubcommandParseFlags(t *testing.T) {
expect(t, cCtx.String("lang"), "spanish")
}

func TestApp_RunAsSubCommandIncorrectUsage(t *testing.T) {
a := App{
Name: "cmd",
Flags: []Flag{
&StringFlag{Name: "foo"},
},
Writer: bytes.NewBufferString(""),
}

set := flag.NewFlagSet("", flag.ContinueOnError)
_ = set.Parse([]string{"", "-bar"})
c := &Context{flagSet: set}

err := a.RunAsSubcommand(c)

expect(t, err.Error(), "flag provided but not defined: -bar")
}

func TestApp_CommandWithFlagBeforeTerminator(t *testing.T) {
var parsedOption string
var args Args
Expand Down Expand Up @@ -1511,6 +1493,9 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) {
Subcommands: []*Command{{
Name: "mySubCommand",
Flags: []Flag{&StringFlag{Name: "requiredFlag", Required: true}},
Action: func(c *Context) error {
return nil
},
}},
}},
},
Expand Down Expand Up @@ -1880,7 +1865,6 @@ func TestApp_Run_CommandHelpName(t *testing.T) {
}
cmd := &Command{
Name: "foo",
HelpName: "custom",
Description: "foo commands",
Subcommands: []*Command{subCmd},
}
Expand Down Expand Up @@ -2000,7 +1984,7 @@ func TestApp_Run_Help(t *testing.T) {
}

err := app.Run(tt.helpArguments)
if err != nil && err.Error() != tt.wantErr.Error() {
if err != nil && tt.wantErr != nil && err.Error() != tt.wantErr.Error() {
t.Errorf("want err: %s, did note %s\n", tt.wantErr, err)
}

Expand Down

0 comments on commit c4780b8

Please sign in to comment.