|
4 | 4 | "context" |
5 | 5 | "errors" |
6 | 6 | "flag" |
| 7 | + "fmt" |
7 | 8 | "os" |
8 | 9 | "regexp" |
9 | 10 | "strings" |
|
18 | 19 | // Returned when an [ExecuteOption] provided to [Execute] is illegal. |
19 | 20 | ErrIllegalExecuteOptions = errors.New("cmder: illegal command execution option") |
20 | 21 |
|
21 | | - // Returned when an [ExecuteOption] provided to [Execute] is illegal. |
| 22 | + // Returned when failed to update flag value from environment variable. |
22 | 23 | ErrEnvironmentBindFailure = errors.New("cmder: failed to update flag from environment variable") |
23 | 24 | ) |
24 | 25 |
|
|
77 | 78 | // Whenever the user provides the '-h' or '--help' flag at the command line, [Execute] will display command usage and |
78 | 79 | // exit. The format of the help text can be adjusted by configuring [UsageTemplate]. By default, usage information will |
79 | 80 | // be written to stderr, but this can be adjusted by setting [UsageOutputWriter]. |
| 81 | +// |
| 82 | +// If a command's [Run] routine returns [ErrShowUsage] (or an error wrapping [ErrShowUsage]), [Execute] will render |
| 83 | +// help text and exit with status 2. |
80 | 84 | func Execute(ctx context.Context, cmd Command, op ...ExecuteOption) error { |
81 | 85 | // do some checks |
82 | 86 | if cmd == nil { |
@@ -154,25 +158,45 @@ type command struct { |
154 | 158 |
|
155 | 159 | // onInit calls the [RunnableLifecycle] init routine if present on c. |
156 | 160 | func (c command) onInit(ctx context.Context) error { |
| 161 | + var err error |
| 162 | + |
157 | 163 | if cmd, ok := c.Command.(RunnableLifecycle); ok { |
158 | | - return cmd.Initialize(ctx, c.args) |
| 164 | + err = cmd.Initialize(ctx, c.args) |
159 | 165 | } |
160 | 166 |
|
161 | | - return nil |
| 167 | + if errors.Is(err, ErrShowUsage) { |
| 168 | + _ = usage(c) |
| 169 | + os.Exit(2) |
| 170 | + } |
| 171 | + |
| 172 | + return err |
162 | 173 | } |
163 | 174 |
|
164 | 175 | // run calls the [Runnable] run routine of c. |
165 | 176 | func (c command) run(ctx context.Context) error { |
166 | | - return c.Run(ctx, c.args) |
| 177 | + err := c.Run(ctx, c.args) |
| 178 | + if errors.Is(err, ErrShowUsage) { |
| 179 | + _ = usage(c) |
| 180 | + os.Exit(2) |
| 181 | + } |
| 182 | + |
| 183 | + return err |
167 | 184 | } |
168 | 185 |
|
169 | 186 | // onDestroy calls the [RunnableLifecycle] destroy routine if present on c. |
170 | 187 | func (c command) onDestroy(ctx context.Context) error { |
| 188 | + var err error |
| 189 | + |
171 | 190 | if cmd, ok := c.Command.(RunnableLifecycle); ok { |
172 | | - return cmd.Destroy(ctx, c.args) |
| 191 | + err = cmd.Destroy(ctx, c.args) |
173 | 192 | } |
174 | 193 |
|
175 | | - return nil |
| 194 | + if errors.Is(err, ErrShowUsage) { |
| 195 | + _ = usage(c) |
| 196 | + os.Exit(2) |
| 197 | + } |
| 198 | + |
| 199 | + return err |
176 | 200 | } |
177 | 201 |
|
178 | 202 | // buildCallStack builds a slice representing the command call stack. The first element in the slice is the root |
@@ -287,7 +311,11 @@ func bindEnvironmentFlags(stack []command, cmd command, ops *ExecuteOptions) err |
287 | 311 |
|
288 | 312 | if value, ok := os.LookupEnv(variable); ok { |
289 | 313 | if err := flag.Value.Set(value); err != nil { |
290 | | - return errors.Join(ErrEnvironmentBindFailure, err) |
| 314 | + return errors.Join( |
| 315 | + ErrEnvironmentBindFailure, |
| 316 | + fmt.Errorf("cmder: failed to set flag %s from variable %s", flag.Name, variable), |
| 317 | + err, |
| 318 | + ) |
291 | 319 | } |
292 | 320 | } |
293 | 321 | } |
|
0 commit comments