Skip to content

Commit

Permalink
Merge pull request #3 from urfave/on-err-handle
Browse files Browse the repository at this point in the history
Interrupt parsing on `On` func error
  • Loading branch information
meatballhat committed Jul 8, 2023
2 parents 9fc0402 + 35ee292 commit cafe4c3
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 48 deletions.
2 changes: 1 addition & 1 deletion argh.go
Expand Up @@ -13,7 +13,7 @@ var (
isTracingOn = os.Getenv("ARGH_TRACING") == "on"
traceLogger *log.Logger

Error = errors.New("argh error")
Err = errors.New("argh error")
)

func init() {
Expand Down
24 changes: 16 additions & 8 deletions argh_test.go
Expand Up @@ -80,76 +80,84 @@ func BenchmarkArgh(b *testing.B) {
Flags: &argh.Flags{
Map: map[string]argh.FlagConfig{
"ok": {
On: func(fl argh.CommandFlag) {
On: func(fl argh.CommandFlag) error {
okFlag = ptrTo(true)
return nil
},
},
"dur": {
NValue: 1,
On: func(fl argh.CommandFlag) {
On: func(fl argh.CommandFlag) error {
if v, ok := fl.Values["0"]; ok {
if pt, err := time.ParseDuration(v); err != nil {
durFlag = ptrTo(pt)
}
}
return nil
},
},
"f64": {
NValue: 1,
On: func(fl argh.CommandFlag) {
On: func(fl argh.CommandFlag) error {
if v, ok := fl.Values["0"]; ok {
if f, err := strconv.ParseFloat(v, 64); err == nil {
f64Flag = ptrTo(f)
}
}
return nil
},
},
"i": {
NValue: 1,
On: func(fl argh.CommandFlag) {
On: func(fl argh.CommandFlag) error {
if v, ok := fl.Values["0"]; ok {
if i, err := strconv.ParseInt(v, 10, 64); err == nil {
iFlag = ptrTo(int(i))
}
}
return nil
},
},
"i64": {
NValue: 1,
On: func(fl argh.CommandFlag) {
On: func(fl argh.CommandFlag) error {
if v, ok := fl.Values["0"]; ok {
if i, err := strconv.ParseInt(v, 10, 64); err == nil {
i64Flag = ptrTo(i)
}
}
return nil
},
},
"s": {
NValue: 1,
On: func(fl argh.CommandFlag) {
On: func(fl argh.CommandFlag) error {
if v, ok := fl.Values["0"]; ok {
sFlag = ptrTo(v)
}
return nil
},
},
"u": {
NValue: 1,
On: func(fl argh.CommandFlag) {
On: func(fl argh.CommandFlag) error {
if v, ok := fl.Values["0"]; ok {
if u, err := strconv.ParseUint(v, 10, 64); err == nil {
uFlag = ptrTo(uint(u))
}
}
return nil
},
},
"u64": {
NValue: 1,
On: func(fl argh.CommandFlag) {
On: func(fl argh.CommandFlag) error {
if v, ok := fl.Values["0"]; ok {
if u, err := strconv.ParseUint(v, 10, 64); err == nil {
u64Flag = ptrTo(u)
}
}
return nil
},
},
},
Expand Down
20 changes: 15 additions & 5 deletions example_test.go
Expand Up @@ -15,57 +15,67 @@ func ExampleParserConfig() {
pCfg := argh.NewParserConfig()
pCfg.Prog.NValue = argh.OneOrMoreValue
pCfg.Prog.ValueNames = []string{"val"}
pCfg.Prog.On = func(cf argh.CommandFlag) {
pCfg.Prog.On = func(cf argh.CommandFlag) error {
state["prog"] = cf

fmt.Printf("prog Name: %[1]q\n", cf.Name)
fmt.Printf("prog Values: %[1]q\n", cf.Values)
fmt.Printf("prog len(Nodes): %[1]v\n", len(cf.Nodes))

return nil
}

pCfg.Prog.SetFlagConfig("a", &argh.FlagConfig{
NValue: 2,
On: func(cf argh.CommandFlag) {
On: func(cf argh.CommandFlag) error {
state["a"] = cf

fmt.Printf("prog -a Name: %[1]q\n", cf.Name)
fmt.Printf("prog -a Values: %[1]q\n", cf.Values)
fmt.Printf("prog -a len(Nodes): %[1]v\n", len(cf.Nodes))

return nil
},
})

pCfg.Prog.SetFlagConfig("b", &argh.FlagConfig{
Persist: true,
On: func(cf argh.CommandFlag) {
On: func(cf argh.CommandFlag) error {
state["b"] = cf

fmt.Printf("prog -b Name: %[1]q\n", cf.Name)
fmt.Printf("prog -b Values: %[1]q\n", cf.Values)
fmt.Printf("prog -b len(Nodes): %[1]v\n", len(cf.Nodes))

return nil
},
})

sub := &argh.CommandConfig{
NValue: 3,
ValueNames: []string{"pilot", "navigator", "comms"},
On: func(cf argh.CommandFlag) {
On: func(cf argh.CommandFlag) error {
state["sub"] = cf

fmt.Printf("prog sub Name: %[1]q\n", cf.Name)
fmt.Printf("prog sub Values: %[1]q\n", cf.Values)
fmt.Printf("prog sub len(Nodes): %[1]v\n", len(cf.Nodes))

return nil
},
}

sub.SetFlagConfig("c", &argh.FlagConfig{
NValue: 1,
ValueNames: []string{"feels"},
On: func(cf argh.CommandFlag) {
On: func(cf argh.CommandFlag) error {
state["c"] = cf

fmt.Printf("prog sub -c Name: %[1]q\n", cf.Name)
fmt.Printf("prog sub -c Values: %[1]q\n", cf.Values)
fmt.Printf("prog sub -c len(Nodes): %[1]v\n", len(cf.Nodes))

return nil
},
})

Expand Down
90 changes: 59 additions & 31 deletions parser.go
Expand Up @@ -47,7 +47,7 @@ func (p *parser) init(r io.Reader, pCfg *ParserConfig) error {
p.errors = ParserErrorList{}

if pCfg == nil {
return fmt.Errorf("nil parser config: %w", Error)
return fmt.Errorf("nil parser config: %w", Err)
}

p.cfg = pCfg
Expand All @@ -66,7 +66,10 @@ func (p *parser) parseArgs() (*ParseTree, error) {
}

tracef("parseArgs() parsing %q as program command; cfg=%+#v", p.lit, p.cfg.Prog)
prog := p.parseCommand(p.cfg.Prog)
prog, err := p.parseCommand(p.cfg.Prog)
if err != nil {
return nil, err
}

tracef("parseArgs() top level node is %T", prog)

Expand All @@ -89,7 +92,7 @@ func (p *parser) next() {
tracef("next() after scan: %v %q %v", p.tok, p.lit, p.pos)
}

func (p *parser) parseCommand(cCfg *CommandConfig) Node {
func (p *parser) parseCommand(cCfg *CommandConfig) (Node, error) {
tracef("parseCommand(%+#v)", cCfg)

node := &CommandFlag{
Expand Down Expand Up @@ -117,7 +120,12 @@ func (p *parser) parseCommand(cCfg *CommandConfig) Node {
if subCfg, ok := cCfg.GetCommandConfig(p.lit); ok {
subCommand := p.lit

nodes = append(nodes, p.parseCommand(&subCfg))
subNode, err := p.parseCommand(&subCfg)
if err != nil {
return node, err
}

nodes = append(nodes, subNode)

tracef("parseCommand(...) breaking after sub-command=%v", subCommand)
break
Expand Down Expand Up @@ -159,7 +167,10 @@ func (p *parser) parseCommand(cCfg *CommandConfig) Node {
case LONG_FLAG, SHORT_FLAG, COMPOUND_SHORT_FLAG:
tok := p.tok

flagNode := p.parseFlag(cCfg.Flags)
flagNode, err := p.parseFlag(cCfg.Flags)
if err != nil {
return node, err
}

tracef("parseCommand(...) appending %s node=%+#v", tok, flagNode)

Expand All @@ -186,21 +197,23 @@ func (p *parser) parseCommand(cCfg *CommandConfig) Node {

if cCfg.On != nil {
tracef("parseCommand(...) calling command config handler for node=%+#v", node)
cCfg.On(*node)
if err := cCfg.On(*node); err != nil {
return node, err
}
} else {
tracef("parseCommand(...) no command config handler for node=%+#v", node)
}

tracef("parseCommand(...) returning node=%+#v", node)
return node
return node, nil
}

func (p *parser) parseIdent() Node {
node := &Ident{Literal: p.lit}
return node
}

func (p *parser) parseFlag(flags *Flags) Node {
func (p *parser) parseFlag(flags *Flags) (Node, error) {
switch p.tok {
case SHORT_FLAG:
tracef("parseFlag(...) parsing short flag with config=%+#v", flags)
Expand All @@ -216,33 +229,35 @@ func (p *parser) parseFlag(flags *Flags) Node {
panic(fmt.Sprintf("token %v cannot be parsed as flag", p.tok))
}

func (p *parser) parseShortFlag(flags *Flags) Node {
func (p *parser) parseShortFlag(flags *Flags) (Node, error) {
node := &CommandFlag{Name: string(p.lit[1])}

flCfg, ok := flags.Get(node.Name)
if !ok {
p.addError(fmt.Sprintf("unknown flag %[1]q", node.Name))
errMsg := fmt.Sprintf("unknown flag %[1]q", node.Name)
p.addError(errMsg)

return node
return node, fmt.Errorf(errMsg+": %[1]w", Err)
}

return p.parseConfiguredFlag(node, flCfg, nil)
}

func (p *parser) parseLongFlag(flags *Flags) Node {
func (p *parser) parseLongFlag(flags *Flags) (Node, error) {
node := &CommandFlag{Name: string(p.lit[2:])}

flCfg, ok := flags.Get(node.Name)
if !ok {
p.addError(fmt.Sprintf("unknown flag %[1]q", node.Name))
errMsg := fmt.Sprintf("unknown flag %[1]q", node.Name)
p.addError(errMsg)

return node
return node, fmt.Errorf(errMsg+": %[1]w", Err)
}

return p.parseConfiguredFlag(node, flCfg, nil)
}

func (p *parser) parseCompoundShortFlag(flags *Flags) Node {
func (p *parser) parseCompoundShortFlag(flags *Flags) (Node, error) {
unparsedFlags := []*CommandFlag{}
unparsedFlagConfigs := []FlagConfig{}

Expand All @@ -253,9 +268,10 @@ func (p *parser) parseCompoundShortFlag(flags *Flags) Node {

flCfg, ok := flags.Get(node.Name)
if !ok {
p.addError(fmt.Sprintf("unknown flag %[1]q", node.Name))
errMsg := fmt.Sprintf("unknown flag %[1]q", node.Name)
p.addError(errMsg)

continue
return nil, fmt.Errorf(errMsg+": %[1]w", Err)
}

unparsedFlags = append(unparsedFlags, node)
Expand All @@ -273,33 +289,43 @@ func (p *parser) parseCompoundShortFlag(flags *Flags) Node {
// group, it will be parsed with an override NValue of
// ZeroValue so that it does not consume the next token.
if flCfg.NValue.Required() {
errMsg := fmt.Sprintf(
"short flag %[1]q before end of compound group expects value",
node.Name,
)
p.addError(
fmt.Sprintf(
"short flag %[1]q before end of compound group expects value",
node.Name,
),
errMsg,
)

return nil, fmt.Errorf(errMsg+": %[1]w", Err)
}

flagNode, err := p.parseConfiguredFlag(node, flCfg, zeroValuePtr)
if err != nil {
return nil, err
}

flagNodes = append(
flagNodes,
p.parseConfiguredFlag(node, flCfg, zeroValuePtr),
)
flagNodes = append(flagNodes, flagNode)

continue
}

flagNodes = append(flagNodes, p.parseConfiguredFlag(node, flCfg, nil))
flagNode, err := p.parseConfiguredFlag(node, flCfg, nil)
if err != nil {
return nil, err
}

flagNodes = append(flagNodes, flagNode)
}

return &CompoundShortFlag{Nodes: flagNodes}
return &CompoundShortFlag{Nodes: flagNodes}, nil
}

func (p *parser) parseConfiguredFlag(node *CommandFlag, flCfg FlagConfig, nValueOverride *NValue) Node {
func (p *parser) parseConfiguredFlag(node *CommandFlag, flCfg FlagConfig, nValueOverride *NValue) (Node, error) {
values := map[string]string{}
nodes := []Node{}

atExit := func() *CommandFlag {
atExit := func() (*CommandFlag, error) {
if len(nodes) > 0 {
node.Nodes = nodes
}
Expand All @@ -310,12 +336,14 @@ func (p *parser) parseConfiguredFlag(node *CommandFlag, flCfg FlagConfig, nValue

if flCfg.On != nil {
tracef("parseConfiguredFlag(...) calling flag config handler for node=%+#[1]v", node)
flCfg.On(*node)
if err := flCfg.On(*node); err != nil {
return nil, err
}
} else {
tracef("parseConfiguredFlag(...) no flag config handler for node=%+#[1]v", node)
}

return node
return node, nil
}

identIndex := 0
Expand Down

0 comments on commit cafe4c3

Please sign in to comment.