Coverage Report

Created: 2025-12-31 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/argh-0.1.10/src/lib.rs
Line
Count
Source
1
// Copyright (c) 2020 Google LLC All rights reserved.
2
// Use of this source code is governed by a BSD-style
3
// license that can be found in the LICENSE file.
4
5
//! Derive-based argument parsing optimized for code size and conformance
6
//! to the Fuchsia commandline tools specification
7
//!
8
//! The public API of this library consists primarily of the `FromArgs`
9
//! derive and the `from_env` function, which can be used to produce
10
//! a top-level `FromArgs` type from the current program's commandline
11
//! arguments.
12
//!
13
//! ## Basic Example
14
//!
15
//! ```rust,no_run
16
//! use argh::FromArgs;
17
//!
18
//! #[derive(FromArgs)]
19
//! /// Reach new heights.
20
//! struct GoUp {
21
//!     /// whether or not to jump
22
//!     #[argh(switch, short = 'j')]
23
//!     jump: bool,
24
//!
25
//!     /// how high to go
26
//!     #[argh(option)]
27
//!     height: usize,
28
//!
29
//!     /// an optional nickname for the pilot
30
//!     #[argh(option)]
31
//!     pilot_nickname: Option<String>,
32
//! }
33
//!
34
//! let up: GoUp = argh::from_env();
35
//! ```
36
//!
37
//! `./some_bin --help` will then output the following:
38
//!
39
//! ```bash
40
//! Usage: cmdname [-j] --height <height> [--pilot-nickname <pilot-nickname>]
41
//!
42
//! Reach new heights.
43
//!
44
//! Options:
45
//!   -j, --jump        whether or not to jump
46
//!   --height          how high to go
47
//!   --pilot-nickname  an optional nickname for the pilot
48
//!   --help            display usage information
49
//! ```
50
//!
51
//! The resulting program can then be used in any of these ways:
52
//! - `./some_bin --height 5`
53
//! - `./some_bin -j --height 5`
54
//! - `./some_bin --jump --height 5 --pilot-nickname Wes`
55
//!
56
//! Switches, like `jump`, are optional and will be set to true if provided.
57
//!
58
//! Options, like `height` and `pilot_nickname`, can be either required,
59
//! optional, or repeating, depending on whether they are contained in an
60
//! `Option` or a `Vec`. Default values can be provided using the
61
//! `#[argh(default = "<your_code_here>")]` attribute, and in this case an
62
//! option is treated as optional.
63
//!
64
//! ```rust
65
//! use argh::FromArgs;
66
//!
67
//! fn default_height() -> usize {
68
//!     5
69
//! }
70
//!
71
//! #[derive(FromArgs)]
72
//! /// Reach new heights.
73
//! struct GoUp {
74
//!     /// an optional nickname for the pilot
75
//!     #[argh(option)]
76
//!     pilot_nickname: Option<String>,
77
//!
78
//!     /// an optional height
79
//!     #[argh(option, default = "default_height()")]
80
//!     height: usize,
81
//!
82
//!     /// an optional direction which is "up" by default
83
//!     #[argh(option, default = "String::from(\"only up\")")]
84
//!     direction: String,
85
//! }
86
//!
87
//! fn main() {
88
//!     let up: GoUp = argh::from_env();
89
//! }
90
//! ```
91
//!
92
//! Custom option types can be deserialized so long as they implement the
93
//! `FromArgValue` trait (automatically implemented for all `FromStr` types).
94
//! If more customized parsing is required, you can supply a custom
95
//! `fn(&str) -> Result<T, String>` using the `from_str_fn` attribute:
96
//!
97
//! ```
98
//! # use argh::FromArgs;
99
//!
100
//! #[derive(FromArgs)]
101
//! /// Goofy thing.
102
//! struct FiveStruct {
103
//!     /// always five
104
//!     #[argh(option, from_str_fn(always_five))]
105
//!     five: usize,
106
//! }
107
//!
108
//! fn always_five(_value: &str) -> Result<usize, String> {
109
//!     Ok(5)
110
//! }
111
//! ```
112
//!
113
//! Positional arguments can be declared using `#[argh(positional)]`.
114
//! These arguments will be parsed in order of their declaration in
115
//! the structure:
116
//!
117
//! ```rust
118
//! use argh::FromArgs;
119
//! #[derive(FromArgs, PartialEq, Debug)]
120
//! /// A command with positional arguments.
121
//! struct WithPositional {
122
//!     #[argh(positional)]
123
//!     first: String,
124
//! }
125
//! ```
126
//!
127
//! The last positional argument may include a default, or be wrapped in
128
//! `Option` or `Vec` to indicate an optional or repeating positional argument.
129
//!
130
//! If your final positional argument has the `greedy` option on it, it will consume
131
//! any arguments after it as if a `--` were placed before the first argument to
132
//! match the greedy positional:
133
//!
134
//! ```rust
135
//! use argh::FromArgs;
136
//! #[derive(FromArgs, PartialEq, Debug)]
137
//! /// A command with a greedy positional argument at the end.
138
//! struct WithGreedyPositional {
139
//!     /// some stuff
140
//!     #[argh(option)]
141
//!     stuff: Option<String>,
142
//!     #[argh(positional, greedy)]
143
//!     all_the_rest: Vec<String>,
144
//! }
145
//! ```
146
//!
147
//! Now if you pass `--stuff Something` after a positional argument, it will
148
//! be consumed by `all_the_rest` instead of setting the `stuff` field.
149
//!
150
//! Note that `all_the_rest` won't be listed as a positional argument in the
151
//! long text part of help output (and it will be listed at the end of the usage
152
//! line as `[all_the_rest...]`), and it's up to the caller to append any
153
//! extra help output for the meaning of the captured arguments. This is to
154
//! enable situations where some amount of argument processing needs to happen
155
//! before the rest of the arguments can be interpreted, and shouldn't be used
156
//! for regular use as it might be confusing.
157
//!
158
//! Subcommands are also supported. To use a subcommand, declare a separate
159
//! `FromArgs` type for each subcommand as well as an enum that cases
160
//! over each command:
161
//!
162
//! ```rust
163
//! # use argh::FromArgs;
164
//!
165
//! #[derive(FromArgs, PartialEq, Debug)]
166
//! /// Top-level command.
167
//! struct TopLevel {
168
//!     #[argh(subcommand)]
169
//!     nested: MySubCommandEnum,
170
//! }
171
//!
172
//! #[derive(FromArgs, PartialEq, Debug)]
173
//! #[argh(subcommand)]
174
//! enum MySubCommandEnum {
175
//!     One(SubCommandOne),
176
//!     Two(SubCommandTwo),
177
//! }
178
//!
179
//! #[derive(FromArgs, PartialEq, Debug)]
180
//! /// First subcommand.
181
//! #[argh(subcommand, name = "one")]
182
//! struct SubCommandOne {
183
//!     #[argh(option)]
184
//!     /// how many x
185
//!     x: usize,
186
//! }
187
//!
188
//! #[derive(FromArgs, PartialEq, Debug)]
189
//! /// Second subcommand.
190
//! #[argh(subcommand, name = "two")]
191
//! struct SubCommandTwo {
192
//!     #[argh(switch)]
193
//!     /// whether to fooey
194
//!     fooey: bool,
195
//! }
196
//! ```
197
//!
198
//! You can also discover subcommands dynamically at runtime. To do this,
199
//! declare subcommands as usual and add a variant to the enum with the
200
//! `dynamic` attribute. Instead of deriving `FromArgs`, the value inside the
201
//! dynamic variant should implement `DynamicSubCommand`.
202
//!
203
//! ```rust
204
//! # use argh::CommandInfo;
205
//! # use argh::DynamicSubCommand;
206
//! # use argh::EarlyExit;
207
//! # use argh::FromArgs;
208
//! # use once_cell::sync::OnceCell;
209
//!
210
//! #[derive(FromArgs, PartialEq, Debug)]
211
//! /// Top-level command.
212
//! struct TopLevel {
213
//!     #[argh(subcommand)]
214
//!     nested: MySubCommandEnum,
215
//! }
216
//!
217
//! #[derive(FromArgs, PartialEq, Debug)]
218
//! #[argh(subcommand)]
219
//! enum MySubCommandEnum {
220
//!     Normal(NormalSubCommand),
221
//!     #[argh(dynamic)]
222
//!     Dynamic(Dynamic),
223
//! }
224
//!
225
//! #[derive(FromArgs, PartialEq, Debug)]
226
//! /// Normal subcommand.
227
//! #[argh(subcommand, name = "normal")]
228
//! struct NormalSubCommand {
229
//!     #[argh(option)]
230
//!     /// how many x
231
//!     x: usize,
232
//! }
233
//!
234
//! /// Dynamic subcommand.
235
//! #[derive(PartialEq, Debug)]
236
//! struct Dynamic {
237
//!     name: String
238
//! }
239
//!
240
//! impl DynamicSubCommand for Dynamic {
241
//!     fn commands() -> &'static [&'static CommandInfo] {
242
//!         static RET: OnceCell<Vec<&'static CommandInfo>> = OnceCell::new();
243
//!         RET.get_or_init(|| {
244
//!             let mut commands = Vec::new();
245
//!
246
//!             // argh needs the `CommandInfo` structs we generate to be valid
247
//!             // for the static lifetime. We can allocate the structures on
248
//!             // the heap with `Box::new` and use `Box::leak` to get a static
249
//!             // reference to them. We could also just use a constant
250
//!             // reference, but only because this is a synthetic example; the
251
//!             // point of using dynamic commands is to have commands you
252
//!             // don't know about until runtime!
253
//!             commands.push(&*Box::leak(Box::new(CommandInfo {
254
//!                 name: "dynamic_command",
255
//!                 description: "A dynamic command",
256
//!             })));
257
//!
258
//!             commands
259
//!         })
260
//!     }
261
//!
262
//!     fn try_redact_arg_values(
263
//!         command_name: &[&str],
264
//!         args: &[&str],
265
//!     ) -> Option<Result<Vec<String>, EarlyExit>> {
266
//!         for command in Self::commands() {
267
//!             if command_name.last() == Some(&command.name) {
268
//!                 // Process arguments and redact values here.
269
//!                 if !args.is_empty() {
270
//!                     return Some(Err("Our example dynamic command never takes arguments!"
271
//!                                     .to_string().into()));
272
//!                 }
273
//!                 return Some(Ok(Vec::new()))
274
//!             }
275
//!         }
276
//!         None
277
//!     }
278
//!
279
//!     fn try_from_args(command_name: &[&str], args: &[&str]) -> Option<Result<Self, EarlyExit>> {
280
//!         for command in Self::commands() {
281
//!             if command_name.last() == Some(&command.name) {
282
//!                 if !args.is_empty() {
283
//!                     return Some(Err("Our example dynamic command never takes arguments!"
284
//!                                     .to_string().into()));
285
//!                 }
286
//!                 return Some(Ok(Dynamic { name: command.name.to_string() }))
287
//!             }
288
//!         }
289
//!         None
290
//!     }
291
//! }
292
//! ```
293
//!
294
//! Programs that are run from an environment such as cargo may find it
295
//! useful to have positional arguments present in the structure but
296
//! omitted from the usage output. This can be accomplished by adding
297
//! the `hidden_help` attribute to that argument:
298
//!
299
//! ```rust
300
//! # use argh::FromArgs;
301
//!
302
//! #[derive(FromArgs)]
303
//! /// Cargo arguments
304
//! struct CargoArgs {
305
//!     // Cargo puts the command name invoked into the first argument,
306
//!     // so we don't want this argument to show up in the usage text.
307
//!     #[argh(positional, hidden_help)]
308
//!     command: String,
309
//!     /// an option used for internal debugging
310
//!     #[argh(option, hidden_help)]
311
//!     internal_debugging: String,
312
//!     #[argh(positional)]
313
//!     real_first_arg: String,
314
//! }
315
//! ```
316
317
#![deny(missing_docs)]
318
319
use std::str::FromStr;
320
321
pub use argh_derive::FromArgs;
322
323
/// Information about a particular command used for output.
324
pub type CommandInfo = argh_shared::CommandInfo<'static>;
325
326
/// Types which can be constructed from a set of commandline arguments.
327
pub trait FromArgs: Sized {
328
    /// Construct the type from an input set of arguments.
329
    ///
330
    /// The first argument `command_name` is the identifier for the current command. In most cases,
331
    /// users should only pass in a single item for the command name, which typically comes from
332
    /// the first item from `std::env::args()`. Implementations however should append the
333
    /// subcommand name in when recursively calling [FromArgs::from_args] for subcommands. This
334
    /// allows `argh` to generate correct subcommand help strings.
335
    ///
336
    /// The second argument `args` is the rest of the command line arguments.
337
    ///
338
    /// # Examples
339
    ///
340
    /// ```rust
341
    /// # use argh::FromArgs;
342
    ///
343
    /// /// Command to manage a classroom.
344
    /// #[derive(Debug, PartialEq, FromArgs)]
345
    /// struct ClassroomCmd {
346
    ///     #[argh(subcommand)]
347
    ///     subcommands: Subcommands,
348
    /// }
349
    ///
350
    /// #[derive(Debug, PartialEq, FromArgs)]
351
    /// #[argh(subcommand)]
352
    /// enum Subcommands {
353
    ///     List(ListCmd),
354
    ///     Add(AddCmd),
355
    /// }
356
    ///
357
    /// /// list all the classes.
358
    /// #[derive(Debug, PartialEq, FromArgs)]
359
    /// #[argh(subcommand, name = "list")]
360
    /// struct ListCmd {
361
    ///     /// list classes for only this teacher.
362
    ///     #[argh(option)]
363
    ///     teacher_name: Option<String>,
364
    /// }
365
    ///
366
    /// /// add students to a class.
367
    /// #[derive(Debug, PartialEq, FromArgs)]
368
    /// #[argh(subcommand, name = "add")]
369
    /// struct AddCmd {
370
    ///     /// the name of the class's teacher.
371
    ///     #[argh(option)]
372
    ///     teacher_name: String,
373
    ///
374
    ///     /// the name of the class.
375
    ///     #[argh(positional)]
376
    ///     class_name: String,
377
    /// }
378
    ///
379
    /// let args = ClassroomCmd::from_args(
380
    ///     &["classroom"],
381
    ///     &["list", "--teacher-name", "Smith"],
382
    /// ).unwrap();
383
    /// assert_eq!(
384
    ///    args,
385
    ///     ClassroomCmd {
386
    ///         subcommands: Subcommands::List(ListCmd {
387
    ///             teacher_name: Some("Smith".to_string()),
388
    ///         })
389
    ///     },
390
    /// );
391
    ///
392
    /// // Help returns an error, but internally returns an `Ok` status.
393
    /// let early_exit = ClassroomCmd::from_args(
394
    ///     &["classroom"],
395
    ///     &["help"],
396
    /// ).unwrap_err();
397
    /// assert_eq!(
398
    ///     early_exit,
399
    ///     argh::EarlyExit {
400
    ///        output: r#"Usage: classroom <command> [<args>]
401
    ///
402
    /// Command to manage a classroom.
403
    ///
404
    /// Options:
405
    ///   --help            display usage information
406
    ///
407
    /// Commands:
408
    ///   list              list all the classes.
409
    ///   add               add students to a class.
410
    /// "#.to_string(),
411
    ///        status: Ok(()),
412
    ///     },
413
    /// );
414
    ///
415
    /// // Help works with subcommands.
416
    /// let early_exit = ClassroomCmd::from_args(
417
    ///     &["classroom"],
418
    ///     &["list", "help"],
419
    /// ).unwrap_err();
420
    /// assert_eq!(
421
    ///     early_exit,
422
    ///     argh::EarlyExit {
423
    ///        output: r#"Usage: classroom list [--teacher-name <teacher-name>]
424
    ///
425
    /// list all the classes.
426
    ///
427
    /// Options:
428
    ///   --teacher-name    list classes for only this teacher.
429
    ///   --help            display usage information
430
    /// "#.to_string(),
431
    ///        status: Ok(()),
432
    ///     },
433
    /// );
434
    ///
435
    /// // Incorrect arguments will error out.
436
    /// let err = ClassroomCmd::from_args(
437
    ///     &["classroom"],
438
    ///     &["lisp"],
439
    /// ).unwrap_err();
440
    /// assert_eq!(
441
    ///    err,
442
    ///    argh::EarlyExit {
443
    ///        output: "Unrecognized argument: lisp\n".to_string(),
444
    ///        status: Err(()),
445
    ///     },
446
    /// );
447
    /// ```
448
    fn from_args(command_name: &[&str], args: &[&str]) -> Result<Self, EarlyExit>;
449
450
    /// Get a String with just the argument names, e.g., options, flags, subcommands, etc, but
451
    /// without the values of the options and arguments. This can be useful as a means to capture
452
    /// anonymous usage statistics without revealing the content entered by the end user.
453
    ///
454
    /// The first argument `command_name` is the identifier for the current command. In most cases,
455
    /// users should only pass in a single item for the command name, which typically comes from
456
    /// the first item from `std::env::args()`. Implementations however should append the
457
    /// subcommand name in when recursively calling [FromArgs::from_args] for subcommands. This
458
    /// allows `argh` to generate correct subcommand help strings.
459
    ///
460
    /// The second argument `args` is the rest of the command line arguments.
461
    ///
462
    /// # Examples
463
    ///
464
    /// ```rust
465
    /// # use argh::FromArgs;
466
    ///
467
    /// /// Command to manage a classroom.
468
    /// #[derive(FromArgs)]
469
    /// struct ClassroomCmd {
470
    ///     #[argh(subcommand)]
471
    ///     subcommands: Subcommands,
472
    /// }
473
    ///
474
    /// #[derive(FromArgs)]
475
    /// #[argh(subcommand)]
476
    /// enum Subcommands {
477
    ///     List(ListCmd),
478
    ///     Add(AddCmd),
479
    /// }
480
    ///
481
    /// /// list all the classes.
482
    /// #[derive(FromArgs)]
483
    /// #[argh(subcommand, name = "list")]
484
    /// struct ListCmd {
485
    ///     /// list classes for only this teacher.
486
    ///     #[argh(option)]
487
    ///     teacher_name: Option<String>,
488
    /// }
489
    ///
490
    /// /// add students to a class.
491
    /// #[derive(FromArgs)]
492
    /// #[argh(subcommand, name = "add")]
493
    /// struct AddCmd {
494
    ///     /// the name of the class's teacher.
495
    ///     #[argh(option)]
496
    ///     teacher_name: String,
497
    ///
498
    ///     /// has the class started yet?
499
    ///     #[argh(switch)]
500
    ///     started: bool,
501
    ///
502
    ///     /// the name of the class.
503
    ///     #[argh(positional)]
504
    ///     class_name: String,
505
    ///
506
    ///     /// the student names.
507
    ///     #[argh(positional)]
508
    ///     students: Vec<String>,
509
    /// }
510
    ///
511
    /// let args = ClassroomCmd::redact_arg_values(
512
    ///     &["classroom"],
513
    ///     &["list"],
514
    /// ).unwrap();
515
    /// assert_eq!(
516
    ///     args,
517
    ///     &[
518
    ///         "classroom",
519
    ///         "list",
520
    ///     ],
521
    /// );
522
    ///
523
    /// let args = ClassroomCmd::redact_arg_values(
524
    ///     &["classroom"],
525
    ///     &["list", "--teacher-name", "Smith"],
526
    /// ).unwrap();
527
    /// assert_eq!(
528
    ///    args,
529
    ///    &[
530
    ///         "classroom",
531
    ///         "list",
532
    ///         "--teacher-name",
533
    ///     ],
534
    /// );
535
    ///
536
    /// let args = ClassroomCmd::redact_arg_values(
537
    ///     &["classroom"],
538
    ///     &["add", "--teacher-name", "Smith", "--started", "Math", "Abe", "Sung"],
539
    /// ).unwrap();
540
    /// assert_eq!(
541
    ///     args,
542
    ///     &[
543
    ///         "classroom",
544
    ///         "add",
545
    ///         "--teacher-name",
546
    ///         "--started",
547
    ///         "class_name",
548
    ///         "students",
549
    ///         "students",
550
    ///     ],
551
    /// );
552
    ///
553
    /// // `ClassroomCmd::redact_arg_values` will error out if passed invalid arguments.
554
    /// assert_eq!(
555
    ///     ClassroomCmd::redact_arg_values(&["classroom"], &["add", "--teacher-name"]),
556
    ///     Err(argh::EarlyExit {
557
    ///         output: "No value provided for option '--teacher-name'.\n".into(),
558
    ///         status: Err(()),
559
    ///     }),
560
    /// );
561
    ///
562
    /// // `ClassroomCmd::redact_arg_values` will generate help messages.
563
    /// assert_eq!(
564
    ///     ClassroomCmd::redact_arg_values(&["classroom"], &["help"]),
565
    ///     Err(argh::EarlyExit {
566
    ///         output: r#"Usage: classroom <command> [<args>]
567
    ///
568
    /// Command to manage a classroom.
569
    ///
570
    /// Options:
571
    ///   --help            display usage information
572
    ///
573
    /// Commands:
574
    ///   list              list all the classes.
575
    ///   add               add students to a class.
576
    /// "#.to_string(),
577
    ///         status: Ok(()),
578
    ///     }),
579
    /// );
580
    /// ```
581
0
    fn redact_arg_values(_command_name: &[&str], _args: &[&str]) -> Result<Vec<String>, EarlyExit> {
582
0
        Ok(vec!["<<REDACTED>>".into()])
583
0
    }
584
}
585
586
/// A top-level `FromArgs` implementation that is not a subcommand.
587
pub trait TopLevelCommand: FromArgs {}
588
589
/// A `FromArgs` implementation that can parse into one or more subcommands.
590
pub trait SubCommands: FromArgs {
591
    /// Info for the commands.
592
    const COMMANDS: &'static [&'static CommandInfo];
