Skip to content

Commit d5947d7

Browse files
committed
Initial cut
1 parent f37b9d9 commit d5947d7

File tree

8 files changed

+244
-380
lines changed

8 files changed

+244
-380
lines changed

app.go

Lines changed: 32 additions & 244 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"io"
88
"os"
99
"path/filepath"
10-
"reflect"
1110
"sort"
1211
"time"
1312
)
@@ -112,6 +111,8 @@ type App struct {
112111
Suggest bool
113112

114113
didSetup bool
114+
115+
rootCommand *Command
115116
}
116117

117118
type SuggestFlagFunc func(flags []Flag, provided string, hideHelp bool) string
@@ -268,136 +269,35 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) {
268269
// always appends the completion flag at the end of the command
269270
shellComplete, arguments := checkShellCompleteFlag(a, arguments)
270271

271-
set, err := a.newFlagSet()
272-
if err != nil {
273-
return err
274-
}
275-
276-
err = parseIter(set, a, arguments[1:], shellComplete)
277-
nerr := normalizeFlags(a.Flags, set)
278-
cCtx := NewContext(a, set, &Context{Context: ctx})
279-
if nerr != nil {
280-
_, _ = fmt.Fprintln(a.Writer, nerr)
281-
if !a.HideHelp {
282-
_ = ShowAppHelp(cCtx)
283-
}
284-
return nerr
285-
}
272+
cCtx := NewContext(a, nil, &Context{Context: ctx})
286273
cCtx.shellComplete = shellComplete
287274

288-
if checkCompletions(cCtx) {
289-
return nil
290-
}
291-
292-
if err != nil {
293-
if a.OnUsageError != nil {
294-
err := a.OnUsageError(cCtx, err, false)
295-
a.handleExitCoder(cCtx, err)
296-
return err
297-
}
298-
_, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
299-
if a.Suggest {
300-
if suggestion, err := a.suggestFlagFromError(err, ""); err == nil {
301-
fmt.Fprintf(a.Writer, suggestion)
302-
}
303-
}
304-
if !a.HideHelp {
305-
_ = ShowAppHelp(cCtx)
306-
}
307-
return err
308-
}
309-
310-
if a.After != nil && !cCtx.shellComplete {
311-
defer func() {
312-
if afterErr := a.After(cCtx); afterErr != nil {
313-
if err != nil {
314-
err = newMultiError(err, afterErr)
315-
} else {
316-
err = afterErr
317-
}
318-
}
319-
}()
320-
}
321-
322-
if !a.HideHelp && checkHelp(cCtx) {
323-
_ = ShowAppHelp(cCtx)
324-
return nil
325-
}
326-
327-
if !a.HideVersion && checkVersion(cCtx) {
328-
ShowVersion(cCtx)
329-
return nil
330-
}
331-
332-
cerr := cCtx.checkRequiredFlags(a.Flags)
333-
if cerr != nil {
334-
_ = ShowAppHelp(cCtx)
335-
return cerr
336-
}
337-
338-
if a.Before != nil && !cCtx.shellComplete {
339-
beforeErr := a.Before(cCtx)
340-
if beforeErr != nil {
341-
a.handleExitCoder(cCtx, beforeErr)
342-
err = beforeErr
343-
return err
344-
}
345-
}
346-
347-
if err = runFlagActions(cCtx, a.Flags); err != nil {
348-
return err
349-
}
350-
351-
var c *Command
352-
args := cCtx.Args()
353-
if args.Present() {
354-
name := args.First()
355-
if a.validCommandName(name) {
356-
c = a.Command(name)
357-
} else {
358-
hasDefault := a.DefaultCommand != ""
359-
isFlagName := checkStringSliceIncludes(name, cCtx.FlagNames())
360-
361-
var (
362-
isDefaultSubcommand = false
363-
defaultHasSubcommands = false
364-
)
365-
366-
if hasDefault {
367-
dc := a.Command(a.DefaultCommand)
368-
defaultHasSubcommands = len(dc.Subcommands) > 0
369-
for _, dcSub := range dc.Subcommands {
370-
if checkStringSliceIncludes(name, dcSub.Names()) {
371-
isDefaultSubcommand = true
372-
break
373-
}
374-
}
375-
}
376-
377-
if isFlagName || (hasDefault && (defaultHasSubcommands && isDefaultSubcommand)) {
378-
argsWithDefault := a.argsWithDefaultCommand(args)
379-
if !reflect.DeepEqual(args, argsWithDefault) {
380-
c = a.Command(argsWithDefault.First())
381-
}
382-
}
383-
}
384-
} else if a.DefaultCommand != "" {
385-
c = a.Command(a.DefaultCommand)
386-
}
387-
388-
if c != nil {
389-
return c.Run(cCtx)
390-
}
391-
392-
if a.Action == nil {
393-
a.Action = helpCommand.Action
394-
}
395-
396-
// Run default Action
397-
err = a.Action(cCtx)
398-
399-
a.handleExitCoder(cCtx, err)
400-
return err
275+
a.rootCommand = &Command{
276+
HelpName: a.HelpName,
277+
Subcommands: a.Commands,
278+
flagCategories: a.flagCategories,
279+
Flags: a.Flags,
280+
Name: a.Name,
281+
//Action: a.Action, // dont set this now
282+
UseShortOptionHandling: a.UseShortOptionHandling,
283+
Before: a.Before,
284+
After: a.After,
285+
HideHelp: a.HideHelp,
286+
HideHelpCommand: a.HideHelpCommand,
287+
OnUsageError: a.OnUsageError,
288+
CustomHelpTemplate: a.CustomAppHelpTemplate,
289+
Usage: a.Usage,
290+
UsageText: a.UsageText,
291+
Description: a.Description,
292+
ArgsUsage: a.ArgsUsage,
293+
BashComplete: a.BashComplete,
294+
categories: a.categories,
295+
helpAction: helpCommand.Action,
296+
isRoot: true,
297+
}
298+
cCtx.Command = a.rootCommand
299+
300+
return a.rootCommand.Run(cCtx, arguments)
401301
}
402302

