Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interrupt parsing on On func error #3

Merged
merged 1 commit into from
Jul 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion argh.go
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Loading