Skip to content

Commit b182164

Browse files
committed
refactor: add CommandInfo pointer to UnknownOptionError
1 parent fa1f134 commit b182164

3 files changed

Lines changed: 64 additions & 50 deletions

File tree

cli.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ type InputInfo struct {
125125
Versioner Versioner
126126
}
127127

128-
// ValueParser describes any function that takes a string and returns some type or an
128+
// ValueParser describes any function that takes a string and returns some value or an
129129
// error. This is the signature of any input value parser. See [ParseBool], [ParseInt],
130130
// and the other provided parsers for some examples.
131131
type ValueParser = func(string) (any, error)
@@ -400,7 +400,7 @@ func parse(c *CommandInfo, p *Command, args []string) error {
400400
optName := arg[charIdx]
401401
optInfo := lookupOptionByShortName(c, optName)
402402
if optInfo == nil {
403-
return UnknownOptionError{Name: "-" + string(arg[charIdx])}
403+
return UnknownOptionError{Cmd: c, Name: "-" + string(arg[charIdx])}
404404
}
405405

406406
// If this is another bool option, the raw value will be empty. If this is
@@ -482,7 +482,7 @@ func parse(c *CommandInfo, p *Command, args []string) error {
482482
}
483483
}
484484
if optInfo == nil {
485-
return UnknownOptionError{Name: args[i]}
485+
return UnknownOptionError{Cmd: c, Name: args[i]}
486486
}
487487

488488
var rawValue string
@@ -516,8 +516,6 @@ func parse(c *CommandInfo, p *Command, args []string) error {
516516
p.Inputs = append(p.Inputs, pi)
517517
}
518518

