diff --git a/app.go b/app.go index f4c4af8..edf02fb 100644 --- a/app.go +++ b/app.go @@ -2,8 +2,11 @@ package cli import ( "fmt" + "io" "io/ioutil" "os" + "text/tabwriter" + "text/template" "time" ) @@ -41,6 +44,8 @@ type App struct { Author string // Author e-mail Email string + // Writer writer to write output to + Writer io.Writer } // Tries to find out when this binary was compiled. @@ -62,11 +67,30 @@ func NewApp() *App { BashComplete: DefaultAppComplete, Action: helpCommand.Action, Compiled: compileTime(), + Author: "Author", + Email: "unknown@email", + Writer: os.Stdout, } } // Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination func (a *App) Run(arguments []string) error { + if HelpPrinter == nil { + defer func() { + HelpPrinter = nil + }() + + HelpPrinter = func(templ string, data interface{}) { + w := tabwriter.NewWriter(a.Writer, 0, 8, 1, '\t', 0) + t := template.Must(template.New("help").Parse(templ)) + err := t.Execute(w, data) + if err != nil { + panic(err) + } + w.Flush() + } + } + // append help to commands if a.Command(helpCommand.Name) == nil && !a.HideHelp { a.Commands = append(a.Commands, helpCommand) @@ -88,18 +112,18 @@ func (a *App) Run(arguments []string) error { err := set.Parse(arguments[1:]) nerr := normalizeFlags(a.Flags, set) if nerr != nil { - fmt.Println(nerr) + fmt.Fprintln(a.Writer, nerr) context := NewContext(a, set, set) ShowAppHelp(context) - fmt.Println("") + fmt.Fprintln(a.Writer) return nerr } context := NewContext(a, set, set) if err != nil { - fmt.Printf("Incorrect Usage.\n\n") + fmt.Fprintf(a.Writer, "Incorrect Usage.\n\n") ShowAppHelp(context) - fmt.Println("") + fmt.Fprintln(a.Writer) return err } @@ -139,7 +163,7 @@ func (a *App) Run(arguments []string) error { // Another entry point to the cli app, takes care of passing arguments and error handling func (a *App) RunAndExitOnError() { if err := a.Run(os.Args); err != nil { - os.Stderr.WriteString(fmt.Sprintln(err)) + fmt.Fprintln(os.Stderr, err) os.Exit(1) } } @@ -167,18 +191,18 @@ func (a *App) RunAsSubcommand(ctx *Context) error { context := NewContext(a, set, ctx.globalSet) if nerr != nil { - fmt.Println(nerr) + fmt.Fprintln(a.Writer, nerr) if len(a.Commands) > 0 { ShowSubcommandHelp(context) } else { ShowCommandHelp(ctx, context.Args().First()) } - fmt.Println("") + fmt.Fprintln(a.Writer) return nerr } if err != nil { - fmt.Printf("Incorrect Usage.\n\n") + fmt.Fprintf(a.Writer, "Incorrect Usage.\n\n") ShowSubcommandHelp(context) return err } diff --git a/app_test.go b/app_test.go index 81d1174..1413199 100644 --- a/app_test.go +++ b/app_test.go @@ -265,6 +265,50 @@ func TestApp_ParseSliceFlags(t *testing.T) { } } +func TestApp_DefaultStdout(t *testing.T) { + app := cli.NewApp() + + if app.Writer != os.Stdout { + t.Error("Default output writer not set.") + } +} + +type mockWriter struct { + written []byte +} + +func (fw *mockWriter) Write(p []byte) (n int, err error) { + if fw.written == nil { + fw.written = p + } else { + fw.written = append(fw.written, p...) + } + + return len(p), nil +} + +func (fw *mockWriter) GetWritten() (b []byte) { + return fw.written +} + +func TestApp_SetStdout(t *testing.T) { + w := &mockWriter{} + + app := cli.NewApp() + app.Name = "test" + app.Writer = w + + err := app.Run([]string{"help"}) + + if err != nil { + t.Fatalf("Run error: %s", err) + } + + if len(w.written) == 0 { + t.Error("App did not write output to desired writer.") + } +} + func TestApp_BeforeFunc(t *testing.T) { beforeRun, subcommandRun := false, false beforeError := fmt.Errorf("fail") diff --git a/command.go b/command.go index 5622b38..1536b15 100644 --- a/command.go +++ b/command.go @@ -74,18 +74,18 @@ func (c Command) Run(ctx *Context) error { } if err != nil { - fmt.Printf("Incorrect Usage.\n\n") + fmt.Fprint(ctx.App.Writer, "Incorrect Usage.\n\n") ShowCommandHelp(ctx, c.Name) - fmt.Println("") + fmt.Fprintln(ctx.App.Writer) return err } nerr := normalizeFlags(c.Flags, set) if nerr != nil { - fmt.Println(nerr) - fmt.Println("") + fmt.Fprintln(ctx.App.Writer, nerr) + fmt.Fprintln(ctx.App.Writer) ShowCommandHelp(ctx, c.Name) - fmt.Println("") + fmt.Fprintln(ctx.App.Writer) return nerr } context := NewContext(ctx.App, set, ctx.globalSet) diff --git a/help.go b/help.go index f71fca0..bfb2788 100644 --- a/help.go +++ b/help.go @@ -1,11 +1,6 @@ package cli -import ( - "fmt" - "os" - "text/tabwriter" - "text/template" -) +import "fmt" // The text template for the Default help topic. // cli.go uses text/template to render templates. You can @@ -94,7 +89,9 @@ var helpSubcommand = Command{ } // Prints help for the App -var HelpPrinter = printHelp +type helpPrinter func(templ string, data interface{}) + +var HelpPrinter helpPrinter = nil // Prints version for the App var VersionPrinter = printVersion @@ -106,9 +103,9 @@ func ShowAppHelp(c *Context) { // Prints the list of subcommands as the default app completion method func DefaultAppComplete(c *Context) { for _, command := range c.App.Commands { - fmt.Println(command.Name) + fmt.Fprintln(c.App.Writer, command.Name) if command.ShortName != "" { - fmt.Println(command.ShortName) + fmt.Fprintln(c.App.Writer, command.ShortName) } } } @@ -125,7 +122,7 @@ func ShowCommandHelp(c *Context, command string) { if c.App.CommandNotFound != nil { c.App.CommandNotFound(c, command) } else { - fmt.Printf("No help topic for '%v'\n", command) + fmt.Fprintf(c.App.Writer, "No help topic for '%v'\n", command) } } @@ -140,7 +137,7 @@ func ShowVersion(c *Context) { } func printVersion(c *Context) { - fmt.Printf("%v version %v\n", c.App.Name, c.App.Version) + fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version) } // Prints the lists of commands within a given context @@ -159,16 +156,6 @@ func ShowCommandCompletions(ctx *Context, command string) { } } -func printHelp(templ string, data interface{}) { - w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) - t := template.Must(template.New("help").Parse(templ)) - err := t.Execute(w, data) - if err != nil { - panic(err) - } - w.Flush() -} - func checkVersion(c *Context) bool { if c.GlobalBool("version") { ShowVersion(c)