403303
func (a *App) suggestFlagFromError(err error, command string) (string, error) {
@@ -407,15 +307,17 @@ func (a *App) suggestFlagFromError(err error, command string) (string, error) {
407307
}
408308

409309
flags := a.Flags
310+
hideHelp := a.HideHelp
410311
if command != "" {
411312
cmd := a.Command(command)
412313
if cmd == nil {
413314
return "", err
414315
}
415316
flags = cmd.Flags
317+
hideHelp = hideHelp || cmd.HideHelp
416318
}
417319

418-
suggestion := SuggestFlag(flags, flag, a.HideHelp)
320+
suggestion := SuggestFlag(flags, flag, hideHelp)
419321
if len(suggestion) == 0 {
420322
return "", err
421323
}
@@ -435,120 +337,6 @@ func (a *App) RunAndExitOnError() {
435337
}
436338
}
437339

438-
// RunAsSubcommand invokes the subcommand given the context, parses ctx.Args() to
439-
// generate command-specific flags
440-
func (a *App) RunAsSubcommand(ctx *Context) (err error) {
441-
// Setup also handles HideHelp and HideHelpCommand
442-
a.Setup()
443-
444-
var newCmds []*Command
445-
for _, c := range a.Commands {
446-
if c.HelpName == "" {
447-
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
448-
}
449-
newCmds = append(newCmds, c)
450-
}
451-
a.Commands = newCmds
452-
453-
set, err := a.newFlagSet()
454-
if err != nil {
455-
return err
456-
}
457-
458-
err = parseIter(set, a, ctx.Args().Tail(), ctx.shellComplete)
459-
nerr := normalizeFlags(a.Flags, set)
460-
cCtx := NewContext(a, set, ctx)
461-
462-
if nerr != nil {
463-
_, _ = fmt.Fprintln(a.Writer, nerr)
464-
_, _ = fmt.Fprintln(a.Writer)
465-
if len(a.Commands) > 0 {
466-
_ = ShowSubcommandHelp(cCtx)
467-
} else {
468-
_ = ShowCommandHelp(ctx, cCtx.Args().First())
469-
}
470-
return nerr
471-
}
472-
473-
if checkCompletions(cCtx) {
474-
return nil
475-
}
476-
477-
if err != nil {
478-
if a.OnUsageError != nil {
479-
err = a.OnUsageError(cCtx, err, true)
480-
a.handleExitCoder(cCtx, err)
481-
return err
482-
}
483-
_, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
484-
if a.Suggest {
485-
if suggestion, err := a.suggestFlagFromError(err, cCtx.Command.Name); err == nil {
486-
fmt.Fprintf(a.Writer, suggestion)
487-
}
488-
}
489-
_ = ShowSubcommandHelp(cCtx)
490-
return err
491-
}
492-
493-
if len(a.Commands) > 0 {
494-
if checkSubcommandHelp(cCtx) {
495-
return nil
496-
}
497-
} else {
498-
if checkCommandHelp(ctx, cCtx.Args().First()) {
499-
return nil
500-
}
501-
}
502-
503-
cerr := cCtx.checkRequiredFlags(a.Flags)
504-
if cerr != nil {
505-
_ = ShowSubcommandHelp(cCtx)
506-
return cerr
507-
}
508-
509-
if a.After != nil && !cCtx.shellComplete {
510-
defer func() {
511-
afterErr := a.After(cCtx)
512-
if afterErr != nil {
513-
a.handleExitCoder(cCtx, err)
514-
if err != nil {
515-
err = newMultiError(err, afterErr)
516-
} else {
517-
err = afterErr
518-
}
519-
}
520-
}()
521-
}
522-
523-
if a.Before != nil && !cCtx.shellComplete {
524-
beforeErr := a.Before(cCtx)
525-
if beforeErr != nil {
526-
a.handleExitCoder(cCtx, beforeErr)
527-
err = beforeErr
528-
return err
529-
}
530-
}
531-
532-
if err = runFlagActions(cCtx, a.Flags); err != nil {
533-
return err
534-
}
535-
536-
args := cCtx.Args()
537-
if args.Present() {
538-
name := args.First()
539-
c := a.Command(name)
540-
if c != nil {
541-
return c.Run(cCtx)
542-
}
543-
}
544-
545-
// Run default Action
546-
err = a.Action(cCtx)
547-
548-
a.handleExitCoder(cCtx, err)
549-
return err
550-
}
551-
552340
// Command returns the named command on App. Returns nil if the command does not exist
553341
func (a *App) Command(name string) *Command {
554342
for _, c := range a.Commands {

app_test.go

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -683,24 +683,6 @@ func TestApp_RunAsSubcommandParseFlags(t *testing.T) {
683683
expect(t, cCtx.String("lang"), "spanish")
684684
}
685685

686-
func TestApp_RunAsSubCommandIncorrectUsage(t *testing.T) {
687-
a := App{
688-
Name: "cmd",
689-
Flags: []Flag{
690-
&StringFlag{Name: "foo"},
691-
},
692-
Writer: bytes.NewBufferString(""),
693-
}
694-
695-
set := flag.NewFlagSet("", flag.ContinueOnError)
696-
_ = set.Parse([]string{"", "-bar"})
697-
c := &Context{flagSet: set}
698-
699-
err := a.RunAsSubcommand(c)
700-
701-
expect(t, err.Error(), "flag provided but not defined: -bar")
702-
}
703-
704686
func TestApp_CommandWithFlagBeforeTerminator(t *testing.T) {
705687
var parsedOption string
706688
var args Args
@@ -1547,6 +1529,9 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) {
15471529
Subcommands: []*Command{{
15481530
Name: "mySubCommand",
15491531
Flags: []Flag{&StringFlag{Name: "requiredFlag", Required: true}},
1532+
Action: func(c *Context) error {
1533+
return nil
1534+
},
15501535
}},
15511536
}},
15521537
},
@@ -1916,7 +1901,6 @@ func TestApp_Run_CommandHelpName(t *testing.T) {
19161901
}
19171902
cmd := &Command{
19181903
Name: "foo",
1919-
HelpName: "custom",
19201904
Description: "foo commands",
19211905
Subcommands: []*Command{subCmd},
19221906
}
@@ -2036,7 +2020,7 @@ func TestApp_Run_Help(t *testing.T) {
20362020
}
20372021

20382022
err := app.Run(tt.helpArguments)
2039-
if err != nil && err.Error() != tt.wantErr.Error() {
2023+
if err != nil && tt.wantErr != nil && err.Error() != tt.wantErr.Error() {
20402024
t.Errorf("want err: %s, did note %s\n", tt.wantErr, err)
20412025
}
20422026

0 commit comments

Comments
 (0)