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. 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` However, it's possible to sort them from outside this library by using `FlagsByName`
with `sort`. or `CommandsByName` with `sort`.
For example this: For example this:
<!-- { <!-- {
"args": ["&#45;&#45;help"], "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 ``` go
package main 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.FlagsByName(app.Flags))
sort.Sort(cli.CommandsByName(app.Commands))
app.Run(os.Args) app.Run(os.Args)
} }
@ -940,16 +960,13 @@ SUPPORT: support@awesometown.example.com
cli.AppHelpTemplate = `NAME: cli.AppHelpTemplate = `NAME:
{{.Name}} - {{.Usage}} {{.Name}} - {{.Usage}}
USAGE: USAGE:
{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command {{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
[command options]{{end}} {{if
.ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
{{if len .Authors}} {{if len .Authors}}
AUTHOR(S): AUTHOR:
{{range .Authors}}{{ . }}{{end}} {{range .Authors}}{{ . }}{{end}}
{{end}}{{if .Commands}} {{end}}{{if .Commands}}
COMMANDS: COMMANDS:
{{range .Commands}}{{if not .HideHelp}} {{join .Names ", "}}{{ "\t" {{range .Commands}}{{if not .HideHelp}} {{join .Names ", "}}{{ "\t"}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
GLOBAL OPTIONS: GLOBAL OPTIONS:
{{range .VisibleFlags}}{{.}} {{range .VisibleFlags}}{{.}}
{{end}}{{end}}{{if .Copyright }} {{end}}{{end}}{{if .Copyright }}

@ -11,6 +11,8 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
"runtime"
"strings"
"gopkg.in/urfave/cli.v1" "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 nil, fmt.Errorf("Cannot read from file: '%s' because it does not exist.", filePath)
} }
return ioutil.ReadFile(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 { } else {
return nil, fmt.Errorf("unable to determine how to load from path %s", filePath) return nil, fmt.Errorf("unable to determine how to load from path %s", filePath)
} }

@ -61,6 +61,20 @@ type Command struct {
commandNamePath []string 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. // FullName returns the full name of the command.
// For subcommands this ensures that parent commands are part of the command path // For subcommands this ensures that parent commands are part of the command path
func (c Command) FullName() string { 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 // 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 // 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 // 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) { func HandleExitCoder(err error) {
if err == nil { if err == nil {
return return
@ -93,9 +93,8 @@ func HandleExitCoder(err error) {
} }
if multiErr, ok := err.(MultiError); ok { if multiErr, ok := err.(MultiError); ok {
for _, merr := range multiErr.Errors { code := handleMultiError(multiErr)
HandleExitCoder(merr) OsExiter(code)
}
return return
} }
@ -108,3 +107,18 @@ func HandleExitCoder(err error) {
} }
OsExiter(1) 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 called := false
OsExiter = func(rc int) { OsExiter = func(rc int) {
if !called {
exitCode = rc exitCode = rc
called = true called = true
} }
}
defer func() { OsExiter = fakeOsExiter }() defer func() { OsExiter = fakeOsExiter }()
@ -29,9 +31,11 @@ func TestHandleExitCoder_ExitCoder(t *testing.T) {
called := false called := false
OsExiter = func(rc int) { OsExiter = func(rc int) {
if !called {
exitCode = rc exitCode = rc
called = true called = true
} }
}
defer func() { OsExiter = fakeOsExiter }() defer func() { OsExiter = fakeOsExiter }()
@ -46,17 +50,20 @@ func TestHandleExitCoder_MultiErrorWithExitCoder(t *testing.T) {
called := false called := false
OsExiter = func(rc int) { OsExiter = func(rc int) {
if !called {
exitCode = rc exitCode = rc
called = true called = true
} }
}
defer func() { OsExiter = fakeOsExiter }() defer func() { OsExiter = fakeOsExiter }()
exitErr := NewExitError("galactic perimeter breach", 9) 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) HandleExitCoder(err)
expect(t, exitCode, 9) expect(t, exitCode, 11)
expect(t, called, true) expect(t, called, true)
} }
@ -65,9 +72,11 @@ func TestHandleExitCoder_ErrorWithMessage(t *testing.T) {
called := false called := false
OsExiter = func(rc int) { OsExiter = func(rc int) {
if !called {
exitCode = rc exitCode = rc
called = true called = true
} }
}
ErrWriter = &bytes.Buffer{} ErrWriter = &bytes.Buffer{}
defer func() { defer func() {
@ -88,9 +97,11 @@ func TestHandleExitCoder_ErrorWithoutMessage(t *testing.T) {
called := false called := false
OsExiter = func(rc int) { OsExiter = func(rc int) {
if !called {
exitCode = rc exitCode = rc
called = true called = true
} }
}
ErrWriter = &bytes.Buffer{} ErrWriter = &bytes.Buffer{}
defer func() { defer func() {
@ -123,8 +134,10 @@ func TestHandleExitCoder_ErrorWithFormat(t *testing.T) {
called := false called := false
OsExiter = func(rc int) { OsExiter = func(rc int) {
if !called {
called = true called = true
} }
}
ErrWriter = &bytes.Buffer{} ErrWriter = &bytes.Buffer{}
defer func() { defer func() {
@ -143,8 +156,10 @@ func TestHandleExitCoder_MultiErrorWithFormat(t *testing.T) {
called := false called := false
OsExiter = func(rc int) { OsExiter = func(rc int) {
if !called {
called = true called = true
} }
}
ErrWriter = &bytes.Buffer{} ErrWriter = &bytes.Buffer{}
defer func() { OsExiter = fakeOsExiter }() defer func() { OsExiter = fakeOsExiter }()

Loading…
Cancel
Save