Merge pull request #154 from codegangsta/allow-writer-to-be-set

Allow writer to be set
This commit is contained in:
Dan Buch 2014-12-12 08:50:46 -05:00
commit a14c5b47c7
4 changed files with 89 additions and 34 deletions

40
app.go
View File

@ -2,8 +2,11 @@ package cli
import ( import (
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"os" "os"
"text/tabwriter"
"text/template"
"time" "time"
) )
@ -41,6 +44,8 @@ type App struct {
Author string Author string
// Author e-mail // Author e-mail
Email string Email string
// Writer writer to write output to
Writer io.Writer
} }
// Tries to find out when this binary was compiled. // Tries to find out when this binary was compiled.
@ -62,11 +67,30 @@ func NewApp() *App {
BashComplete: DefaultAppComplete, BashComplete: DefaultAppComplete,
Action: helpCommand.Action, Action: helpCommand.Action,
Compiled: compileTime(), 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 // 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 { 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 // append help to commands
if a.Command(helpCommand.Name) == nil && !a.HideHelp { if a.Command(helpCommand.Name) == nil && !a.HideHelp {
a.Commands = append(a.Commands, helpCommand) a.Commands = append(a.Commands, helpCommand)
@ -88,18 +112,18 @@ func (a *App) Run(arguments []string) error {
err := set.Parse(arguments[1:]) err := set.Parse(arguments[1:])
nerr := normalizeFlags(a.Flags, set) nerr := normalizeFlags(a.Flags, set)
if nerr != nil { if nerr != nil {
fmt.Println(nerr) fmt.Fprintln(a.Writer, nerr)
context := NewContext(a, set, set) context := NewContext(a, set, set)
ShowAppHelp(context) ShowAppHelp(context)
fmt.Println("") fmt.Fprintln(a.Writer)
return nerr return nerr
} }
context := NewContext(a, set, set) context := NewContext(a, set, set)
if err != nil { if err != nil {
fmt.Printf("Incorrect Usage.\n\n") fmt.Fprintf(a.Writer, "Incorrect Usage.\n\n")
ShowAppHelp(context) ShowAppHelp(context)
fmt.Println("") fmt.Fprintln(a.Writer)
return err 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 // Another entry point to the cli app, takes care of passing arguments and error handling
func (a *App) RunAndExitOnError() { func (a *App) RunAndExitOnError() {
if err := a.Run(os.Args); err != nil { if err := a.Run(os.Args); err != nil {
os.Stderr.WriteString(fmt.Sprintln(err)) fmt.Fprintln(os.Stderr, err)
os.Exit(1) os.Exit(1)
} }
} }
@ -167,18 +191,18 @@ func (a *App) RunAsSubcommand(ctx *Context) error {
context := NewContext(a, set, ctx.globalSet) context := NewContext(a, set, ctx.globalSet)
if nerr != nil { if nerr != nil {
fmt.Println(nerr) fmt.Fprintln(a.Writer, nerr)
if len(a.Commands) > 0 { if len(a.Commands) > 0 {
ShowSubcommandHelp(context) ShowSubcommandHelp(context)
} else { } else {
ShowCommandHelp(ctx, context.Args().First()) ShowCommandHelp(ctx, context.Args().First())
} }
fmt.Println("") fmt.Fprintln(a.Writer)
return nerr return nerr
} }
if err != nil { if err != nil {
fmt.Printf("Incorrect Usage.\n\n") fmt.Fprintf(a.Writer, "Incorrect Usage.\n\n")
ShowSubcommandHelp(context) ShowSubcommandHelp(context)
return err return err
} }

View File

@ -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) { func TestApp_BeforeFunc(t *testing.T) {
beforeRun, subcommandRun := false, false beforeRun, subcommandRun := false, false
beforeError := fmt.Errorf("fail") beforeError := fmt.Errorf("fail")

View File

@ -74,18 +74,18 @@ func (c Command) Run(ctx *Context) error {
} }
if err != nil { if err != nil {
fmt.Printf("Incorrect Usage.\n\n") fmt.Fprint(ctx.App.Writer, "Incorrect Usage.\n\n")
ShowCommandHelp(ctx, c.Name) ShowCommandHelp(ctx, c.Name)
fmt.Println("") fmt.Fprintln(ctx.App.Writer)
return err return err
} }
nerr := normalizeFlags(c.Flags, set) nerr := normalizeFlags(c.Flags, set)
if nerr != nil { if nerr != nil {
fmt.Println(nerr) fmt.Fprintln(ctx.App.Writer, nerr)
fmt.Println("") fmt.Fprintln(ctx.App.Writer)
ShowCommandHelp(ctx, c.Name) ShowCommandHelp(ctx, c.Name)
fmt.Println("") fmt.Fprintln(ctx.App.Writer)
return nerr return nerr
} }
context := NewContext(ctx.App, set, ctx.globalSet) context := NewContext(ctx.App, set, ctx.globalSet)

29
help.go
View File

@ -1,11 +1,6 @@
package cli package cli
import ( import "fmt"
"fmt"
"os"
"text/tabwriter"
"text/template"
)
// The text template for the Default help topic. // The text template for the Default help topic.
// cli.go uses text/template to render templates. You can // cli.go uses text/template to render templates. You can
@ -94,7 +89,9 @@ var helpSubcommand = Command{
} }
// Prints help for the App // Prints help for the App
var HelpPrinter = printHelp type helpPrinter func(templ string, data interface{})
var HelpPrinter helpPrinter = nil
// Prints version for the App // Prints version for the App
var VersionPrinter = printVersion var VersionPrinter = printVersion
@ -106,9 +103,9 @@ func ShowAppHelp(c *Context) {
// Prints the list of subcommands as the default app completion method // Prints the list of subcommands as the default app completion method
func DefaultAppComplete(c *Context) { func DefaultAppComplete(c *Context) {
for _, command := range c.App.Commands { for _, command := range c.App.Commands {
fmt.Println(command.Name) fmt.Fprintln(c.App.Writer, command.Name)
if command.ShortName != "" { 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 { if c.App.CommandNotFound != nil {
c.App.CommandNotFound(c, command) c.App.CommandNotFound(c, command)
} else { } 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) { 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 // 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 { func checkVersion(c *Context) bool {
if c.GlobalBool("version") { if c.GlobalBool("version") {
ShowVersion(c) ShowVersion(c)