Skip to content

Commit 4bb41b9

Browse files
committed
refactor: add CommandInfo pointer to rest of parsing errors
1 parent 295938a commit 4bb41b9

File tree

3 files changed

+75
-58
lines changed

3 files changed

+75
-58
lines changed

cli.go

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ func parse(c *CommandInfo, p *Command, args []string) error {
402402
optName := arg[charIdx]
403403
optInfo := lookupOptionByShortName(c, optName)
404404
if optInfo == nil {
405-
return UnknownOptionError{Cmd: c, Name: "-" + string(arg[charIdx])}
405+
return UnknownOptionError{CmdInfo: c, Name: "-" + string(arg[charIdx])}
406406
}
407407

408408
// If this is another bool option, the raw value will be empty. If this is
@@ -418,7 +418,7 @@ func parse(c *CommandInfo, p *Command, args []string) error {
418418
if i < len(args) {
419419
rawValue = args[i]
420420
} else {
421-
return MissingOptionValueError{Name: string(optName)}
421+
return MissingOptionValueError{CmdInfo: c, Name: string(optName)}
422422
}
423423
} else {
424424
rawValue = arg[charIdx+1:]
@@ -484,7 +484,7 @@ func parse(c *CommandInfo, p *Command, args []string) error {
484484
}
485485
}
486486
if optInfo == nil {
487-
return UnknownOptionError{Cmd: c, Name: args[i]}
487+
return UnknownOptionError{CmdInfo: c, Name: args[i]}
488488
}
489489

490490
var rawValue string
@@ -495,7 +495,7 @@ func parse(c *CommandInfo, p *Command, args []string) error {
495495
if i < len(args) {
496496
rawValue = args[i]
497497
} else {
498-
return MissingOptionValueError{Name: name}
498+
return MissingOptionValueError{CmdInfo: c, Name: name}
499499
}
500500
}
501501

@@ -535,7 +535,7 @@ func parse(c *CommandInfo, p *Command, args []string) error {
535535
}
536536
var errMissingOpts error
537537
if len(missing) > 0 {
538-
errMissingOpts = MissingOptionsError{Names: missing}
538+
errMissingOpts = MissingOptionsError{CmdInfo: c, Names: missing}
539539

540540
// If we are about to parse positional arguments instead of subcommands,
541541
// we can just return this error right now. Otherwise we have to wait
@@ -566,7 +566,7 @@ func parse(c *CommandInfo, p *Command, args []string) error {
566566
}
567567
}
568568
if len(missing) > 0 {
569-
return MissingArgsError{Names: missing}
569+
return MissingArgsError{CmdInfo: c, Names: missing}
570570
}
571571
return nil
572572
} else {
@@ -594,7 +594,7 @@ func parse(c *CommandInfo, p *Command, args []string) error {
594594
}
595595
}
596596
if subcmdInfo == nil {
597-
return UnknownSubcmdError{Cmd: c, Name: rest[0]}
597+
return UnknownSubcmdError{CmdInfo: c, Name: rest[0]}
598598
}
599599
p.Subcmd = &Command{
600600
Inputs: make([]Input, 0, len(rest)),
@@ -649,51 +649,62 @@ func newInput(info *InputInfo, src ParsedFrom, rawValue string) (Input, error) {
649649
var ErrNoSubcmd = errors.New("missing subcommand")
650650

651651
type UnknownSubcmdError struct {
652-
Cmd *CommandInfo
653-
Name string
652+
CmdInfo *CommandInfo
653+
Name string
654654
}
655655

656656
func (usce UnknownSubcmdError) Error() string {
657-
return strings.Join(usce.Cmd.Path, " ") + ": unknown subcommand '" + usce.Name + "'"
657+
return strings.Join(usce.CmdInfo.Path, " ") + ": unknown subcommand '" + usce.Name + "'"
658658
}
659659

660660
type UnknownOptionError struct {
661-
Cmd *CommandInfo
662-
Name string
661+
CmdInfo *CommandInfo
662+
Name string
663663
}
664664

665665
func (uoe UnknownOptionError) Error() string {
666-
return strings.Join(uoe.Cmd.Path, " ") + ": unknown option '" + uoe.Name + "'"
666+
return strings.Join(uoe.CmdInfo.Path, " ") + ": unknown option '" + uoe.Name + "'"
667667
}
668668

669-
type MissingOptionValueError struct{ Name string }
669+
type MissingOptionValueError struct {
670+
CmdInfo *CommandInfo
671+
Name string
672+
}
670673

671674
func (mov MissingOptionValueError) Error() string {
672-
return "option '" + mov.Name + "' requires a value"
675+
return strings.Join(mov.CmdInfo.Path, " ") + ": option '" + mov.Name + "' requires a value"
673676
}
674677

675-
type MissingOptionsError struct{ Names []string }
678+
type MissingOptionsError struct {
679+
CmdInfo *CommandInfo
680+
Names []string
681+
}
676682

677683
func (moe MissingOptionsError) Error() string {
678-
return fmt.Sprintf("missing the following required options: %s", strings.Join(moe.Names, ", "))
684+
return fmt.Sprintf("%s: missing the following required options: %s",
685+
strings.Join(moe.CmdInfo.Path, " "), strings.Join(moe.Names, ", "))
679686
}
680687

681688
func (moe MissingOptionsError) Is(err error) bool {
682689
if e, ok := err.(MissingOptionsError); ok {
683-
return slices.Equal(moe.Names, e.Names)
690+
return moe.CmdInfo == e.CmdInfo && slices.Equal(moe.Names, e.Names)
684691
}
685692
return false
686693
}
687694

688-
type MissingArgsError struct{ Names []string }
695+
type MissingArgsError struct {
696+
CmdInfo *CommandInfo
697+
Names []string
698+
}
689699

690700
func (mae MissingArgsError) Error() string {
691-
return fmt.Sprintf("missing the following required arguments: %s", strings.Join(mae.Names, ", "))
701+
return fmt.Sprintf("%s: missing the following required arguments: %s",
702+
strings.Join(mae.CmdInfo.Path, " "), strings.Join(mae.Names, ", "))
692703
}
693704

694705
func (mae MissingArgsError) Is(err error) bool {
695706
if e, ok := err.(MissingArgsError); ok {
696-
return slices.Equal(mae.Names, e.Names)
707+
return mae.CmdInfo == e.CmdInfo && slices.Equal(mae.Names, e.Names)
697708
}
698709
return false
699710
}

cli_test.go

Lines changed: 41 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -54,27 +54,27 @@ func TestParsing(t *testing.T) {
5454
}, {
5555
Case: ttCase(),
5656
args: []string{"-b", "v2", "--aa"},
57-
expErr: MissingOptionsError{Names: []string{"--cc"}},
57+
expErr: MissingOptionsError{CmdInfo: &tc.cmd, Names: []string{"--cc"}},
5858
}, {
5959
Case: ttCase(),
6060
args: []string{"-z"},
61-
expErr: UnknownOptionError{Cmd: &tc.cmd, Name: "-z"},
61+
expErr: UnknownOptionError{CmdInfo: &tc.cmd, Name: "-z"},
6262
}, {
6363
Case: ttCase(),
6464
args: []string{"--zz=abc"},
65-
expErr: UnknownOptionError{Cmd: &tc.cmd, Name: "--zz=abc"},
65+
expErr: UnknownOptionError{CmdInfo: &tc.cmd, Name: "--zz=abc"},
6666
}, {
6767
Case: ttCase(),
6868
args: []string{"--bb", "B"},
69-
expErr: UnknownOptionError{Cmd: &tc.cmd, Name: "--bb"},
69+
expErr: UnknownOptionError{CmdInfo: &tc.cmd, Name: "--bb"},
7070
}, {
7171
Case: ttCase(),
7272
args: []string{"--dd"},
73-
expErr: MissingOptionValueError{Name: "dd"},
73+
expErr: MissingOptionValueError{CmdInfo: &tc.cmd, Name: "dd"},
7474
}, {
7575
Case: ttCase(),
7676
args: []string{"-b"},
77-
expErr: MissingOptionValueError{Name: "b"},
77+
expErr: MissingOptionValueError{CmdInfo: &tc.cmd, Name: "b"},
7878
},
7979
}
8080
return &tc
@@ -147,19 +147,21 @@ func TestParsing(t *testing.T) {
147147
},
148148
},
149149
},
150-
}, {
150+
}, func() *testCase {
151151
// positional arg stuff
152152
// all required args but not all optional ones
153153
// missing required args error
154154
// args with default values
155155
// surplus
156-
name: "posargs",
157-
cmd: NewCmd("posargs").
158-
Arg(NewArg("arg1").Required()).
159-
Arg(NewArg("arg2").Required().Env("ARG2")).
160-
Arg(NewArg("arg3")).
161-
Arg(NewArg("arg4").Default("Z").Env("ARG4")),
162-
variations: []testInputOutput{
156+
tc := testCase{
157+
name: "posargs",
158+
cmd: NewCmd("posargs").
159+
Arg(NewArg("arg1").Required()).
160+
Arg(NewArg("arg2").Required().Env("ARG2")).
161+
Arg(NewArg("arg3")).
162+
Arg(NewArg("arg4").Default("Z").Env("ARG4")),
163+
}
164+
tc.variations = []testInputOutput{
163165
{
164166
Case: ttCase(),
165167
args: []string{"A", "B", "C", "D", "E", "F"},
@@ -209,14 +211,15 @@ func TestParsing(t *testing.T) {
209211
}, {
210212
Case: ttCase(),
211213
args: []string{},
212-
expErr: MissingArgsError{Names: []string{"arg1", "arg2"}},
214+
expErr: MissingArgsError{CmdInfo: &tc.cmd, Names: []string{"arg1", "arg2"}},
213215
}, {
214216
Case: ttCase(),
215217
args: []string{"A"},
216-
expErr: MissingArgsError{Names: []string{"arg2"}},
218+
expErr: MissingArgsError{CmdInfo: &tc.cmd, Names: []string{"arg2"}},
217219
},
218-
},
219-
}, {
220+
}
221+
return &tc
222+
}(), {
220223
// '--' with '--posarg' after it
221224
// '=' on an option with and without content
222225
// mix of `--opt val`, `--opt=val`, and short names
@@ -349,26 +352,28 @@ func TestParsing(t *testing.T) {
349352
}, {
350353
Case: ttCase(),
351354
args: []string{"three", "--dd", "D"},
352-
expErr: UnknownSubcmdError{Cmd: &tc.cmd, Name: "three"},
355+
expErr: UnknownSubcmdError{CmdInfo: &tc.cmd, Name: "three"},
353356
}, {
354357
Case: ttCase(),
355358
args: []string{"four", "six"},
356-
expErr: UnknownSubcmdError{Cmd: &tc.cmd.Subcmds[2], Name: "six"},
359+
expErr: UnknownSubcmdError{CmdInfo: &tc.cmd.Subcmds[2], Name: "six"},
357360
}, {
358361
Case: ttCase(),
359362
args: []string{"--aa"},
360363
expErr: ErrNoSubcmd,
361364
},
362365
}
363366
return &tc
364-
}(), {
367+
}(), func() *testCase {
365368
// subcommand help won't require required values
366-
name: "subcommands",
367-
cmd: NewCmd("cmd").
368-
Opt(NewOpt("aa").Required()).
369-
Subcmd(NewCmd("one").
370-
Opt(NewOpt("cc").Required())),
371-
variations: []testInputOutput{
369+
tc := testCase{
370+
name: "subcommands",
371+
cmd: NewCmd("cmd").
372+
Opt(NewOpt("aa").Required()).
373+
Subcmd(NewCmd("one").
374+
Opt(NewOpt("cc").Required())),
375+
}
376+
tc.variations = []testInputOutput{
372377
{
373378
Case: ttCase(),
374379
args: []string{"one", "-h"},
@@ -378,10 +383,11 @@ func TestParsing(t *testing.T) {
378383
}, {
379384
Case: ttCase(),
380385
args: []string{"--aa=1", "one"},
381-
expErr: MissingOptionsError{Names: []string{"--cc"}},
386+
expErr: MissingOptionsError{CmdInfo: &tc.cmd.Subcmds[0], Names: []string{"--cc"}},
382387
},
383-
},
384-
}, {
388+
}
389+
return &tc
390+
}(), {
385391
// custom parser
386392
name: "custom_parser",
387393
cmd: NewCmd("cp").
@@ -444,7 +450,7 @@ func TestParsing(t *testing.T) {
444450
}, {
445451
Case: ttCase(),
446452
args: []string{"-cba"},
447-
expErr: MissingOptionValueError{Name: "a"},
453+
expErr: MissingOptionValueError{CmdInfo: &tc.cmd, Name: "a"},
448454
}, {
449455
Case: ttCase(),
450456
args: []string{"-cb", "-a", "valA"},
@@ -485,7 +491,7 @@ func TestParsing(t *testing.T) {
485491
}, {
486492
Case: ttCase(),
487493
args: []string{"-bz"},
488-
expErr: UnknownOptionError{Cmd: &tc.cmd, Name: "-z"},
494+
expErr: UnknownOptionError{CmdInfo: &tc.cmd, Name: "-z"},
489495
}, {
490496
Case: ttCase(),
491497
args: []string{"-aa", "v"},
@@ -558,8 +564,8 @@ func TestParsing(t *testing.T) {
558564
args: []string{"--version"},
559565
expErr: HelpOrVersionRequested{Msg: "(devel)\n"},
560566
},
561-
{Case: ttCase(), args: []string{"-v"}, expErr: UnknownOptionError{Cmd: &tc.cmd, Name: "-v"}},
562-
{Case: ttCase(), args: []string{"-V"}, expErr: UnknownOptionError{Cmd: &tc.cmd, Name: "-V"}},
567+
{Case: ttCase(), args: []string{"-v"}, expErr: UnknownOptionError{CmdInfo: &tc.cmd, Name: "-v"}},
568+
{Case: ttCase(), args: []string{"-V"}, expErr: UnknownOptionError{CmdInfo: &tc.cmd, Name: "-V"}},
563569
}
564570
return &tc
565571
}(), func() *testCase {
@@ -577,7 +583,7 @@ func TestParsing(t *testing.T) {
577583
}, {
578584
Case: ttCase(),
579585
args: []string{"--version"},
580-
expErr: UnknownOptionError{Cmd: &tc.cmd, Name: "--version"},
586+
expErr: UnknownOptionError{CmdInfo: &tc.cmd, Name: "--version"},
581587
},
582588
}
583589
return &tc

examples_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ func ExampleInputInfo_Required_option() {
445445
fmt.Println(err)
446446
// Output:
447447
// hello world
448-
// missing the following required options: -b
448+
// cli.test: missing the following required options: -b
449449
}
450450

451451
func ExampleInputInfo_Required_postionalArgument() {
@@ -463,7 +463,7 @@ func ExampleInputInfo_Required_postionalArgument() {
463463
fmt.Println(err)
464464
// Output:
465465
// hello world
466-
// missing the following required arguments: a
466+
// cli.test: missing the following required arguments: a
467467
}
468468

469469
func ExampleInputInfo_Short() {

0 commit comments

Comments
 (0)