593
594
    /// Get a list of commands that are discovered at runtime.
595
0
    fn dynamic_commands() -> &'static [&'static CommandInfo] {
596
0
        &[]
597
0
    }
598
}
599
600
/// A `FromArgs` implementation that represents a single subcommand.
601
pub trait SubCommand: FromArgs {
602
    /// Information about the subcommand.
603
    const COMMAND: &'static CommandInfo;
604
}
605
606
impl<T: SubCommand> SubCommands for T {
607
    const COMMANDS: &'static [&'static CommandInfo] = &[T::COMMAND];
608
}
609
610
/// Trait implemented by values returned from a dynamic subcommand handler.
611
pub trait DynamicSubCommand: Sized {
612
    /// Info about supported subcommands.
613
    fn commands() -> &'static [&'static CommandInfo];
614
615
    /// Perform the function of `FromArgs::redact_arg_values` for this dynamic
616
    /// command.
617
    ///
618
    /// The full list of subcommands, ending with the subcommand that should be
619
    /// dynamically recognized, is passed in `command_name`. If the command
620
    /// passed is not recognized, this function should return `None`. Otherwise
621
    /// it should return `Some`, and the value within the `Some` has the same
622
    /// semantics as the return of `FromArgs::redact_arg_values`.
623
    fn try_redact_arg_values(
624
        command_name: &[&str],
625
        args: &[&str],
626
    ) -> Option<Result<Vec<String>, EarlyExit>>;
