Support persistent flags
This commit is contained in:
parent
395006cdb8
commit
92e3d6fe5b
@ -107,7 +107,9 @@ func (p *parser) parseCommand(cCfg *CommandConfig) Node {
|
||||
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)
|
||||
|
||||
if subCfg, ok := cCfg.Commands[p.lit]; ok {
|
||||
tracef("parseCommand(...) cCfg=%+#v", cCfg)
|
||||
|
||||
if subCfg, ok := cCfg.GetCommandConfig(p.lit); ok {
|
||||
subCommand := p.lit
|
||||
|
||||
nodes = append(nodes, p.parseCommand(&subCfg))
|
||||
@ -186,26 +188,26 @@ func (p *parser) parseIdent() Node {
|
||||
return node
|
||||
}
|
||||
|
||||
func (p *parser) parseFlag(flCfgMap map[string]FlagConfig) Node {
|
||||
func (p *parser) parseFlag(flags *Flags) Node {
|
||||
switch p.tok {
|
||||
case SHORT_FLAG:
|
||||
tracef("parseFlag(...) parsing short flag with config=%+#v", flCfgMap)
|
||||
return p.parseShortFlag(flCfgMap)
|
||||
tracef("parseFlag(...) parsing short flag with config=%+#v", flags)
|
||||
return p.parseShortFlag(flags)
|
||||
case LONG_FLAG:
|
||||
tracef("parseFlag(...) parsing long flag with config=%+#v", flCfgMap)
|
||||
return p.parseLongFlag(flCfgMap)
|
||||
tracef("parseFlag(...) parsing long flag with config=%+#v", flags)
|
||||
return p.parseLongFlag(flags)
|
||||
case COMPOUND_SHORT_FLAG:
|
||||
tracef("parseFlag(...) parsing compound short flag with config=%+#v", flCfgMap)
|
||||
return p.parseCompoundShortFlag(flCfgMap)
|
||||
tracef("parseFlag(...) parsing compound short flag with config=%+#v", flags)
|
||||
return p.parseCompoundShortFlag(flags)
|
||||
}
|
||||
|
||||
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])}
|
||||
|
||||
flCfg, ok := flCfgMap[node.Name]
|
||||
flCfg, ok := flags.Get(node.Name)
|
||||
if !ok {
|
||||
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)
|
||||
}
|
||||
|
||||
func (p *parser) parseLongFlag(flCfgMap map[string]FlagConfig) Node {
|
||||
func (p *parser) parseLongFlag(flags *Flags) Node {
|
||||
node := &Flag{Name: string(p.lit[2:])}
|
||||
|
||||
flCfg, ok := flCfgMap[node.Name]
|
||||
flCfg, ok := flags.Get(node.Name)
|
||||
if !ok {
|
||||
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)
|
||||
}
|
||||
|
||||
func (p *parser) parseCompoundShortFlag(flCfgMap map[string]FlagConfig) Node {
|
||||
func (p *parser) parseCompoundShortFlag(flags *Flags) Node {
|
||||
flagNodes := []Node{}
|
||||
|
||||
withoutFlagPrefix := p.lit[1:]
|
||||
@ -237,7 +239,7 @@ func (p *parser) parseCompoundShortFlag(flCfgMap map[string]FlagConfig) Node {
|
||||
node := &Flag{Name: string(r)}
|
||||
|
||||
if i == len(withoutFlagPrefix)-1 {
|
||||
flCfg, ok := flCfgMap[node.Name]
|
||||
flCfg, ok := flags.Get(node.Name)
|
||||
if !ok {
|
||||
p.addError(fmt.Sprintf("unknown flag %q", node.Name))
|
||||
|
||||
|
@ -36,11 +36,62 @@ type ParserConfig struct {
|
||||
type CommandConfig struct {
|
||||
NValue NValue
|
||||
ValueNames []string
|
||||
Flags map[string]FlagConfig
|
||||
Commands map[string]CommandConfig
|
||||
Flags *Flags
|
||||
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 {
|
||||
NValue NValue
|
||||
Persist bool
|
||||
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{
|
||||
Prog: argh.CommandConfig{
|
||||
Flags: map[string]argh.FlagConfig{
|
||||
"e": {},
|
||||
"a": {},
|
||||
"t": {},
|
||||
"wat": {},
|
||||
Flags: &argh.Flags{
|
||||
Map: map[string]argh.FlagConfig{
|
||||
"e": {},
|
||||
"a": {},
|
||||
"t": {},
|
||||
"wat": {},
|
||||
},
|
||||
},
|
||||
Commands: map[string]argh.CommandConfig{
|
||||
"hello": argh.CommandConfig{
|
||||
NValue: 1,
|
||||
ValueNames: []string{"name"},
|
||||
Commands: &argh.Commands{
|
||||
Map: map[string]argh.CommandConfig{
|
||||
"hello": argh.CommandConfig{
|
||||
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",
|
||||
args: []string{"pizzas"},
|
||||
@ -181,10 +268,12 @@ func TestParser(t *testing.T) {
|
||||
args: []string{"pizzas", "--tasty", "--fresh", "--super-hot-right-now"},
|
||||
cfg: &argh.ParserConfig{
|
||||
Prog: argh.CommandConfig{
|
||||
Flags: map[string]argh.FlagConfig{
|
||||
"tasty": {},
|
||||
"fresh": {},
|
||||
"super-hot-right-now": {},
|
||||
Flags: &argh.Flags{
|
||||
Map: map[string]argh.FlagConfig{
|
||||
"tasty": {},
|
||||
"fresh": {},
|
||||
"super-hot-right-now": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -224,13 +313,15 @@ func TestParser(t *testing.T) {
|
||||
},
|
||||
cfg: &argh.ParserConfig{
|
||||
Prog: argh.CommandConfig{
|
||||
Commands: map[string]argh.CommandConfig{},
|
||||
Flags: map[string]argh.FlagConfig{
|
||||
"tasty": {},
|
||||
"fresh": argh.FlagConfig{NValue: 1},
|
||||
"super-hot-right-now": {},
|
||||
"box": argh.FlagConfig{NValue: argh.OneOrMoreValue},
|
||||
"please": {},
|
||||
Commands: &argh.Commands{Map: map[string]argh.CommandConfig{}},
|
||||
Flags: &argh.Flags{
|
||||
Map: map[string]argh.FlagConfig{
|
||||
"tasty": {},
|
||||
"fresh": argh.FlagConfig{NValue: 1},
|
||||
"super-hot-right-now": {},
|
||||
"box": argh.FlagConfig{NValue: argh.OneOrMoreValue},
|
||||
"please": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -301,10 +392,12 @@ func TestParser(t *testing.T) {
|
||||
args: []string{"pizzas", "-t", "-f", "-s"},
|
||||
cfg: &argh.ParserConfig{
|
||||
Prog: argh.CommandConfig{
|
||||
Flags: map[string]argh.FlagConfig{
|
||||
"t": {},
|
||||
"f": {},
|
||||
"s": {},
|
||||
Flags: &argh.Flags{
|
||||
Map: map[string]argh.FlagConfig{
|
||||
"t": {},
|
||||
"f": {},
|
||||
"s": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -337,12 +430,14 @@ func TestParser(t *testing.T) {
|
||||
args: []string{"pizzas", "-aca", "-blol"},
|
||||
cfg: &argh.ParserConfig{
|
||||
Prog: argh.CommandConfig{
|
||||
Flags: map[string]argh.FlagConfig{
|
||||
"a": {},
|
||||
"b": {},
|
||||
"c": {},
|
||||
"l": {},
|
||||
"o": {},
|
||||
Flags: &argh.Flags{
|
||||
Map: map[string]argh.FlagConfig{
|
||||
"a": {},
|
||||
"b": {},
|
||||
"c": {},
|
||||
"l": {},
|
||||
"o": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -390,13 +485,15 @@ func TestParser(t *testing.T) {
|
||||
args: []string{"pizzas", "-a", "--ca", "-b", "1312", "-lol"},
|
||||
cfg: &argh.ParserConfig{
|
||||
Prog: argh.CommandConfig{
|
||||
Commands: map[string]argh.CommandConfig{},
|
||||
Flags: map[string]argh.FlagConfig{
|
||||
"a": {},
|
||||
"b": argh.FlagConfig{NValue: 1},
|
||||
"ca": {},
|
||||
"l": {},
|
||||
"o": {},
|
||||
Commands: &argh.Commands{Map: map[string]argh.CommandConfig{}},
|
||||
Flags: &argh.Flags{
|
||||
Map: map[string]argh.FlagConfig{
|
||||
"a": {},
|
||||
"b": argh.FlagConfig{NValue: 1},
|
||||
"ca": {},
|
||||
"l": {},
|
||||
"o": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -453,18 +550,24 @@ func TestParser(t *testing.T) {
|
||||
args: []string{"pizzas", "fly", "freely", "sometimes", "and", "other", "times", "fry", "deeply", "--forever"},
|
||||
cfg: &argh.ParserConfig{
|
||||
Prog: argh.CommandConfig{
|
||||
Commands: map[string]argh.CommandConfig{
|
||||
"fly": argh.CommandConfig{
|
||||
Commands: map[string]argh.CommandConfig{
|
||||
"fry": argh.CommandConfig{
|
||||
Flags: map[string]argh.FlagConfig{
|
||||
"forever": {},
|
||||
Commands: &argh.Commands{
|
||||
Map: map[string]argh.CommandConfig{
|
||||
"fly": argh.CommandConfig{
|
||||
Commands: &argh.Commands{
|
||||
Map: map[string]argh.CommandConfig{
|
||||
"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{
|
||||
@ -530,14 +633,16 @@ func TestParser(t *testing.T) {
|
||||
args: []string{"pizzas", "-need", "sauce", "heat", "love", "-also", "over9000"},
|
||||
cfg: &argh.ParserConfig{
|
||||
Prog: argh.CommandConfig{
|
||||
Flags: map[string]argh.FlagConfig{
|
||||
"a": {NValue: argh.ZeroOrMoreValue},
|
||||
"d": {NValue: argh.OneOrMoreValue},
|
||||
"e": {},
|
||||
"l": {},
|
||||
"n": {},
|
||||
"o": {NValue: 1, ValueNames: []string{"level"}},
|
||||
"s": {NValue: argh.ZeroOrMoreValue},
|
||||
Flags: &argh.Flags{
|
||||
Map: map[string]argh.FlagConfig{
|
||||
"a": {NValue: argh.ZeroOrMoreValue},
|
||||
"d": {NValue: argh.OneOrMoreValue},
|
||||
"e": {},
|
||||
"l": {},
|
||||
"n": {},
|
||||
"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"},
|
||||
cfg: &argh.ParserConfig{
|
||||
Prog: argh.CommandConfig{
|
||||
Commands: map[string]argh.CommandConfig{
|
||||
"fly": argh.CommandConfig{
|
||||
Flags: map[string]argh.FlagConfig{
|
||||
"freely": {},
|
||||
},
|
||||
Commands: map[string]argh.CommandConfig{
|
||||
"fry": argh.CommandConfig{
|
||||
Flags: map[string]argh.FlagConfig{
|
||||
"deeply": {},
|
||||
"w": {},
|
||||
"A": {},
|
||||
"t": argh.FlagConfig{NValue: 1},
|
||||
Commands: &argh.Commands{
|
||||
Map: map[string]argh.CommandConfig{
|
||||
"fly": argh.CommandConfig{
|
||||
Flags: &argh.Flags{
|
||||
Map: map[string]argh.FlagConfig{
|
||||
"freely": {},
|
||||
},
|
||||
},
|
||||
Commands: &argh.Commands{
|
||||
Map: map[string]argh.CommandConfig{
|
||||
"fry": argh.CommandConfig{
|
||||
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{
|
||||
@ -723,19 +836,25 @@ func TestParser(t *testing.T) {
|
||||
args: []string{"PIZZAs", "^wAT@golf", "^^hecKing", "goose", "bonk", "^^FIERCENESS@-2"},
|
||||
cfg: &argh.ParserConfig{
|
||||
Prog: argh.CommandConfig{
|
||||
Commands: map[string]argh.CommandConfig{
|
||||
"goose": argh.CommandConfig{
|
||||
NValue: 1,
|
||||
Flags: map[string]argh.FlagConfig{
|
||||
"FIERCENESS": argh.FlagConfig{NValue: 1},
|
||||
Commands: &argh.Commands{
|
||||
Map: map[string]argh.CommandConfig{
|
||||
"goose": argh.CommandConfig{
|
||||
NValue: 1,
|
||||
Flags: &argh.Flags{
|
||||
Map: map[string]argh.FlagConfig{
|
||||
"FIERCENESS": argh.FlagConfig{NValue: 1},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Flags: map[string]argh.FlagConfig{
|
||||
"w": argh.FlagConfig{},
|
||||
"A": argh.FlagConfig{},
|
||||
"T": argh.FlagConfig{NValue: 1},
|
||||
"hecKing": argh.FlagConfig{},
|
||||
Flags: &argh.Flags{
|
||||
Map: map[string]argh.FlagConfig{
|
||||
"w": argh.FlagConfig{},
|
||||
"A": argh.FlagConfig{},
|
||||
"T": argh.FlagConfig{NValue: 1},
|
||||
"hecKing": argh.FlagConfig{},
|
||||
},
|
||||
},
|
||||
},
|
||||
ScannerConfig: &argh.ScannerConfig{
|
||||
@ -792,13 +911,17 @@ func TestParser(t *testing.T) {
|
||||
args: []string{"hotdog", "/f", "/L", "/o:ppy", "hats"},
|
||||
cfg: &argh.ParserConfig{
|
||||
Prog: argh.CommandConfig{
|
||||
Flags: map[string]argh.FlagConfig{
|
||||
"f": {},
|
||||
"L": {},
|
||||
"o": argh.FlagConfig{NValue: 1},
|
||||
Flags: &argh.Flags{
|
||||
Map: map[string]argh.FlagConfig{
|
||||
"f": {},
|
||||
"L": {},
|
||||
"o": argh.FlagConfig{NValue: 1},
|
||||
},
|
||||
},
|
||||
Commands: map[string]argh.CommandConfig{
|
||||
"hats": {},
|
||||
Commands: &argh.Commands{
|
||||
Map: map[string]argh.CommandConfig{
|
||||
"hats": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
ScannerConfig: &argh.ScannerConfig{
|
||||
@ -835,8 +958,10 @@ func TestParser(t *testing.T) {
|
||||
args: []string{"pizzas", "=", "--wat"},
|
||||
cfg: &argh.ParserConfig{
|
||||
Prog: argh.CommandConfig{
|
||||
Flags: map[string]argh.FlagConfig{
|
||||
"wat": {},
|
||||
Flags: &argh.Flags{
|
||||
Map: map[string]argh.FlagConfig{
|
||||
"wat": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -20,10 +20,14 @@ func TestQuerier_Program(t *testing.T) {
|
||||
args: []string{"pizzas", "ahoy", "--treatsa", "fun"},
|
||||
cfg: &argh.ParserConfig{
|
||||
Prog: argh.CommandConfig{
|
||||
Commands: map[string]argh.CommandConfig{
|
||||
"ahoy": argh.CommandConfig{
|
||||
Flags: map[string]argh.FlagConfig{
|
||||
"treatsa": argh.FlagConfig{NValue: 1},
|
||||
Commands: &argh.Commands{
|
||||
Map: map[string]argh.CommandConfig{
|
||||
"ahoy": argh.CommandConfig{
|
||||
Flags: &argh.Flags{
|
||||
Map: map[string]argh.FlagConfig{
|
||||
"treatsa": argh.FlagConfig{NValue: 1},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user