Merge pull request #227 from databus23/global-flags-from-all-parents

Collect global flags from the full command hierarchy
This commit is contained in:
Jesse Szwedko 2015-06-01 21:25:30 -07:00
commit 854c52192c
6 changed files with 75 additions and 31 deletions

6
app.go
View File

@ -104,12 +104,12 @@ func (a *App) Run(arguments []string) (err error) {
nerr := normalizeFlags(a.Flags, set) nerr := normalizeFlags(a.Flags, set)
if nerr != nil { if nerr != nil {
fmt.Fprintln(a.Writer, nerr) fmt.Fprintln(a.Writer, nerr)
context := NewContext(a, set, set) context := NewContext(a, set, nil)
ShowAppHelp(context) ShowAppHelp(context)
fmt.Fprintln(a.Writer) fmt.Fprintln(a.Writer)
return nerr return nerr
} }
context := NewContext(a, set, set) context := NewContext(a, set, nil)
if err != nil { if err != nil {
fmt.Fprintf(a.Writer, "Incorrect Usage.\n\n") fmt.Fprintf(a.Writer, "Incorrect Usage.\n\n")
@ -190,7 +190,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
set.SetOutput(ioutil.Discard) set.SetOutput(ioutil.Discard)
err = set.Parse(ctx.Args().Tail()) err = set.Parse(ctx.Args().Tail())
nerr := normalizeFlags(a.Flags, set) nerr := normalizeFlags(a.Flags, set)
context := NewContext(a, set, ctx.globalSet) context := NewContext(a, set, ctx)
if nerr != nil { if nerr != nil {
fmt.Fprintln(a.Writer, nerr) fmt.Fprintln(a.Writer, nerr)

View File

@ -629,6 +629,7 @@ func TestAppCommandNotFound(t *testing.T) {
func TestGlobalFlagsInSubcommands(t *testing.T) { func TestGlobalFlagsInSubcommands(t *testing.T) {
subcommandRun := false subcommandRun := false
parentFlag := false
app := cli.NewApp() app := cli.NewApp()
app.Flags = []cli.Flag{ app.Flags = []cli.Flag{
@ -638,6 +639,9 @@ func TestGlobalFlagsInSubcommands(t *testing.T) {
app.Commands = []cli.Command{ app.Commands = []cli.Command{
cli.Command{ cli.Command{
Name: "foo", Name: "foo",
Flags: []cli.Flag{
cli.BoolFlag{Name: "parent, p", Usage: "Parent flag"},
},
Subcommands: []cli.Command{ Subcommands: []cli.Command{
{ {
Name: "bar", Name: "bar",
@ -645,15 +649,19 @@ func TestGlobalFlagsInSubcommands(t *testing.T) {
if c.GlobalBool("debug") { if c.GlobalBool("debug") {
subcommandRun = true subcommandRun = true
} }
if c.GlobalBool("parent") {
parentFlag = true
}
}, },
}, },
}, },
}, },
} }
app.Run([]string{"command", "-d", "foo", "bar"}) app.Run([]string{"command", "-d", "foo", "-p", "bar"})
expect(t, subcommandRun, true) expect(t, subcommandRun, true)
expect(t, parentFlag, true)
} }
func TestApp_Run_CommandWithSubcommandHasHelpTopic(t *testing.T) { func TestApp_Run_CommandWithSubcommandHasHelpTopic(t *testing.T) {

View File

@ -105,7 +105,7 @@ func (c Command) Run(ctx *Context) error {
fmt.Fprintln(ctx.App.Writer) fmt.Fprintln(ctx.App.Writer)
return nerr return nerr
} }
context := NewContext(ctx.App, set, ctx.globalSet) context := NewContext(ctx.App, set, ctx)
if checkCommandCompletions(context, c.Name) { if checkCommandCompletions(context, c.Name) {
return nil return nil

View File

@ -13,7 +13,7 @@ func TestCommandDoNotIgnoreFlags(t *testing.T) {
test := []string{"blah", "blah", "-break"} test := []string{"blah", "blah", "-break"}
set.Parse(test) set.Parse(test)
c := cli.NewContext(app, set, set) c := cli.NewContext(app, set, nil)
command := cli.Command{ command := cli.Command{
Name: "test-cmd", Name: "test-cmd",
@ -33,7 +33,7 @@ func TestCommandIgnoreFlags(t *testing.T) {
test := []string{"blah", "blah"} test := []string{"blah", "blah"}
set.Parse(test) set.Parse(test)
c := cli.NewContext(app, set, set) c := cli.NewContext(app, set, nil)
command := cli.Command{ command := cli.Command{
Name: "test-cmd", Name: "test-cmd",

View File

@ -16,14 +16,14 @@ type Context struct {
App *App App *App
Command Command Command Command
flagSet *flag.FlagSet flagSet *flag.FlagSet
globalSet *flag.FlagSet
setFlags map[string]bool setFlags map[string]bool
globalSetFlags map[string]bool globalSetFlags map[string]bool
parentContext *Context
} }
// Creates a new context. For use in when invoking an App or Command action. // Creates a new context. For use in when invoking an App or Command action.
func NewContext(app *App, set *flag.FlagSet, globalSet *flag.FlagSet) *Context { func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context {
return &Context{App: app, flagSet: set, globalSet: globalSet} return &Context{App: app, flagSet: set, parentContext: parentCtx}
} }
// Looks up the value of a local int flag, returns 0 if no int flag exists // Looks up the value of a local int flag, returns 0 if no int flag exists
@ -73,37 +73,58 @@ func (c *Context) Generic(name string) interface{} {
// Looks up the value of a global int flag, returns 0 if no int flag exists // Looks up the value of a global int flag, returns 0 if no int flag exists
func (c *Context) GlobalInt(name string) int { func (c *Context) GlobalInt(name string) int {
return lookupInt(name, c.globalSet) if fs := lookupParentFlagSet(name, c); fs != nil {
return lookupInt(name, fs)
}
return 0
} }
// Looks up the value of a global time.Duration flag, returns 0 if no time.Duration flag exists // Looks up the value of a global time.Duration flag, returns 0 if no time.Duration flag exists
func (c *Context) GlobalDuration(name string) time.Duration { func (c *Context) GlobalDuration(name string) time.Duration {
return lookupDuration(name, c.globalSet) if fs := lookupParentFlagSet(name, c); fs != nil {
return lookupDuration(name, fs)
}
return 0
} }
// Looks up the value of a global bool flag, returns false if no bool flag exists // Looks up the value of a global bool flag, returns false if no bool flag exists
func (c *Context) GlobalBool(name string) bool { func (c *Context) GlobalBool(name string) bool {
return lookupBool(name, c.globalSet) if fs := lookupParentFlagSet(name, c); fs != nil {
return lookupBool(name, fs)
}
return false
} }
// Looks up the value of a global string flag, returns "" if no string flag exists // Looks up the value of a global string flag, returns "" if no string flag exists
func (c *Context) GlobalString(name string) string { func (c *Context) GlobalString(name string) string {
return lookupString(name, c.globalSet) if fs := lookupParentFlagSet(name, c); fs != nil {
return lookupString(name, fs)
}
return ""
} }
// Looks up the value of a global string slice flag, returns nil if no string slice flag exists // Looks up the value of a global string slice flag, returns nil if no string slice flag exists
func (c *Context) GlobalStringSlice(name string) []string { func (c *Context) GlobalStringSlice(name string) []string {
return lookupStringSlice(name, c.globalSet) if fs := lookupParentFlagSet(name, c); fs != nil {
return lookupStringSlice(name, fs)
}
return nil
} }
// Looks up the value of a global int slice flag, returns nil if no int slice flag exists // Looks up the value of a global int slice flag, returns nil if no int slice flag exists
func (c *Context) GlobalIntSlice(name string) []int { func (c *Context) GlobalIntSlice(name string) []int {
return lookupIntSlice(name, c.globalSet) if fs := lookupParentFlagSet(name, c); fs != nil {
return lookupIntSlice(name, fs)
}
return nil
} }
// Looks up the value of a global generic flag, returns nil if no generic flag exists // Looks up the value of a global generic flag, returns nil if no generic flag exists
func (c *Context) GlobalGeneric(name string) interface{} { func (c *Context) GlobalGeneric(name string) interface{} {
return lookupGeneric(name, c.globalSet) if fs := lookupParentFlagSet(name, c); fs != nil {
return lookupGeneric(name, fs)
}
return nil
} }
// Returns the number of flags set // Returns the number of flags set
@ -126,11 +147,13 @@ func (c *Context) IsSet(name string) bool {
func (c *Context) GlobalIsSet(name string) bool { func (c *Context) GlobalIsSet(name string) bool {
if c.globalSetFlags == nil { if c.globalSetFlags == nil {
c.globalSetFlags = make(map[string]bool) c.globalSetFlags = make(map[string]bool)
c.globalSet.Visit(func(f *flag.Flag) { for ctx := c.parentContext; ctx != nil && c.globalSetFlags[name] == false; ctx = ctx.parentContext {
ctx.flagSet.Visit(func(f *flag.Flag) {
c.globalSetFlags[f.Name] = true c.globalSetFlags[f.Name] = true
}) })
} }
return c.globalSetFlags[name] == true }
return c.globalSetFlags[name]
} }
// Returns a slice of flag names used in this context. // Returns a slice of flag names used in this context.
@ -201,6 +224,15 @@ func (a Args) Swap(from, to int) error {
return nil return nil
} }
func lookupParentFlagSet(name string, ctx *Context) *flag.FlagSet {
for ctx := ctx.parentContext; ctx != nil; ctx = ctx.parentContext {
if f := ctx.flagSet.Lookup(name); f != nil {
return ctx.flagSet
}
}
return nil
}
func lookupInt(name string, set *flag.FlagSet) int { func lookupInt(name string, set *flag.FlagSet) int {
f := set.Lookup(name) f := set.Lookup(name)
if f != nil { if f != nil {

View File

@ -13,8 +13,9 @@ func TestNewContext(t *testing.T) {
set.Int("myflag", 12, "doc") set.Int("myflag", 12, "doc")
globalSet := flag.NewFlagSet("test", 0) globalSet := flag.NewFlagSet("test", 0)
globalSet.Int("myflag", 42, "doc") globalSet.Int("myflag", 42, "doc")
globalCtx := cli.NewContext(nil, globalSet, nil)
command := cli.Command{Name: "mycommand"} command := cli.Command{Name: "mycommand"}
c := cli.NewContext(nil, set, globalSet) c := cli.NewContext(nil, set, globalCtx)
c.Command = command c.Command = command
expect(t, c.Int("myflag"), 12) expect(t, c.Int("myflag"), 12)
expect(t, c.GlobalInt("myflag"), 42) expect(t, c.GlobalInt("myflag"), 42)
@ -24,42 +25,42 @@ func TestNewContext(t *testing.T) {
func TestContext_Int(t *testing.T) { func TestContext_Int(t *testing.T) {
set := flag.NewFlagSet("test", 0) set := flag.NewFlagSet("test", 0)
set.Int("myflag", 12, "doc") set.Int("myflag", 12, "doc")
c := cli.NewContext(nil, set, set) c := cli.NewContext(nil, set, nil)
expect(t, c.Int("myflag"), 12) expect(t, c.Int("myflag"), 12)
} }
func TestContext_Duration(t *testing.T) { func TestContext_Duration(t *testing.T) {
set := flag.NewFlagSet("test", 0) set := flag.NewFlagSet("test", 0)
set.Duration("myflag", time.Duration(12*time.Second), "doc") set.Duration("myflag", time.Duration(12*time.Second), "doc")
c := cli.NewContext(nil, set, set) c := cli.NewContext(nil, set, nil)
expect(t, c.Duration("myflag"), time.Duration(12*time.Second)) expect(t, c.Duration("myflag"), time.Duration(12*time.Second))
} }
func TestContext_String(t *testing.T) { func TestContext_String(t *testing.T) {
set := flag.NewFlagSet("test", 0) set := flag.NewFlagSet("test", 0)
set.String("myflag", "hello world", "doc") set.String("myflag", "hello world", "doc")
c := cli.NewContext(nil, set, set) c := cli.NewContext(nil, set, nil)
expect(t, c.String("myflag"), "hello world") expect(t, c.String("myflag"), "hello world")
} }
func TestContext_Bool(t *testing.T) { func TestContext_Bool(t *testing.T) {
set := flag.NewFlagSet("test", 0) set := flag.NewFlagSet("test", 0)
set.Bool("myflag", false, "doc") set.Bool("myflag", false, "doc")
c := cli.NewContext(nil, set, set) c := cli.NewContext(nil, set, nil)
expect(t, c.Bool("myflag"), false) expect(t, c.Bool("myflag"), false)
} }
func TestContext_BoolT(t *testing.T) { func TestContext_BoolT(t *testing.T) {
set := flag.NewFlagSet("test", 0) set := flag.NewFlagSet("test", 0)
set.Bool("myflag", true, "doc") set.Bool("myflag", true, "doc")
c := cli.NewContext(nil, set, set) c := cli.NewContext(nil, set, nil)
expect(t, c.BoolT("myflag"), true) expect(t, c.BoolT("myflag"), true)
} }
func TestContext_Args(t *testing.T) { func TestContext_Args(t *testing.T) {
set := flag.NewFlagSet("test", 0) set := flag.NewFlagSet("test", 0)
set.Bool("myflag", false, "doc") set.Bool("myflag", false, "doc")
c := cli.NewContext(nil, set, set) c := cli.NewContext(nil, set, nil)
set.Parse([]string{"--myflag", "bat", "baz"}) set.Parse([]string{"--myflag", "bat", "baz"})
expect(t, len(c.Args()), 2) expect(t, len(c.Args()), 2)
expect(t, c.Bool("myflag"), true) expect(t, c.Bool("myflag"), true)
@ -71,7 +72,8 @@ func TestContext_IsSet(t *testing.T) {
set.String("otherflag", "hello world", "doc") set.String("otherflag", "hello world", "doc")
globalSet := flag.NewFlagSet("test", 0) globalSet := flag.NewFlagSet("test", 0)
globalSet.Bool("myflagGlobal", true, "doc") globalSet.Bool("myflagGlobal", true, "doc")
c := cli.NewContext(nil, set, globalSet) globalCtx := cli.NewContext(nil, globalSet, nil)
c := cli.NewContext(nil, set, globalCtx)
set.Parse([]string{"--myflag", "bat", "baz"}) set.Parse([]string{"--myflag", "bat", "baz"})
globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"}) globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"})
expect(t, c.IsSet("myflag"), true) expect(t, c.IsSet("myflag"), true)
@ -87,7 +89,8 @@ func TestContext_GlobalIsSet(t *testing.T) {
globalSet := flag.NewFlagSet("test", 0) globalSet := flag.NewFlagSet("test", 0)
globalSet.Bool("myflagGlobal", true, "doc") globalSet.Bool("myflagGlobal", true, "doc")
globalSet.Bool("myflagGlobalUnset", true, "doc") globalSet.Bool("myflagGlobalUnset", true, "doc")
c := cli.NewContext(nil, set, globalSet) globalCtx := cli.NewContext(nil, globalSet, nil)
c := cli.NewContext(nil, set, globalCtx)
set.Parse([]string{"--myflag", "bat", "baz"}) set.Parse([]string{"--myflag", "bat", "baz"})
globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"}) globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"})
expect(t, c.GlobalIsSet("myflag"), false) expect(t, c.GlobalIsSet("myflag"), false)
@ -104,7 +107,8 @@ func TestContext_NumFlags(t *testing.T) {
set.String("otherflag", "hello world", "doc") set.String("otherflag", "hello world", "doc")
globalSet := flag.NewFlagSet("test", 0) globalSet := flag.NewFlagSet("test", 0)
globalSet.Bool("myflagGlobal", true, "doc") globalSet.Bool("myflagGlobal", true, "doc")
c := cli.NewContext(nil, set, globalSet) globalCtx := cli.NewContext(nil, globalSet, nil)
c := cli.NewContext(nil, set, globalCtx)
set.Parse([]string{"--myflag", "--otherflag=foo"}) set.Parse([]string{"--myflag", "--otherflag=foo"})
globalSet.Parse([]string{"--myflagGlobal"}) globalSet.Parse([]string{"--myflagGlobal"})
expect(t, c.NumFlags(), 2) expect(t, c.NumFlags(), 2)