627
628
    /// Perform the function of `FromArgs::from_args` for this dynamic command.
629
    ///
630
    /// The full list of subcommands, ending with the subcommand that should be
631
    /// dynamically recognized, is passed in `command_name`. If the command
632
    /// passed is not recognized, this function should return `None`. Otherwise
633
    /// it should return `Some`, and the value within the `Some` has the same
634
    /// semantics as the return of `FromArgs::from_args`.
635
    fn try_from_args(command_name: &[&str], args: &[&str]) -> Option<Result<Self, EarlyExit>>;
636
}
637
638
/// Information to display to the user about why a `FromArgs` construction exited early.
639
///
640
/// This can occur due to either failed parsing or a flag like `--help`.
641
#[derive(Debug, Clone, PartialEq, Eq)]
642
pub struct EarlyExit {
643
    /// The output to display to the user of the commandline tool.
644
    pub output: String,
645
    /// Status of argument parsing.
646
    ///
647
    /// `Ok` if the command was parsed successfully and the early exit is due
648
    /// to a flag like `--help` causing early exit with output.
649
    ///
650
    /// `Err` if the arguments were not successfully parsed.
651
    // TODO replace with std::process::ExitCode when stable.
652
    pub status: Result<(), ()>,
653
}
654
655
impl From<String> for EarlyExit {
656
0
    fn from(err_msg: String) -> Self {
657
0
        Self { output: err_msg, status: Err(()) }
658
0
    }
659
}
660
661
/// Extract the base cmd from a path
662
0
fn cmd<'a>(default: &'a str, path: &'a str) -> &'a str {
663
0
    std::path::Path::new(path).file_name().and_then(|s| s.to_str()).unwrap_or(default)
664
0
}
665
666
/// Create a `FromArgs` type from the current process's `env::args`.
667
///
668
/// This function will exit early from the current process if argument parsing
669
/// was unsuccessful or if information like `--help` was requested. Error messages will be printed
670
/// to stderr, and `--help` output to stdout.
671
0
pub fn from_env<T: TopLevelCommand>() -> T {
672
0
    let strings: Vec<String> = std::env::args_os()
673
0
        .map(|s| s.into_string())
674
0
        .collect::<Result<Vec<_>, _>>()
675
0
        .unwrap_or_else(|arg| {
676
0
            eprintln!("Invalid utf8: {}", arg.to_string_lossy());
677
0
            std::process::exit(1)
678
        });
679
680
0
    if strings.is_empty() {
681
0
        eprintln!("No program name, argv is empty");
682
0
        std::process::exit(1)
683
0
    }
684
685
0
    let cmd = cmd(&strings[0], &strings[0]);
686
0
    let strs: Vec<&str> = strings.iter().map(|s| s.as_str()).collect();
687
0
    T::from_args(&[cmd], &strs[1..]).unwrap_or_else(|early_exit| {
688
0
        std::process::exit(match early_exit.status {
689
            Ok(()) => {
690
0
                println!("{}", early_exit.output);
691
0
                0
692
            }
693
            Err(()) => {
694
0
                eprintln!("{}\nRun {} --help for more information.", early_exit.output, cmd);
695
0
                1
696
            }
697
        })
698
    })
699
0
}
700
701
/// Create a `FromArgs` type from the current process's `env::args`.
702
///
703
/// This special cases usages where argh is being used in an environment where cargo is
704
/// driving the build. We skip the second env variable.
705
///
706
/// This function will exit early from the current process if argument parsing
707
/// was unsuccessful or if information like `--help` was requested. Error messages will be printed
708
/// to stderr, and `--help` output to stdout.
709
0
pub fn cargo_from_env<T: TopLevelCommand>() -> T {
710
0
    let strings: Vec<String> = std::env::args().collect();
711
0
    let cmd = cmd(&strings[1], &strings[1]);
712
0
    let strs: Vec<&str> = strings.iter().map(|s| s.as_str()).collect();
713
0
    T::from_args(&[cmd], &strs[2..]).unwrap_or_else(|early_exit| {
714
0
        std::process::exit(match early_exit.status {
715
            Ok(()) => {
716
0
                println!("{}", early_exit.output);
717
0
                0
718
            }
719
            Err(()) => {
720
0
                eprintln!("{}\nRun --help for more information.", early_exit.output);
721
0
                1
722
            }
723
        })
724
    })
725
0
}
726
727
/// Types which can be constructed from a single commandline value.
728
///
729
/// Any field type declared in a struct that derives `FromArgs` must implement
730
/// this trait. A blanket implementation exists for types implementing
731
/// `FromStr<Error: Display>`. Custom types can implement this trait
732
/// directly.
733
pub trait FromArgValue: Sized {
734
    /// Construct the type from a commandline value, returning an error string
735
    /// on failure.
736
    fn from_arg_value(value: &str) -> Result<Self, String>;
737
}
738
739
impl<T> FromArgValue for T
740
where
741
    T: FromStr,
