Skip to content

Commit 864da5e

Browse files
committed
Remove ParseAndRun and update examples
1 parent cf5befe commit 864da5e

File tree

5 files changed

+70
-50
lines changed

5 files changed

+70
-50
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,18 @@ root := &cli.Command{
6969
return nil
7070
},
7171
}
72-
if err := cli.ParseAndRun(context.Background(), root, os.Args[1:], nil); err != nil {
72+
if err := cli.Parse(root, os.Args[1:]); err != nil {
7373
if errors.Is(err, flag.ErrHelp) {
74+
fmt.Fprintf(os.Stdout, "%s\n", cli.DefaultUsage(root))
7475
return
7576
}
7677
fmt.Fprintf(os.Stderr, "error: %v\n", err)
7778
os.Exit(1)
7879
}
80+
if err := cli.Run(context.Background(), root, nil); err != nil {
81+
fmt.Fprintf(os.Stderr, "error: %v\n", err)
82+
os.Exit(1)
83+
}
7984
```
8085

8186
This code defines a simple `echo` command that echoes back the input. It supports a `-c` flag to

examples/cmd/echo/main.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,16 @@ func main() {
3636
return nil
3737
},
3838
}
39-
if err := cli.ParseAndRun(context.Background(), root, os.Args[1:], nil); err != nil {
39+
if err := cli.Parse(root, os.Args[1:]); err != nil {
4040
if errors.Is(err, flag.ErrHelp) {
4141
fmt.Fprintf(os.Stdout, "%s\n", cli.DefaultUsage(root))
4242
return
4343
}
4444
fmt.Fprintf(os.Stderr, "error: %v\n", err)
4545
os.Exit(1)
4646
}
47+
if err := cli.Run(context.Background(), root, nil); err != nil {
48+
fmt.Fprintf(os.Stderr, "error: %v\n", err)
49+
os.Exit(1)
50+
}
4751
}

examples/cmd/task/main.go

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,20 +50,16 @@ func main() {
5050
},
5151
}
5252

53-
if err := cli.ParseAndRun(
54-
context.Background(),
55-
root,
56-
os.Args[1:],
57-
nil,
58-
); err != nil {
53+
if err := cli.Parse(root, os.Args[1:]); err != nil {
5954
if errors.Is(err, flag.ErrHelp) {
6055
fmt.Fprintf(os.Stdout, "%s\n", cli.DefaultUsage(root))
6156
return
6257
}
63-
fmt.Fprintf(os.Stderr, "%s\n\nerror: %v\n",
64-
cli.DefaultUsage(root),
65-
err,
66-
)
58+
fmt.Fprintf(os.Stderr, "error: %v\n", err)
59+
os.Exit(1)
60+
}
61+
if err := cli.Run(context.Background(), root, nil); err != nil {
62+
fmt.Fprintf(os.Stderr, "error: %v\n", err)
6763
os.Exit(1)
6864
}
6965
}

run.go

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,6 @@ import (
88
"os"
99
)
1010

11-
// ParseAndRun parses the command hierarchy and runs the command. A convenience function that
12-
// combines [Parse] and [Run] into a single call. See [Parse] and [Run] for more details.
13-
func ParseAndRun(ctx context.Context, root *Command, args []string, options *RunOptions) (retErr error) {
14-
if err := Parse(root, args); err != nil {
15-
return err
16-
}
17-
defer func() {
18-
if r := recover(); r != nil {
19-
switch err := r.(type) {
20-
case error:
21-
retErr = fmt.Errorf("internal: %v", err)
22-
default:
23-
retErr = fmt.Errorf("recovered: %v", r)
24-
}
25-
}
26-
}()
27-
return Run(ctx, root, options)
28-
}
29-
3011
// RunOptions specifies options for running a command.
3112
type RunOptions struct {
3213
// Stdin, Stdout, and Stderr are the standard input, output, and error streams for the command.
@@ -48,10 +29,30 @@ func Run(ctx context.Context, root *Command, options *RunOptions) error {
4829
if root.state == nil || len(root.state.path) == 0 {
4930
return errors.New("command has not been parsed")
5031
}
32+
cmd := root.terminal()
33+
if cmd == nil {
34+
// This should never happen, but if it does, it's likely a bug in the Parse function.
35+
return errors.New("no terminal command found")
36+
}
37+
5138
options = checkAndSetRunOptions(options)
5239
updateState(root.state, options)
5340

54-
return root.terminal().Exec(ctx, root.state)
41+
return run(ctx, cmd, root.state)
42+
}
43+
44+
func run(ctx context.Context, cmd *Command, state *State) (retErr error) {
45+
defer func() {
46+
if r := recover(); r != nil {
47+
switch err := r.(type) {
48+
case error:
49+
retErr = fmt.Errorf("internal: %v", err)
50+
default:
51+
retErr = fmt.Errorf("recover: %v", r)
52+
}
53+
}
54+
}()
55+
return cmd.Exec(ctx, state)
5556
}
5657

5758
func updateState(s *State, opt *RunOptions) {

run_test.go

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,12 @@ import (
1212
func TestRun(t *testing.T) {
1313
t.Parallel()
1414

15-
t.Run("parse and run", func(t *testing.T) {
15+
t.Run("print version", func(t *testing.T) {
1616
t.Parallel()
17-
var count int
1817

1918
root := &Command{
20-
Name: "count",
21-
Usage: "count [flags] [command]",
22-
Flags: FlagsFunc(func(fset *flag.FlagSet) {
23-
fset.Bool("dry-run", false, "dry run")
24-
}),
19+
Name: "printer",
20+
Usage: "printer [flags] [command]",
2521
SubCommands: []*Command{
2622
{
2723
Name: "version",
@@ -32,35 +28,53 @@ func TestRun(t *testing.T) {
3228
},
3329
},
3430
},
31+
Exec: func(ctx context.Context, s *State) error { return nil },
32+
}
33+
err := Parse(root, []string{"version"})
34+
require.NoError(t, err)
35+
36+
output := bytes.NewBuffer(nil)
37+
require.NoError(t, err)
38+
err = Run(context.Background(), root, &RunOptions{Stdout: output})
39+
require.NoError(t, err)
40+
require.Equal(t, "1.0.0\n", output.String())
41+
})
42+
43+
t.Run("parse and run", func(t *testing.T) {
44+
t.Parallel()
45+
var count int
46+
47+
root := &Command{
48+
Name: "count",
49+
Usage: "count [flags] [command]",
50+
Flags: FlagsFunc(func(f *flag.FlagSet) {
51+
f.Bool("dry-run", false, "dry run")
52+
}),
3553
Exec: func(ctx context.Context, s *State) error {
3654
if !GetFlag[bool](s, "dry-run") {
3755
count++
3856
}
3957
return nil
4058
},
4159
}
42-
43-
output := bytes.NewBuffer(nil)
44-
err := ParseAndRun(context.Background(), root, []string{"version"}, &RunOptions{
45-
Stdout: output,
46-
})
60+
err := Parse(root, nil)
4761
require.NoError(t, err)
48-
require.Equal(t, "1.0.0\n", output.String())
49-
output.Reset()
50-
5162
// Run the command 3 times
5263
for i := 0; i < 3; i++ {
53-
err := ParseAndRun(context.Background(), root, nil, nil)
64+
err := Run(context.Background(), root, nil)
5465
require.NoError(t, err)
5566
}
5667
require.Equal(t, 3, count)
5768
// Run with dry-run flag
58-
err = ParseAndRun(context.Background(), root, []string{"--dry-run"}, nil)
69+
err = Parse(root, []string{"--dry-run"})
70+
require.NoError(t, err)
71+
err = Run(context.Background(), root, nil)
5972
require.NoError(t, err)
6073
require.Equal(t, 3, count)
6174
})
6275
t.Run("typo suggestion", func(t *testing.T) {
6376
t.Parallel()
77+
6478
root := &Command{
6579
Name: "count",
6680
Usage: "count [flags] [command]",
@@ -77,7 +91,7 @@ func TestRun(t *testing.T) {
7791
Exec: func(ctx context.Context, s *State) error { return nil },
7892
}
7993

80-
err := ParseAndRun(context.Background(), root, []string{"verzion"}, nil)
94+
err := Parse(root, []string{"verzion"})
8195
require.Error(t, err)
8296
require.Contains(t, err.Error(), `unknown command "verzion". Did you mean one of these?`)
8397
require.Contains(t, err.Error(), ` version`)

0 commit comments

Comments
 (0)