Subcommand structure should be correctly copied to main structure

main
Joe Richey 8 years ago
commit e109e81e6a

@ -455,13 +455,13 @@ error.
Flags for the application and commands are shown in the order they are defined.
However, it's possible to sort them from outside this library by using `FlagsByName`
with `sort`.
or `CommandsByName` with `sort`.
For example this:
<!-- {
"args": ["&#45;&#45;help"],
"output": "Load configuration from FILE\n.*Language for the greeting.*"
"output": "add a task to the list\n.*complete a task on the list\n.*\n\n.*\n.*Load configuration from FILE\n.*Language for the greeting.*"
} -->
``` go
package main
@ -488,7 +488,27 @@ func main() {
},
}
app.Commands = []cli.Command{
{
Name: "complete",
Aliases: []string{"c"},
Usage: "complete a task on the list",
Action: func(c *cli.Context) error {
return nil
},
},
{
Name: "add",
Aliases: []string{"a"},
Usage: "add a task to the list",
Action: func(c *cli.Context) error {
return nil
},
},
}
sort.Sort(cli.FlagsByName(app.Flags))
sort.Sort(cli.CommandsByName(app.Commands))
app.Run(os.Args)
}
@ -940,16 +960,13 @@ SUPPORT: support@awesometown.example.com
cli.AppHelpTemplate = `NAME:
{{.Name}} - {{.Usage}}
USAGE:
{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command
[command options]{{end}} {{if
.ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
{{if len .Authors}}
AUTHOR(S):
AUTHOR:
{{range .Authors}}{{ . }}{{end}}
{{end}}{{if .Commands}}
COMMANDS:
{{range .Commands}}{{if not .HideHelp}} {{join .Names ", "}}{{ "\t"
}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
{{range .Commands}}{{if not .HideHelp}} {{join .Names ", "}}{{ "\t"}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
GLOBAL OPTIONS:
{{range .VisibleFlags}}{{.}}
{{end}}{{end}}{{if .Copyright }}

@ -11,6 +11,8 @@ import (
"net/http"
"net/url"
"os"
"runtime"
"strings"
"gopkg.in/urfave/cli.v1"
@ -78,6 +80,12 @@ func loadDataFrom(filePath string) ([]byte, error) {
return nil, fmt.Errorf("Cannot read from file: '%s' because it does not exist.", filePath)
}
return ioutil.ReadFile(filePath)
} else if runtime.GOOS == "windows" && strings.Contains(u.String(), "\\") {
// on Windows systems u.Path is always empty, so we need to check the string directly.
if _, notFoundFileErr := os.Stat(filePath); notFoundFileErr != nil {
return nil, fmt.Errorf("Cannot read from file: '%s' because it does not exist.", filePath)
}
return ioutil.ReadFile(filePath)
} else {
return nil, fmt.Errorf("unable to determine how to load from path %s", filePath)
}

@ -61,6 +61,20 @@ type Command struct {
commandNamePath []string
}
type CommandsByName []Command
func (c CommandsByName) Len() int {
return len(c)
}
func (c CommandsByName) Less(i, j int) bool {
return c[i].Name < c[j].Name
}
func (c CommandsByName) Swap(i, j int) {
c[i], c[j] = c[j], c[i]
}
// FullName returns the full name of the command.
// For subcommands this ensures that parent commands are part of the command path
func (c Command) FullName() string {

@ -74,7 +74,7 @@ func (ee *ExitError) ExitCode() int {
// HandleExitCoder checks if the error fulfills the ExitCoder interface, and if
// so prints the error to stderr (if it is non-empty) and calls OsExiter with the
// given exit code. If the given error is a MultiError, then this func is
// called on all members of the Errors slice.
// called on all members of the Errors slice and calls OsExiter with the last exit code.
func HandleExitCoder(err error) {
if err == nil {
return
@ -93,9 +93,8 @@ func HandleExitCoder(err error) {
}
if multiErr, ok := err.(MultiError); ok {
for _, merr := range multiErr.Errors {
HandleExitCoder(merr)
}
code := handleMultiError(multiErr)
OsExiter(code)
return
}
@ -108,3 +107,18 @@ func HandleExitCoder(err error) {
}
OsExiter(1)
}
func handleMultiError(multiErr MultiError) int {
code := 1
for _, merr := range multiErr.Errors {
if multiErr2, ok := merr.(MultiError); ok {
code = handleMultiError(multiErr2)
} else {
fmt.Fprintln(ErrWriter, merr)
if exitErr, ok := merr.(ExitCoder); ok {
code = exitErr.ExitCode()
}
}
}
return code
}

@ -12,9 +12,11 @@ func TestHandleExitCoder_nil(t *testing.T) {
called := false
OsExiter = func(rc int) {
if !called {
exitCode = rc
called = true
}
}
defer func() { OsExiter = fakeOsExiter }()
@ -29,9 +31,11 @@ func TestHandleExitCoder_ExitCoder(t *testing.T) {
called := false
OsExiter = func(rc int) {
if !called {
exitCode = rc
called = true
}
}
defer func() { OsExiter = fakeOsExiter }()
@ -46,17 +50,20 @@ func TestHandleExitCoder_MultiErrorWithExitCoder(t *testing.T) {
called := false
OsExiter = func(rc int) {
if !called {
exitCode = rc
called = true
}
}
defer func() { OsExiter = fakeOsExiter }()
exitErr := NewExitError("galactic perimeter breach", 9)
err := NewMultiError(errors.New("wowsa"), errors.New("egad"), exitErr)
exitErr2 := NewExitError("last ExitCoder", 11)
err := NewMultiError(errors.New("wowsa"), errors.New("egad"), exitErr, exitErr2)
HandleExitCoder(err)
expect(t, exitCode, 9)
expect(t, exitCode, 11)
expect(t, called, true)
}
@ -65,9 +72,11 @@ func TestHandleExitCoder_ErrorWithMessage(t *testing.T) {
called := false
OsExiter = func(rc int) {
if !called {
exitCode = rc
called = true
}
}
ErrWriter = &bytes.Buffer{}
defer func() {
@ -88,9 +97,11 @@ func TestHandleExitCoder_ErrorWithoutMessage(t *testing.T) {
called := false
OsExiter = func(rc int) {
if !called {
exitCode = rc
called = true
}
}
ErrWriter = &bytes.Buffer{}
defer func() {
@ -123,8 +134,10 @@ func TestHandleExitCoder_ErrorWithFormat(t *testing.T) {
called := false
OsExiter = func(rc int) {
if !called {
called = true
}
}
ErrWriter = &bytes.Buffer{}
defer func() {
@ -143,8 +156,10 @@ func TestHandleExitCoder_MultiErrorWithFormat(t *testing.T) {
called := false
OsExiter = func(rc int) {
if !called {
called = true
}
}
ErrWriter = &bytes.Buffer{}
defer func() { OsExiter = fakeOsExiter }()

Loading…
Cancel
Save