742
    T::Err: std::fmt::Display,
743
{
744
0
    fn from_arg_value(value: &str) -> Result<Self, String> {
745
0
        T::from_str(value).map_err(|x| x.to_string())
Unexecuted instantiation: <alloc::string::String as argh::FromArgValue>::from_arg_value::{closure#0}
Unexecuted instantiation: <std::path::PathBuf as argh::FromArgValue>::from_arg_value::{closure#0}
Unexecuted instantiation: <bool as argh::FromArgValue>::from_arg_value::{closure#0}
Unexecuted instantiation: <i32 as argh::FromArgValue>::from_arg_value::{closure#0}
Unexecuted instantiation: <u32 as argh::FromArgValue>::from_arg_value::{closure#0}
Unexecuted instantiation: <u64 as argh::FromArgValue>::from_arg_value::{closure#0}
Unexecuted instantiation: <_ as argh::FromArgValue>::from_arg_value::{closure#0}
746
0
    }
Unexecuted instantiation: <std::path::PathBuf as argh::FromArgValue>::from_arg_value
Unexecuted instantiation: <alloc::string::String as argh::FromArgValue>::from_arg_value
Unexecuted instantiation: <bool as argh::FromArgValue>::from_arg_value
Unexecuted instantiation: <i32 as argh::FromArgValue>::from_arg_value
Unexecuted instantiation: <u32 as argh::FromArgValue>::from_arg_value
Unexecuted instantiation: <u64 as argh::FromArgValue>::from_arg_value
Unexecuted instantiation: <_ as argh::FromArgValue>::from_arg_value
747
}
748
749
// The following items are all used by the generated code, and should not be considered part
750
// of this library's public API surface.
751
752
#[doc(hidden)]
753
pub trait ParseFlag {
754
    fn set_flag(&mut self, arg: &str);
755
}
756
757
impl<T: Flag> ParseFlag for T {
758
0
    fn set_flag(&mut self, _arg: &str) {
759
0
        <T as Flag>::set_flag(self);
760
0
    }
Unexecuted instantiation: <bool as argh::ParseFlag>::set_flag
Unexecuted instantiation: <_ as argh::ParseFlag>::set_flag
761
}
762
763
#[doc(hidden)]
764
pub struct RedactFlag {
765
    pub slot: Option<String>,
766
}
767
768
impl ParseFlag for RedactFlag {
769
0
    fn set_flag(&mut self, arg: &str) {
770
0
        self.slot = Some(arg.to_string());
771
0
    }
772
}
773
774
// A trait for for slots that reserve space for a value and know how to parse that value
775
// from a command-line `&str` argument.
776
//
777
// This trait is only implemented for the type `ParseValueSlotTy`. This indirection is
778
// necessary to allow abstracting over `ParseValueSlotTy` instances with different
779
// generic parameters.
780
#[doc(hidden)]
781
pub trait ParseValueSlot {
782
    fn fill_slot(&mut self, arg: &str, value: &str) -> Result<(), String>;
783
}
784
785
// The concrete type implementing the `ParseValueSlot` trait.
786
//
787
// `T` is the type to be parsed from a single string.
788
// `Slot` is the type of the container that can hold a value or values of type `T`.
789
#[doc(hidden)]
790
pub struct ParseValueSlotTy<Slot, T> {
791
    // The slot for a parsed value.
792
    pub slot: Slot,
793
    // The function to parse the value from a string
794
    pub parse_func: fn(&str, &str) -> Result<T, String>,
795
}
796
797
// `ParseValueSlotTy<Option<T>, T>` is used as the slot for all non-repeating
798
// arguments, both optional and required.
799
impl<T> ParseValueSlot for ParseValueSlotTy<Option<T>, T> {
800
0
    fn fill_slot(&mut self, arg: &str, value: &str) -> Result<(), String> {
801
0
        if self.slot.is_some() {
802
0
            return Err("duplicate values provided".to_string());
803
0
        }
804
0
        self.slot = Some((self.parse_func)(arg, value)?);
805
0
        Ok(())
806
0
    }
Unexecuted instantiation: <argh::ParseValueSlotTy<core::option::Option<alloc::string::String>, alloc::string::String> as argh::ParseValueSlot>::fill_slot
Unexecuted instantiation: <argh::ParseValueSlotTy<core::option::Option<std::path::PathBuf>, std::path::PathBuf> as argh::ParseValueSlot>::fill_slot
Unexecuted instantiation: <argh::ParseValueSlotTy<core::option::Option<devices::virtio::fs::config::Config>, devices::virtio::fs::config::Config> as argh::ParseValueSlot>::fill_slot
Unexecuted instantiation: <argh::ParseValueSlotTy<core::option::Option<bool>, bool> as argh::ParseValueSlot>::fill_slot
Unexecuted instantiation: <argh::ParseValueSlotTy<core::option::Option<i32>, i32> as argh::ParseValueSlot>::fill_slot
Unexecuted instantiation: <argh::ParseValueSlotTy<core::option::Option<u32>, u32> as argh::ParseValueSlot>::fill_slot
Unexecuted instantiation: <argh::ParseValueSlotTy<core::option::Option<u64>, u64> as argh::ParseValueSlot>::fill_slot
Unexecuted instantiation: <argh::ParseValueSlotTy<core::option::Option<_>, _> as argh::ParseValueSlot>::fill_slot
807
}
808
809
// `ParseValueSlotTy<Vec<T>, T>` is used as the slot for repeating arguments.
810
impl<T> ParseValueSlot for ParseValueSlotTy<Vec<T>, T> {
811
0
    fn fill_slot(&mut self, arg: &str, value: &str) -> Result<(), String> {
812
0
        self.slot.push((self.parse_func)(arg, value)?);
813
0
        Ok(())
814
0
    }
Unexecuted instantiation: <argh::ParseValueSlotTy<alloc::vec::Vec<alloc::string::String>, alloc::string::String> as argh::ParseValueSlot>::fill_slot
Unexecuted instantiation: <argh::ParseValueSlotTy<alloc::vec::Vec<devices::serial_device::SerialParameters>, devices::serial_device::SerialParameters> as argh::ParseValueSlot>::fill_slot
Unexecuted instantiation: <argh::ParseValueSlotTy<alloc::vec::Vec<(alloc::string::String, std::path::PathBuf)>, (alloc::string::String, std::path::PathBuf)> as argh::ParseValueSlot>::fill_slot
Unexecuted instantiation: <argh::ParseValueSlotTy<alloc::vec::Vec<_>, _> as argh::ParseValueSlot>::fill_slot
815
}
816
817
/// A type which can be the receiver of a `Flag`.
818
pub trait Flag {
819
    /// Creates a default instance of the flag value;
820
    fn default() -> Self
821
    where
822
        Self: Sized;
823
824
    /// Sets the flag. This function is called when the flag is provided.
825
    fn set_flag(&mut self);
826
}
827
828
impl Flag for bool {
829
0
    fn default() -> Self {
830
0
        false
831
0
    }
832
0
    fn set_flag(&mut self) {
833
0
        *self = true;
834
0
    }
835
}
836
837
impl Flag for Option<bool> {
838
0
    fn default() -> Self {
839
0
        None
840
0
    }
841
842
0
    fn set_flag(&mut self) {
843
0
        *self = Some(true);
844
0
    }
845
}
846
847
macro_rules! impl_flag_for_integers {
848
    ($($ty:ty,)*) => {
849
        $(
850
            impl Flag for $ty {
851
0
                fn default() -> Self {
852
0
                    0
853
0
                }
Unexecuted instantiation: <u8 as argh::Flag>::default
Unexecuted instantiation: <u16 as argh::Flag>::default
Unexecuted instantiation: <u32 as argh::Flag>::default
Unexecuted instantiation: <u64 as argh::Flag>::default
Unexecuted instantiation: <u128 as argh::Flag>::default
Unexecuted instantiation: <i8 as argh::Flag>::default
Unexecuted instantiation: <i16 as argh::Flag>::default
Unexecuted instantiation: <i32 as argh::Flag>::default
Unexecuted instantiation: <i64 as argh::Flag>::default
Unexecuted instantiation: <i128 as argh::Flag>::default
854
0
                fn set_flag(&mut self) {
855
0
                    *self = self.saturating_add(1);
856
0
                }
Unexecuted instantiation: <u8 as argh::Flag>::set_flag
Unexecuted instantiation: <u16 as argh::Flag>::set_flag
Unexecuted instantiation: <u32 as argh::Flag>::set_flag
Unexecuted instantiation: <u64 as argh::Flag>::set_flag
Unexecuted instantiation: <u128 as argh::Flag>::set_flag
Unexecuted instantiation: <i8 as argh::Flag>::set_flag
Unexecuted instantiation: <i16 as argh::Flag>::set_flag
Unexecuted instantiation: <i32 as argh::Flag>::set_flag
Unexecuted instantiation: <i64 as argh::Flag>::set_flag
Unexecuted instantiation: <i128 as argh::Flag>::set_flag
857
            }
858
        )*
859
    }
860
}
861
862
impl_flag_for_integers![u8, u16, u32, u64, u128, i8, i16, i32, i64, i128,];
863
864
/// This function implements argument parsing for structs.
865
///
866
/// `cmd_name`: The identifier for the current command.
867
/// `args`: The command line arguments.
868
/// `parse_options`: Helper to parse optional arguments.
869
/// `parse_positionals`: Helper to parse positional arguments.
870
/// `parse_subcommand`: Helper to parse a subcommand.
871
/// `help_func`: Generate a help message.
872
#[doc(hidden)]
873
0
pub fn parse_struct_args(
874
0
    cmd_name: &[&str],
875
0
    args: &[&str],
876
0
    mut parse_options: ParseStructOptions<'_>,
877
0
    mut parse_positionals: ParseStructPositionals<'_>,
878
0
    mut parse_subcommand: Option<ParseStructSubCommand<'_>>,
879
0
    help_func: &dyn Fn() -> String,
880
0
) -> Result<(), EarlyExit> {
881
0
    let mut help = false;
882
0
    let mut remaining_args = args;
883
0
    let mut positional_index = 0;
884
0
    let mut options_ended = false;
885
886
0
    'parse_args: while let Some(&next_arg) = remaining_args.first() {
887
0
        remaining_args = &remaining_args[1..];
888
0
        if (next_arg == "--help" || next_arg == "help") && !options_ended {
889
0
            help = true;
890
0
            continue;
891
0
        }
892
893
0
        if next_arg.starts_with('-') && !options_ended {
894
0
            if next_arg == "--" {
895
0
                options_ended = true;
896
0
                continue;
897
0
            }
898
899
0
            if help {
900
0
                return Err("Trailing arguments are not allowed after `help`.".to_string().into());
901
0
            }
902
903
0
            parse_options.parse(next_arg, &mut remaining_args)?;
904
0
            continue;
905
0
        }
906
907
0
        if let Some(ref mut parse_subcommand) = parse_subcommand {
908
0
            if parse_subcommand.parse(help, cmd_name, next_arg, remaining_args)? {
909
                // Unset `help`, since we handled it in the subcommand
910
0
                help = false;
911
0
                break 'parse_args;
912
0
            }
913
0
        }
914
915
0
        options_ended |= parse_positionals.parse(&mut positional_index, next_arg)?;
916
    }
917
918
0
    if help {
919
0
        Err(EarlyExit { output: help_func(), status: Ok(()) })
920
    } else {
921
0
        Ok(())
922
    }
923
0
}
924
925
#[doc(hidden)]
926
pub struct ParseStructOptions<'a> {
927
    /// A mapping from option string literals to the entry
928
    /// in the output table. This may contain multiple entries mapping to
929
    /// the same location in the table if both a short and long version
930
    /// of the option exist (`-z` and `--zoo`).
931
    pub arg_to_slot: &'static [(&'static str, usize)],
932
933
    /// The storage for argument output data.
934
    pub slots: &'a mut [ParseStructOption<'a>],
935
}
936
937
impl<'a> ParseStructOptions<'a> {
938
    /// Parse a commandline option.
939
    ///
940
    /// `arg`: the current option argument being parsed (e.g. `--foo`).
941
    /// `remaining_args`: the remaining command line arguments. This slice
942
    /// will be advanced forwards if the option takes a value argument.
943
0
    fn parse(&mut self, arg: &str, remaining_args: &mut &[&str]) -> Result<(), String> {
944
0
        let pos = self
945
0
            .arg_to_slot
946
0
            .iter()
947
0
            .find_map(|&(name, pos)| if name == arg { Some(pos) } else { None })
948
0
            .ok_or_else(|| unrecognized_argument(arg))?;
949
950
0
        match self.slots[pos] {
951
0
            ParseStructOption::Flag(ref mut b) => b.set_flag(arg),
952
0
            ParseStructOption::Value(ref mut pvs) => {
953
0
                let value = remaining_args
954
0
                    .first()
955
0
                    .ok_or_else(|| ["No value provided for option '", arg, "'.\n"].concat())?;
956
0
                *remaining_args = &remaining_args[1..];
957
0
                pvs.fill_slot(arg, value).map_err(|s| {
958
0
                    ["Error parsing option '", arg, "' with value '", value, "': ", &s, "\n"]
959
0
                        .concat()
960
0
                })?;
961
            }
962
        }
963
964
0
        Ok(())
965
0
    }
966
}
967
968
0
fn unrecognized_argument(x: &str) -> String {
969
0
    ["Unrecognized argument: ", x, "\n"].concat()
970
0
}
971
972
// `--` or `-` options, including a mutable reference to their value.
973
#[doc(hidden)]
974
pub enum ParseStructOption<'a> {
975
    // A flag which is set to `true` when provided.
976
    Flag(&'a mut dyn ParseFlag),
977
    // A value which is parsed from the string following the `--` argument,
978
    // e.g. `--foo bar`.
979
    Value(&'a mut dyn ParseValueSlot),
980
}
981
982
#[doc(hidden)]
983
pub struct ParseStructPositionals<'a> {
984
    pub positionals: &'a mut [ParseStructPositional<'a>],
985
    pub last_is_repeating: bool,
986
    pub last_is_greedy: bool,
987
}
988
989
impl<'a> ParseStructPositionals<'a> {
990
    /// Parse the next positional argument.
991
    ///
992
    /// `arg`: the argument supplied by the user.
993
    ///
994
    /// Returns true if non-positional argument parsing should stop
995
    /// after this one.
996
0
    fn parse(&mut self, index: &mut usize, arg: &str) -> Result<bool, EarlyExit> {
997
0
        if *index < self.positionals.len() {
998
0
            self.positionals[*index].parse(arg)?;
999
1000
0
            if self.last_is_repeating && *index == self.positionals.len() - 1 {
1001
                // Don't increment position if we're at the last arg
1002
                // *and* the last arg is repeating. If it's also remainder,
1003
                // halt non-option processing after this.
1004
0
                Ok(self.last_is_greedy)
1005
            } else {
1006
                // If it is repeating, though, increment the index and continue
1007
                // processing options.
1008
0
                *index += 1;
1009
0
                Ok(false)
1010
            }
1011
        } else {
1012
0
            Err(EarlyExit { output: unrecognized_arg(arg), status: Err(()) })
1013
        }
1014
0
    }
1015
}
1016
1017
#[doc(hidden)]
1018
pub struct ParseStructPositional<'a> {
1019
    // The positional's name
1020
    pub name: &'static str,
1021
1022
    // The function to parse the positional.
1023
    pub slot: &'a mut dyn ParseValueSlot,
1024
}
1025
1026
impl<'a> ParseStructPositional<'a> {
1027
    /// Parse a positional argument.
1028
    ///
1029
    /// `arg`: the argument supplied by the user.
1030
0
    fn parse(&mut self, arg: &str) -> Result<(), EarlyExit> {
1031
0
        self.slot.fill_slot("", arg).map_err(|s| {
1032
0
            [
1033
0
                "Error parsing positional argument '",
1034
0
                self.name,
1035
0
                "' with value '",
1036
0
                arg,
1037
0
                "': ",
1038
0
                &s,
1039
0
                "\n",
1040
0
            ]
1041
0
            .concat()
1042
0
            .into()
1043
0
        })
1044
0
    }
1045
}
1046
1047
// A type to simplify parsing struct subcommands.
1048
//
1049
// This indirection is necessary to allow abstracting over `FromArgs` instances with different
1050
// generic parameters.
1051
#[doc(hidden)]
1052
pub struct ParseStructSubCommand<'a> {
1053
    // The subcommand commands