519-
var errMissingOpts error
520-
521519
// check that all required options were provided
522520
var missing []string
523521
for i := range c.Opts {
@@ -533,6 +531,7 @@ func parse(c *CommandInfo, p *Command, args []string) error {
533531
}
534532
}
535533
}
534+
var errMissingOpts error
536535
if len(missing) > 0 {
537536
errMissingOpts = MissingOptionsError{Names: missing}
538537

@@ -650,10 +649,13 @@ func (usce UnknownSubcmdError) Error() string {
650649
return "unknown subcommand '" + usce.Name + "'"
651650
}
652651

653-
type UnknownOptionError struct{ Name string }
652+
type UnknownOptionError struct {
653+
Cmd *CommandInfo
654+
Name string
655+
}
654656

655657
func (uoe UnknownOptionError) Error() string {
656-
return "unknown option '" + uoe.Name + "'"
658+
return strings.Join(uoe.Cmd.Path, " ") + ": unknown option '" + uoe.Name + "'"
657659
}
658660

659661
type MissingOptionValueError struct{ Name string }

cli_test.go

Lines changed: 54 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,20 @@ func TestParsing(t *testing.T) {
2626
variations []testInputOutput
2727
}
2828

29-
for _, tt := range []testCase{
30-
{
31-
// no positional args or subcommands
32-
// errors for missing required opts
33-
name: "options_only",
34-
cmd: NewCmd("optsonly").
35-
Opt(NewBoolOpt("aa")).
36-
Opt(NewOpt("bb").ShortOnly('b')).
37-
Opt(NewOpt("cc").Required()).
38-
Opt(NewOpt("dd").Default("v4")).
39-
Opt(NewOpt("ee")),
40-
variations: []testInputOutput{
29+
for _, tt := range []*testCase{
30+
func() *testCase {
31+
tc := testCase{
32+
// no positional args or subcommands
33+
// errors for missing required opts
34+
name: "options_only",
35+
cmd: NewCmd("optsonly").
36+
Opt(NewBoolOpt("aa")).
37+
Opt(NewOpt("bb").ShortOnly('b')).
38+
Opt(NewOpt("cc").Required()).
39+
Opt(NewOpt("dd").Default("v4")).
40+
Opt(NewOpt("ee")),
41+
}
42+
tc.variations = []testInputOutput{
4143
{
4244
Case: ttCase(),
4345
args: []string{"-b", "v2", "--aa", "--cc=v3"},
@@ -56,15 +58,15 @@ func TestParsing(t *testing.T) {
5658
}, {
5759
Case: ttCase(),
5860
args: []string{"-z"},
59-
expErr: UnknownOptionError{Name: "-z"},
61+
expErr: UnknownOptionError{Cmd: &tc.cmd, Name: "-z"},
6062
}, {
6163
Case: ttCase(),
6264
args: []string{"--zz=abc"},
63-
expErr: UnknownOptionError{Name: "--zz=abc"},
65+
expErr: UnknownOptionError{Cmd: &tc.cmd, Name: "--zz=abc"},
6466
}, {
6567
Case: ttCase(),
6668
args: []string{"--bb", "B"},
67-
expErr: UnknownOptionError{Name: "--bb"},
69+
expErr: UnknownOptionError{Cmd: &tc.cmd, Name: "--bb"},
6870
}, {
6971
Case: ttCase(),
7072
args: []string{"--dd"},
@@ -74,8 +76,9 @@ func TestParsing(t *testing.T) {
7476
args: []string{"-b"},
7577
expErr: MissingOptionValueError{Name: "b"},
7678
},
77-
},
78-
}, {
79+
}
80+
return &tc
81+
}(), {
7982
// all provided parsers with defaults
8083
name: "provided_parsers",
8184
cmd: NewCmd("pp").
@@ -392,14 +395,16 @@ func TestParsing(t *testing.T) {
392395
},
393396
},
394397
},
395-
}, {
396-
// stacking / bunching short options and their values
397-
name: "shortstacks",
398-
cmd: NewCmd("shst").
399-
Opt(NewBoolOpt("bb").Short('b')).
400-
Opt(NewOpt("aa").Short('a')).
401-
Opt(NewBoolOpt("cc").Short('c')),
402-
variations: []testInputOutput{
398+
}, func() *testCase {
399+
tc := testCase{
400+
// stacking / bunching short options and their values
401+
name: "shortstacks",
402+
cmd: NewCmd("shst").
403+
Opt(NewBoolOpt("bb").Short('b')).
404+
Opt(NewOpt("aa").Short('a')).
405+
Opt(NewBoolOpt("cc").Short('c')),
406+
}
407+
tc.variations = []testInputOutput{
403408
{
404409
Case: ttCase(),
405410
args: []string{"-bc"},
@@ -472,7 +477,7 @@ func TestParsing(t *testing.T) {
472477
}, {
473478
Case: ttCase(),
474479
args: []string{"-bz"},
475-
expErr: UnknownOptionError{Name: "-z"},
480+
expErr: UnknownOptionError{Cmd: &tc.cmd, Name: "-z"},
476481
}, {
477482
Case: ttCase(),
478483
args: []string{"-aa", "v"},
@@ -483,8 +488,9 @@ func TestParsing(t *testing.T) {
483488
Surplus: []string{"v"},
484489
},
485490
},
486-
},
487-
}, {
491+
}
492+
return &tc
493+
}(), {
488494
// versioning (on just the top level for now)
489495
// using the builder method for a custom versioner
490496
name: "versioning",
@@ -531,37 +537,43 @@ func TestParsing(t *testing.T) {
531537
expErr: HelpOrVersionRequested{Msg: "(devel)\n"},
532538
},
533539
},
534-
}, {
540+
}, func() *testCase {
535541
// versioning (on just the top level for now)
536542
// using the constructor for a version option but leaving out a short name
537-
name: "versioning",
538-
cmd: NewCmd("cmd").Opt(NewVersionOpt(0, "version", VersionOptConfig{})),
539-
variations: []testInputOutput{
543+
tc := testCase{
544+
name: "versioning",
545+
cmd: NewCmd("cmd").Opt(NewVersionOpt(0, "version", VersionOptConfig{})),
546+
}
547+
tc.variations = []testInputOutput{
540548
{
541549
Case: ttCase(),
542550
args: []string{"--version"},
543551
expErr: HelpOrVersionRequested{Msg: "(devel)\n"},
544552
},
545-
{Case: ttCase(), args: []string{"-v"}, expErr: UnknownOptionError{Name: "-v"}},
546-
{Case: ttCase(), args: []string{"-V"}, expErr: UnknownOptionError{Name: "-V"}},
547-
},
548-
}, {
553+
{Case: ttCase(), args: []string{"-v"}, expErr: UnknownOptionError{Cmd: &tc.cmd, Name: "-v"}},
554+
{Case: ttCase(), args: []string{"-V"}, expErr: UnknownOptionError{Cmd: &tc.cmd, Name: "-V"}},
555+
}
556+
return &tc
557+
}(), func() *testCase {
549558
// versioning (on just the top level for now)
550559
// using the constructor for a version option but leaving out a long name
551-
name: "versioning",
552-
cmd: NewCmd("cmd").Opt(NewVersionOpt('Z', "", VersionOptConfig{})),
553-
variations: []testInputOutput{
560+
tc := testCase{
561+
name: "versioning",
562+
cmd: NewCmd("cmd").Opt(NewVersionOpt('Z', "", VersionOptConfig{})),
563+
}
564+
tc.variations = []testInputOutput{
554565
{
555566
Case: ttCase(),
556567
args: []string{"-Z"},
557568
expErr: HelpOrVersionRequested{Msg: "(devel)\n"},
558569
}, {
559570
Case: ttCase(),
560571
args: []string{"--version"},
561-
expErr: UnknownOptionError{Name: "--version"},
572+
expErr: UnknownOptionError{Cmd: &tc.cmd, Name: "--version"},
562573
},
563-
},
564-
},
574+
}
575+
return &tc
576+
}(),
565577
} {
566578
for tioIdx, tio := range tt.variations {
567579
t.Run(fmt.Sprintf("%s %d", tt.name, tioIdx), func(t *testing.T) {

examples_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func ExampleInputInfo_ShortOnly() {
5050
fmt.Println(err)
5151
// Output:
5252
// hello
53-
// unknown option '--flag'
53+
// cli.test: unknown option '--flag'
5454
}
5555

5656
func ExampleInputInfo_Help() {

0 commit comments

Comments
 (0)