Subcommand structure should be correctly copied to main structure
This commit is contained in:
commit
e109e81e6a
33
README.md
33
README.md
@ -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": ["--help"],
|
"args": ["--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)
|
||||||
}
|
}
|
||||||
|
14
command.go
14
command.go
@ -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 {
|
||||||
|
22
errors.go
22
errors.go
@ -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,8 +12,10 @@ func TestHandleExitCoder_nil(t *testing.T) {
|
|||||||
called := false
|
called := false
|
||||||
|
|
||||||
OsExiter = func(rc int) {
|
OsExiter = func(rc int) {
|
||||||
exitCode = rc
|
if !called {
|
||||||
called = true
|
exitCode = rc
|
||||||
|
called = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() { OsExiter = fakeOsExiter }()
|
defer func() { OsExiter = fakeOsExiter }()
|
||||||
@ -29,8 +31,10 @@ func TestHandleExitCoder_ExitCoder(t *testing.T) {
|
|||||||
called := false
|
called := false
|
||||||
|
|
||||||
OsExiter = func(rc int) {
|
OsExiter = func(rc int) {
|
||||||
exitCode = rc
|
if !called {
|
||||||
called = true
|
exitCode = rc
|
||||||
|
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) {
|
||||||
exitCode = rc
|
if !called {
|
||||||
called = true
|
exitCode = rc
|
||||||
|
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,8 +72,10 @@ func TestHandleExitCoder_ErrorWithMessage(t *testing.T) {
|
|||||||
called := false
|
called := false
|
||||||
|
|
||||||
OsExiter = func(rc int) {
|
OsExiter = func(rc int) {
|
||||||
exitCode = rc
|
if !called {
|
||||||
called = true
|
exitCode = rc
|
||||||
|
called = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ErrWriter = &bytes.Buffer{}
|
ErrWriter = &bytes.Buffer{}
|
||||||
|
|
||||||
@ -88,8 +97,10 @@ func TestHandleExitCoder_ErrorWithoutMessage(t *testing.T) {
|
|||||||
called := false
|
called := false
|
||||||
|
|
||||||
OsExiter = func(rc int) {
|
OsExiter = func(rc int) {
|
||||||
exitCode = rc
|
if !called {
|
||||||
called = true
|
exitCode = rc
|
||||||
|
called = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ErrWriter = &bytes.Buffer{}
|
ErrWriter = &bytes.Buffer{}
|
||||||
|
|
||||||
@ -123,7 +134,9 @@ func TestHandleExitCoder_ErrorWithFormat(t *testing.T) {
|
|||||||
called := false
|
called := false
|
||||||
|
|
||||||
OsExiter = func(rc int) {
|
OsExiter = func(rc int) {
|
||||||
called = true
|
if !called {
|
||||||
|
called = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ErrWriter = &bytes.Buffer{}
|
ErrWriter = &bytes.Buffer{}
|
||||||
|
|
||||||
@ -143,7 +156,9 @@ func TestHandleExitCoder_MultiErrorWithFormat(t *testing.T) {
|
|||||||
called := false
|
called := false
|
||||||
|
|
||||||
OsExiter = func(rc int) {
|
OsExiter = func(rc int) {
|
||||||
called = true
|
if !called {
|
||||||
|
called = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ErrWriter = &bytes.Buffer{}
|
ErrWriter = &bytes.Buffer{}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user