Support persistent flags

arghing
Dan Buch 2 years ago
parent 395006cdb8
commit 92e3d6fe5b
Signed by: meatballhat
GPG Key ID: A12F782281063434

@ -107,7 +107,9 @@ func (p *parser) parseCommand(cCfg *CommandConfig) Node {
tracef("parseCommand(...) for=%d nodes=%+#v", i, nodes) tracef("parseCommand(...) for=%d nodes=%+#v", i, nodes)
tracef("parseCommand(...) for=%d tok=%s lit=%q pos=%v", i, p.tok, p.lit, p.pos) tracef("parseCommand(...) for=%d tok=%s lit=%q pos=%v", i, p.tok, p.lit, p.pos)
if subCfg, ok := cCfg.Commands[p.lit]; ok { tracef("parseCommand(...) cCfg=%+#v", cCfg)
if subCfg, ok := cCfg.GetCommandConfig(p.lit); ok {
subCommand := p.lit subCommand := p.lit
nodes = append(nodes, p.parseCommand(&subCfg)) nodes = append(nodes, p.parseCommand(&subCfg))
@ -186,26 +188,26 @@ func (p *parser) parseIdent() Node {
return node return node
} }
func (p *parser) parseFlag(flCfgMap map[string]FlagConfig) Node { func (p *parser) parseFlag(flags *Flags) Node {
switch p.tok { switch p.tok {
case SHORT_FLAG: case SHORT_FLAG:
tracef("parseFlag(...) parsing short flag with config=%+#v", flCfgMap) tracef("parseFlag(...) parsing short flag with config=%+#v", flags)
return p.parseShortFlag(flCfgMap) return p.parseShortFlag(flags)
case LONG_FLAG: case LONG_FLAG:
tracef("parseFlag(...) parsing long flag with config=%+#v", flCfgMap) tracef("parseFlag(...) parsing long flag with config=%+#v", flags)
return p.parseLongFlag(flCfgMap) return p.parseLongFlag(flags)
case COMPOUND_SHORT_FLAG: case COMPOUND_SHORT_FLAG:
tracef("parseFlag(...) parsing compound short flag with config=%+#v", flCfgMap) tracef("parseFlag(...) parsing compound short flag with config=%+#v", flags)
return p.parseCompoundShortFlag(flCfgMap) return p.parseCompoundShortFlag(flags)
} }
panic(fmt.Sprintf("token %v cannot be parsed as flag", p.tok)) panic(fmt.Sprintf("token %v cannot be parsed as flag", p.tok))
} }
func (p *parser) parseShortFlag(flCfgMap map[string]FlagConfig) Node { func (p *parser) parseShortFlag(flags *Flags) Node {
node := &Flag{Name: string(p.lit[1])} node := &Flag{Name: string(p.lit[1])}
flCfg, ok := flCfgMap[node.Name] flCfg, ok := flags.Get(node.Name)
if !ok { if !ok {
p.addError(fmt.Sprintf("unknown flag %q", node.Name)) p.addError(fmt.Sprintf("unknown flag %q", node.Name))
@ -215,10 +217,10 @@ func (p *parser) parseShortFlag(flCfgMap map[string]FlagConfig) Node {
return p.parseConfiguredFlag(node, flCfg) return p.parseConfiguredFlag(node, flCfg)
} }
func (p *parser) parseLongFlag(flCfgMap map[string]FlagConfig) Node { func (p *parser) parseLongFlag(flags *Flags) Node {
node := &Flag{Name: string(p.lit[2:])} node := &Flag{Name: string(p.lit[2:])}
flCfg, ok := flCfgMap[node.Name] flCfg, ok := flags.Get(node.Name)
if !ok { if !ok {
p.addError(fmt.Sprintf("unknown flag %q", node.Name)) p.addError(fmt.Sprintf("unknown flag %q", node.Name))
@ -228,7 +230,7 @@ func (p *parser) parseLongFlag(flCfgMap map[string]FlagConfig) Node {
return p.parseConfiguredFlag(node, flCfg) return p.parseConfiguredFlag(node, flCfg)
} }
func (p *parser) parseCompoundShortFlag(flCfgMap map[string]FlagConfig) Node { func (p *parser) parseCompoundShortFlag(flags *Flags) Node {
flagNodes := []Node{} flagNodes := []Node{}
withoutFlagPrefix := p.lit[1:] withoutFlagPrefix := p.lit[1:]
@ -237,7 +239,7 @@ func (p *parser) parseCompoundShortFlag(flCfgMap map[string]FlagConfig) Node {
node := &Flag{Name: string(r)} node := &Flag{Name: string(r)}
if i == len(withoutFlagPrefix)-1 { if i == len(withoutFlagPrefix)-1 {
flCfg, ok := flCfgMap[node.Name] flCfg, ok := flags.Get(node.Name)
if !ok { if !ok {
p.addError(fmt.Sprintf("unknown flag %q", node.Name)) p.addError(fmt.Sprintf("unknown flag %q", node.Name))

@ -36,11 +36,62 @@ type ParserConfig struct {
type CommandConfig struct { type CommandConfig struct {
NValue NValue NValue NValue
ValueNames []string ValueNames []string
Flags map[string]FlagConfig Flags *Flags
Commands map[string]CommandConfig Commands *Commands
}
func (cCfg *CommandConfig) GetCommandConfig(name string) (CommandConfig, bool) {
if cCfg.Commands == nil {
cCfg.Commands = &Commands{Map: map[string]CommandConfig{}}
}
return cCfg.Commands.Get(name)
}
func (cCfg *CommandConfig) GetFlagConfig(name string) (FlagConfig, bool) {
if cCfg.Flags == nil {
cCfg.Flags = &Flags{Map: map[string]FlagConfig{}}
}
return cCfg.Flags.Get(name)
} }
type FlagConfig struct { type FlagConfig struct {
NValue NValue NValue NValue
Persist bool
ValueNames []string ValueNames []string
} }
type Flags struct {
Parent *Flags
Map map[string]FlagConfig
}
func (fl *Flags) Get(name string) (FlagConfig, bool) {
if fl.Map == nil {
fl.Map = map[string]FlagConfig{}
}
flCfg, ok := fl.Map[name]
if !ok && fl.Parent != nil {
flCfg, ok = fl.Parent.Get(name)
return flCfg, ok && flCfg.Persist
}
return flCfg, ok
}
type Commands struct {
Map map[string]CommandConfig
}
func (cmd *Commands) Get(name string) (CommandConfig, bool) {
tracef("Get(%q)", name)
if cmd.Map == nil {
cmd.Map = map[string]CommandConfig{}
}
cmdCfg, ok := cmd.Map[name]
return cmdCfg, ok
}

@ -25,16 +25,20 @@ func TestParser(t *testing.T) {
}, },
cfg: &argh.ParserConfig{ cfg: &argh.ParserConfig{
Prog: argh.CommandConfig{ Prog: argh.CommandConfig{
Flags: map[string]argh.FlagConfig{ Flags: &argh.Flags{
"e": {}, Map: map[string]argh.FlagConfig{
"a": {}, "e": {},
"t": {}, "a": {},
"wat": {}, "t": {},
"wat": {},
},
}, },
Commands: map[string]argh.CommandConfig{ Commands: &argh.Commands{
"hello": argh.CommandConfig{ Map: map[string]argh.CommandConfig{
NValue: 1, "hello": argh.CommandConfig{
ValueNames: []string{"name"}, NValue: 1,
ValueNames: []string{"name"},
},
}, },
}, },
}, },
@ -88,6 +92,89 @@ func TestParser(t *testing.T) {
}, },
}, },
}, },
{
name: "persistent flags",
args: []string{
"pies", "--wat", "hello", "mario", "-eat",
},
cfg: &argh.ParserConfig{
Prog: func() argh.CommandConfig {
cmdCfg := argh.CommandConfig{
Flags: &argh.Flags{
Map: map[string]argh.FlagConfig{
"e": {Persist: true},
"a": {Persist: true},
"t": {Persist: true},
"wat": {},
},
},
}
cmdCfg.Commands = &argh.Commands{
Map: map[string]argh.CommandConfig{
"hello": argh.CommandConfig{
NValue: 1,
ValueNames: []string{"name"},
Flags: &argh.Flags{
Parent: cmdCfg.Flags,
Map: map[string]argh.FlagConfig{},
},
},
},
}
return cmdCfg
}(),
},
expPT: []argh.Node{
&argh.Command{
Name: "pies",
Nodes: []argh.Node{
&argh.ArgDelimiter{},
&argh.Flag{Name: "wat"},
&argh.ArgDelimiter{},
&argh.Command{
Name: "hello",
Values: map[string]string{
"name": "mario",
},
Nodes: []argh.Node{
&argh.ArgDelimiter{},
&argh.Ident{Literal: "mario"},
&argh.ArgDelimiter{},
&argh.CompoundShortFlag{
Nodes: []argh.Node{
&argh.Flag{Name: "e"},
&argh.Flag{Name: "a"},
&argh.Flag{Name: "t"},
},
},
},
},
},
},
},
expAST: []argh.Node{
&argh.Command{
Name: "pies",
Nodes: []argh.Node{
&argh.Flag{Name: "wat"},
&argh.Command{
Name: "hello",
Values: map[string]string{
"name": "mario",
},
Nodes: []argh.Node{
&argh.Ident{Literal: "mario"},
&argh.Flag{Name: "e"},
&argh.Flag{Name: "a"},
&argh.Flag{Name: "t"},
},
},
},
},
},
},
{ {
name: "bare", name: "bare",
args: []string{"pizzas"}, args: []string{"pizzas"},
@ -181,10 +268,12 @@ func TestParser(t *testing.T) {
args: []string{"pizzas", "--tasty", "--fresh", "--super-hot-right-now"}, args: []string{"pizzas", "--tasty", "--fresh", "--super-hot-right-now"},
cfg: &argh.ParserConfig{ cfg: &argh.ParserConfig{
Prog: argh.CommandConfig{ Prog: argh.CommandConfig{
Flags: map[string]argh.FlagConfig{ Flags: &argh.Flags{
"tasty": {}, Map: map[string]argh.FlagConfig{
"fresh": {}, "tasty": {},
"super-hot-right-now": {}, "fresh": {},
"super-hot-right-now": {},
},
}, },
}, },
}, },
@ -224,13 +313,15 @@ func TestParser(t *testing.T) {
}, },
cfg: &argh.ParserConfig{ cfg: &argh.ParserConfig{
Prog: argh.CommandConfig{ Prog: argh.CommandConfig{
Commands: map[string]argh.CommandConfig{}, Commands: &argh.Commands{Map: map[string]argh.CommandConfig{}},
Flags: map[string]argh.FlagConfig{ Flags: &argh.Flags{
"tasty": {}, Map: map[string]argh.FlagConfig{
"fresh": argh.FlagConfig{NValue: 1}, "tasty": {},
"super-hot-right-now": {}, "fresh": argh.FlagConfig{NValue: 1},
"box": argh.FlagConfig{NValue: argh.OneOrMoreValue}, "super-hot-right-now": {},
"please": {}, "box": argh.FlagConfig{NValue: argh.OneOrMoreValue},
"please": {},
},
}, },
}, },
}, },
@ -301,10 +392,12 @@ func TestParser(t *testing.T) {
args: []string{"pizzas", "-t", "-f", "-s"}, args: []string{"pizzas", "-t", "-f", "-s"},
cfg: &argh.ParserConfig{ cfg: &argh.ParserConfig{
Prog: argh.CommandConfig{ Prog: argh.CommandConfig{
Flags: map[string]argh.FlagConfig{ Flags: &argh.Flags{
"t": {}, Map: map[string]argh.FlagConfig{
"f": {}, "t": {},
"s": {}, "f": {},
"s": {},
},
}, },
}, },
}, },
@ -337,12 +430,14 @@ func TestParser(t *testing.T) {
args: []string{"pizzas", "-aca", "-blol"}, args: []string{"pizzas", "-aca", "-blol"},
cfg: &argh.ParserConfig{ cfg: &argh.ParserConfig{
Prog: argh.CommandConfig{ Prog: argh.CommandConfig{
Flags: map[string]argh.FlagConfig{ Flags: &argh.Flags{
"a": {}, Map: map[string]argh.FlagConfig{
"b": {}, "a": {},
"c": {}, "b": {},
"l": {}, "c": {},
"o": {}, "l": {},
"o": {},
},
}, },
}, },
}, },
@ -390,13 +485,15 @@ func TestParser(t *testing.T) {
args: []string{"pizzas", "-a", "--ca", "-b", "1312", "-lol"}, args: []string{"pizzas", "-a", "--ca", "-b", "1312", "-lol"},
cfg: &argh.ParserConfig{ cfg: &argh.ParserConfig{
Prog: argh.CommandConfig{ Prog: argh.CommandConfig{
Commands: map[string]argh.CommandConfig{}, Commands: &argh.Commands{Map: map[string]argh.CommandConfig{}},
Flags: map[string]argh.FlagConfig{ Flags: &argh.Flags{
"a": {}, Map: map[string]argh.FlagConfig{
"b": argh.FlagConfig{NValue: 1}, "a": {},
"ca": {}, "b": argh.FlagConfig{NValue: 1},
"l": {}, "ca": {},
"o": {}, "l": {},
"o": {},
},
}, },
}, },
}, },
@ -453,18 +550,24 @@ func TestParser(t *testing.T) {
args: []string{"pizzas", "fly", "freely", "sometimes", "and", "other", "times", "fry", "deeply", "--forever"}, args: []string{"pizzas", "fly", "freely", "sometimes", "and", "other", "times", "fry", "deeply", "--forever"},
cfg: &argh.ParserConfig{ cfg: &argh.ParserConfig{
Prog: argh.CommandConfig{ Prog: argh.CommandConfig{
Commands: map[string]argh.CommandConfig{ Commands: &argh.Commands{
"fly": argh.CommandConfig{ Map: map[string]argh.CommandConfig{
Commands: map[string]argh.CommandConfig{ "fly": argh.CommandConfig{
"fry": argh.CommandConfig{ Commands: &argh.Commands{
Flags: map[string]argh.FlagConfig{ Map: map[string]argh.CommandConfig{
"forever": {}, "fry": argh.CommandConfig{
Flags: &argh.Flags{
Map: map[string]argh.FlagConfig{
"forever": {},
},
},
},
}, },
}, },
}, },
}, },
}, },
Flags: map[string]argh.FlagConfig{}, Flags: &argh.Flags{Map: map[string]argh.FlagConfig{}},
}, },
}, },
expPT: []argh.Node{ expPT: []argh.Node{
@ -530,14 +633,16 @@ func TestParser(t *testing.T) {
args: []string{"pizzas", "-need", "sauce", "heat", "love", "-also", "over9000"}, args: []string{"pizzas", "-need", "sauce", "heat", "love", "-also", "over9000"},
cfg: &argh.ParserConfig{ cfg: &argh.ParserConfig{
Prog: argh.CommandConfig{ Prog: argh.CommandConfig{
Flags: map[string]argh.FlagConfig{ Flags: &argh.Flags{
"a": {NValue: argh.ZeroOrMoreValue}, Map: map[string]argh.FlagConfig{
"d": {NValue: argh.OneOrMoreValue}, "a": {NValue: argh.ZeroOrMoreValue},
"e": {}, "d": {NValue: argh.OneOrMoreValue},
"l": {}, "e": {},
"n": {}, "l": {},
"o": {NValue: 1, ValueNames: []string{"level"}}, "n": {},
"s": {NValue: argh.ZeroOrMoreValue}, "o": {NValue: 1, ValueNames: []string{"level"}},
"s": {NValue: argh.ZeroOrMoreValue},
},
}, },
}, },
}, },
@ -631,24 +736,32 @@ func TestParser(t *testing.T) {
args: []string{"pizzas", "fly", "--freely", "fry", "--deeply", "-wAt", "hugs"}, args: []string{"pizzas", "fly", "--freely", "fry", "--deeply", "-wAt", "hugs"},
cfg: &argh.ParserConfig{ cfg: &argh.ParserConfig{
Prog: argh.CommandConfig{ Prog: argh.CommandConfig{
Commands: map[string]argh.CommandConfig{ Commands: &argh.Commands{
"fly": argh.CommandConfig{ Map: map[string]argh.CommandConfig{
Flags: map[string]argh.FlagConfig{ "fly": argh.CommandConfig{
"freely": {}, Flags: &argh.Flags{
}, Map: map[string]argh.FlagConfig{
Commands: map[string]argh.CommandConfig{ "freely": {},
"fry": argh.CommandConfig{ },
Flags: map[string]argh.FlagConfig{ },
"deeply": {}, Commands: &argh.Commands{
"w": {}, Map: map[string]argh.CommandConfig{
"A": {}, "fry": argh.CommandConfig{
"t": argh.FlagConfig{NValue: 1}, Flags: &argh.Flags{
Map: map[string]argh.FlagConfig{
"deeply": {},
"w": {},
"A": {},
"t": argh.FlagConfig{NValue: 1},
},
},
},
}, },
}, },
}, },
}, },
}, },
Flags: map[string]argh.FlagConfig{}, Flags: &argh.Flags{Map: map[string]argh.FlagConfig{}},
}, },
}, },
expPT: []argh.Node{ expPT: []argh.Node{
@ -723,19 +836,25 @@ func TestParser(t *testing.T) {
args: []string{"PIZZAs", "^wAT@golf", "^^hecKing", "goose", "bonk", "^^FIERCENESS@-2"}, args: []string{"PIZZAs", "^wAT@golf", "^^hecKing", "goose", "bonk", "^^FIERCENESS@-2"},
cfg: &argh.ParserConfig{ cfg: &argh.ParserConfig{
Prog: argh.CommandConfig{ Prog: argh.CommandConfig{
Commands: map[string]argh.CommandConfig{ Commands: &argh.Commands{
"goose": argh.CommandConfig{ Map: map[string]argh.CommandConfig{
NValue: 1, "goose": argh.CommandConfig{
Flags: map[string]argh.FlagConfig{ NValue: 1,
"FIERCENESS": argh.FlagConfig{NValue: 1}, Flags: &argh.Flags{
Map: map[string]argh.FlagConfig{
"FIERCENESS": argh.FlagConfig{NValue: 1},
},
},
}, },
}, },
}, },
Flags: map[string]argh.FlagConfig{ Flags: &argh.Flags{
"w": argh.FlagConfig{}, Map: map[string]argh.FlagConfig{
"A": argh.FlagConfig{}, "w": argh.FlagConfig{},
"T": argh.FlagConfig{NValue: 1}, "A": argh.FlagConfig{},
"hecKing": argh.FlagConfig{}, "T": argh.FlagConfig{NValue: 1},
"hecKing": argh.FlagConfig{},
},
}, },
}, },
ScannerConfig: &argh.ScannerConfig{ ScannerConfig: &argh.ScannerConfig{
@ -792,13 +911,17 @@ func TestParser(t *testing.T) {
args: []string{"hotdog", "/f", "/L", "/o:ppy", "hats"}, args: []string{"hotdog", "/f", "/L", "/o:ppy", "hats"},
cfg: &argh.ParserConfig{ cfg: &argh.ParserConfig{
Prog: argh.CommandConfig{ Prog: argh.CommandConfig{
Flags: map[string]argh.FlagConfig{ Flags: &argh.Flags{
"f": {}, Map: map[string]argh.FlagConfig{
"L": {}, "f": {},
"o": argh.FlagConfig{NValue: 1}, "L": {},
"o": argh.FlagConfig{NValue: 1},
},
}, },
Commands: map[string]argh.CommandConfig{ Commands: &argh.Commands{
"hats": {}, Map: map[string]argh.CommandConfig{
"hats": {},
},
}, },
}, },
ScannerConfig: &argh.ScannerConfig{ ScannerConfig: &argh.ScannerConfig{
@ -835,8 +958,10 @@ func TestParser(t *testing.T) {
args: []string{"pizzas", "=", "--wat"}, args: []string{"pizzas", "=", "--wat"},
cfg: &argh.ParserConfig{ cfg: &argh.ParserConfig{
Prog: argh.CommandConfig{ Prog: argh.CommandConfig{
Flags: map[string]argh.FlagConfig{ Flags: &argh.Flags{
"wat": {}, Map: map[string]argh.FlagConfig{
"wat": {},
},
}, },
}, },
}, },

@ -20,10 +20,14 @@ func TestQuerier_Program(t *testing.T) {
args: []string{"pizzas", "ahoy", "--treatsa", "fun"}, args: []string{"pizzas", "ahoy", "--treatsa", "fun"},
cfg: &argh.ParserConfig{ cfg: &argh.ParserConfig{
Prog: argh.CommandConfig{ Prog: argh.CommandConfig{
Commands: map[string]argh.CommandConfig{ Commands: &argh.Commands{
"ahoy": argh.CommandConfig{ Map: map[string]argh.CommandConfig{
Flags: map[string]argh.FlagConfig{ "ahoy": argh.CommandConfig{
"treatsa": argh.FlagConfig{NValue: 1}, Flags: &argh.Flags{
Map: map[string]argh.FlagConfig{
"treatsa": argh.FlagConfig{NValue: 1},
},
},
}, },
}, },
}, },

Loading…
Cancel
Save