1054
    pub subcommands: &'static [&'static CommandInfo],
1055
1056
    pub dynamic_subcommands: &'a [&'static CommandInfo],
1057
1058
    // The function to parse the subcommand arguments.
1059
    #[allow(clippy::type_complexity)]
1060
    pub parse_func: &'a mut dyn FnMut(&[&str], &[&str]) -> Result<(), EarlyExit>,
1061
}
1062
1063
impl<'a> ParseStructSubCommand<'a> {
1064
0
    fn parse(
1065
0
        &mut self,
1066
0
        help: bool,
1067
0
        cmd_name: &[&str],
1068
0
        arg: &str,
1069
0
        remaining_args: &[&str],
1070
0
    ) -> Result<bool, EarlyExit> {
1071
0
        for subcommand in self.subcommands.iter().chain(self.dynamic_subcommands.iter()) {
1072
0
            if subcommand.name == arg {
1073
0
                let mut command = cmd_name.to_owned();
1074
0
                command.push(subcommand.name);
1075
                let prepended_help;
1076
0
                let remaining_args = if help {
1077
0
                    prepended_help = prepend_help(remaining_args);
1078
0
                    &prepended_help
1079
                } else {
1080
0
                    remaining_args
1081
                };
1082
1083
0
                (self.parse_func)(&command, remaining_args)?;
1084
1085
0
                return Ok(true);
1086
0
            }
1087
        }
1088
1089
0
        Ok(false)
1090
0
    }
1091
}
1092
1093
// Prepend `help` to a list of arguments.
1094
// This is used to pass the `help` argument on to subcommands.
1095
0
fn prepend_help<'a>(args: &[&'a str]) -> Vec<&'a str> {
1096
0
    [&["help"], args].concat()
1097
0
}
1098
1099
#[doc(hidden)]
1100
0
pub fn print_subcommands<'a>(commands: impl Iterator<Item = &'a CommandInfo>) -> String {
1101
0
    let mut out = String::new();
1102
0
    for cmd in commands {
1103
0
        argh_shared::write_description(&mut out, cmd);
1104
0
    }
1105
0
    out
1106
0
}
1107
1108
0
fn unrecognized_arg(arg: &str) -> String {
1109
0
    ["Unrecognized argument: ", arg, "\n"].concat()
1110
0
}
1111
1112
// An error string builder to report missing required options and subcommands.
1113
#[doc(hidden)]
1114
#[derive(Default)]
1115
pub struct MissingRequirements {
1116
    options: Vec<&'static str>,
1117
    subcommands: Option<Vec<&'static CommandInfo>>,
1118
    positional_args: Vec<&'static str>,
1119
}
1120
1121
const NEWLINE_INDENT: &str = "\n    ";
1122
1123
impl MissingRequirements {
1124
    // Add a missing required option.
1125
    #[doc(hidden)]
1126
0
    pub fn missing_option(&mut self, name: &'static str) {
1127
0
        self.options.push(name)
1128
0
    }
1129
1130
    // Add a missing required subcommand.
1131
    #[doc(hidden)]
1132
0
    pub fn missing_subcommands(&mut self, commands: impl Iterator<Item = &'static CommandInfo>) {
1133
0
        self.subcommands = Some(commands.collect());
1134
0
    }
1135
1136
    // Add a missing positional argument.
1137
    #[doc(hidden)]
1138
0
    pub fn missing_positional_arg(&mut self, name: &'static str) {
1139
0
        self.positional_args.push(name)
1140
0
    }
1141
1142
    // If any missing options or subcommands were provided, returns an error string
1143
    // describing the missing args.
1144
    #[doc(hidden)]
1145
0
    pub fn err_on_any(&self) -> Result<(), String> {
1146
0
        if self.options.is_empty() && self.subcommands.is_none() && self.positional_args.is_empty()
1147
        {
1148
0
            return Ok(());
1149
0
        }
1150
1151
0
        let mut output = String::new();
1152
1153
0
        if !self.positional_args.is_empty() {
1154
0
            output.push_str("Required positional arguments not provided:");
1155
0
            for arg in &self.positional_args {
1156
0
                output.push_str(NEWLINE_INDENT);
1157
0
                output.push_str(arg);
1158
0
            }
1159
0
        }
1160
1161
0
        if !self.options.is_empty() {
1162
0
            if !self.positional_args.is_empty() {
1163
0
                output.push('\n');
1164
0
            }
1165
0
            output.push_str("Required options not provided:");
1166
0
            for option in &self.options {
1167
0
                output.push_str(NEWLINE_INDENT);
1168
0
                output.push_str(option);
1169
0
            }
1170
0
        }
1171
1172
0
        if let Some(missing_subcommands) = &self.subcommands {
1173
0
            if !self.options.is_empty() {
1174
0
                output.push('\n');
1175
0
            }
1176
0
            output.push_str("One of the following subcommands must be present:");
1177
0
            output.push_str(NEWLINE_INDENT);
1178
0
            output.push_str("help");
1179
0
            for subcommand in missing_subcommands {
1180
0
                output.push_str(NEWLINE_INDENT);
1181
0
                output.push_str(subcommand.name);
1182
0
            }
1183
0
        }
1184
1185
0
        output.push('\n');
1186
1187
0
        Err(output)
1188
0
    }
1189
}
1190
1191
#[cfg(test)]
1192
mod test {
1193
    use super::*;
1194
1195
    #[test]
1196
    fn test_cmd_extraction() {
1197
        let expected = "test_cmd";
1198
        let path = format!("/tmp/{}", expected);
1199
        let cmd = cmd(&path, &path);
1200
        assert_eq!(expected, cmd);
1201
    }
1202
}