• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

clap-rs / clap / 1031648384

pending completion
1031648384

Pull #2593

github

GitHub
Merge 92b7f1c26 into 894be6799
Pull Request #2593: feat(derive): Allow skipping ArgEnum variants

100 of 100 new or added lines in 4 files covered. (100.0%)

11348 of 13223 relevant lines covered (85.82%)

12.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

45.24
/src/derive.rs
1
//! This module contains traits that are usable with the `#[derive(...)].`
2
//! macros in [`clap_derive`].
3

4
use crate::{App, ArgMatches, Error};
5

6
use std::ffi::OsString;
7

8
/// Parse command-line arguments into `Self`.
9
///
10
/// The primary one-stop-shop trait used to create an instance of a `clap`
11
/// [`App`], conduct the parsing, and turn the resulting [`ArgMatches`] back
12
/// into concrete instance of the user struct.
13
///
14
/// This trait is primarily a convenience on top of [`FromArgMatches`] +
15
/// [`IntoApp`] which uses those two underlying traits to build the two
16
/// fundamental functions `parse` which uses the `std::env::args_os` iterator,
17
/// and `parse_from` which allows the consumer to supply the iterator (along
18
/// with fallible options for each).
19
///
20
/// See also [`Subcommand`] and [`Args`].
21
///
22
/// # Examples
23
///
24
/// The following example creates a `Context` struct that would be used
25
/// throughout the application representing the normalized values coming from
26
/// the CLI.
27
///
28
/// ```rust
29
/// # use clap::{Clap};
30
/// /// My super CLI
31
/// #[derive(Clap)]
32
/// #[clap(name = "demo")]
33
/// struct Context {
34
///     /// More verbose output
35
///     #[clap(long)]
36
///     verbose: bool,
37
///     /// An optional name
38
///     #[clap(short, long)]
39
///     name: Option<String>,
40
/// }
41
/// ```
42
///
43
/// The equivalent [`App`] struct + `From` implementation:
44
///
45
/// ```rust
46
/// # use clap::{App, Arg, ArgMatches};
47
/// App::new("demo")
48
///     .about("My super CLI")
49
///     .arg(Arg::new("verbose")
50
///         .long("verbose")
51
///         .about("More verbose output"))
52
///     .arg(Arg::new("name")
53
///         .long("name")
54
///         .short('n')
55
///         .about("An optional name")
56
///         .takes_value(true));
57
///
58
/// struct Context {
59
///     verbose: bool,
60
///     name: Option<String>,
61
/// }
62
///
63
/// impl From<ArgMatches> for Context {
64
///     fn from(m: ArgMatches) -> Self {
65
///         Context {
66
///             verbose: m.is_present("verbose"),
67
///             name: m.value_of("name").map(|n| n.to_owned()),
68
///         }
69
///     }
70
/// }
71
/// ```
72
///
73
pub trait Clap: FromArgMatches + IntoApp + Sized {
74
    /// Parse from `std::env::args_os()`, exit on error
75
    fn parse() -> Self {
×
76
        let matches = <Self as IntoApp>::into_app().get_matches();
×
77
        <Self as FromArgMatches>::from_arg_matches(&matches).expect("IntoApp validated everything")
×
78
    }
79

80
    /// Parse from `std::env::args_os()`, return Err on error.
81
    fn try_parse() -> Result<Self, Error> {
×
82
        let matches = <Self as IntoApp>::into_app().try_get_matches()?;
×
83
        Ok(<Self as FromArgMatches>::from_arg_matches(&matches)
×
84
            .expect("IntoApp validated everything"))
×
85
    }
86

87
    /// Parse from iterator, exit on error
88
    fn parse_from<I, T>(itr: I) -> Self
137✔
89
    where
90
        I: IntoIterator<Item = T>,
91
        // TODO (@CreepySkeleton): discover a way to avoid cloning here
92
        T: Into<OsString> + Clone,
93
    {
94
        let matches = <Self as IntoApp>::into_app().get_matches_from(itr);
137✔
95
        <Self as FromArgMatches>::from_arg_matches(&matches).expect("IntoApp validated everything")
144✔
96
    }
97

98
    /// Parse from iterator, return Err on error.
99
    fn try_parse_from<I, T>(itr: I) -> Result<Self, Error>
54✔
100
    where
101
        I: IntoIterator<Item = T>,
102
        // TODO (@CreepySkeleton): discover a way to avoid cloning here
103
        T: Into<OsString> + Clone,
104
    {
105
        let matches = <Self as IntoApp>::into_app().try_get_matches_from(itr)?;
54✔
106
        Ok(<Self as FromArgMatches>::from_arg_matches(&matches)
11✔
107
            .expect("IntoApp validated everything"))
×
108
    }
109

110
    /// Update from iterator, exit on error
111
    fn update_from<I, T>(&mut self, itr: I)
5✔
112
    where
113
        I: IntoIterator<Item = T>,
114
        // TODO (@CreepySkeleton): discover a way to avoid cloning here
115
        T: Into<OsString> + Clone,
116
    {
117
        // TODO find a way to get partial matches
118
        let matches = <Self as IntoApp>::into_app_for_update().get_matches_from(itr);
5✔
119
        <Self as FromArgMatches>::update_from_arg_matches(self, &matches);
5✔
120
    }
121

122
    /// Update from iterator, return Err on error.
123
    fn try_update_from<I, T>(&mut self, itr: I) -> Result<(), Error>
124
    where
125
        I: IntoIterator<Item = T>,
126
        // TODO (@CreepySkeleton): discover a way to avoid cloning here
127
        T: Into<OsString> + Clone,
128
    {
129
        let matches = <Self as IntoApp>::into_app_for_update().try_get_matches_from(itr)?;
×
130
        <Self as FromArgMatches>::update_from_arg_matches(self, &matches);
×
131
        Ok(())
×
132
    }
133
}
134

135
/// Build an [`App`] relevant for a user-defined container.
136
pub trait IntoApp: Sized {
137
    /// Build an [`App`] that can instantiate `Self`.
138
    ///
139
    /// See [`FromArgMatches::from_arg_matches`] for instantiating `Self`.
140
    fn into_app<'help>() -> App<'help>;
141
    /// Build an [`App`] that can update `self`.
142
    ///
143
    /// See [`FromArgMatches::update_from_arg_matches`] for updating `self`.
144
    fn into_app_for_update<'help>() -> App<'help>;
145
}
146

147
/// Converts an instance of [`ArgMatches`] to a user-defined container.
148
pub trait FromArgMatches: Sized {
149
    /// Instantiate `Self` from [`ArgMatches`], parsing the arguments as needed.
150
    ///
151
    /// Motivation: If our application had two CLI options, `--name
152
    /// <STRING>` and the flag `--debug`, we may create a struct as follows:
153
    ///
154
    /// ```no_run
155
    /// struct Context {
156
    ///     name: String,
157
    ///     debug: bool
158
    /// }
159
    /// ```
160
    ///
161
    /// We then need to convert the `ArgMatches` that `clap` generated into our struct.
162
    /// `from_arg_matches` serves as the equivalent of:
163
    ///
164
    /// ```no_run
165
    /// # use clap::ArgMatches;
166
    /// # struct Context {
167
    /// #   name: String,
168
    /// #   debug: bool
169
    /// # }
170
    /// impl From<ArgMatches> for Context {
171
    ///    fn from(m: ArgMatches) -> Self {
172
    ///        Context {
173
    ///            name: m.value_of("name").unwrap().to_string(),
174
    ///            debug: m.is_present("debug"),
175
    ///        }
176
    ///    }
177
    /// }
178
    /// ```
179
    fn from_arg_matches(matches: &ArgMatches) -> Option<Self>;
180

181
    /// Assign values from `ArgMatches` to `self`.
182
    fn update_from_arg_matches(&mut self, matches: &ArgMatches);
183
}
184

185
/// Parse arguments into a user-defined container.
186
///
187
/// Implementing this trait let's a parent container delegate argument parsing behavior to `Self`.
188
/// with:
189
/// - `#[clap(flatten)] args: ChildArgs`: Can only be used with struct fields.
190
/// - Implicitly: Can only be used with enum variants.
191
///
192
///
193
/// # Example
194
///
195
/// ```rust
196
/// #[derive(clap::Clap)]
197
/// struct Args {
198
///     #[clap(flatten)]
199
///     logging: LogArgs,
200
/// }
201
///
202
/// #[derive(clap::Args)]
203
/// struct LogArgs {
204
///     #[clap(long, short = 'v', parse(from_occurrences))]
205
///     verbose: i8,
206
/// }
207
/// ```
208
pub trait Args: FromArgMatches + Sized {
209
    /// Append to [`App`] so it can instantiate `Self`.
210
    ///
211
    /// See also [`IntoApp`].
212
    fn augment_args(app: App<'_>) -> App<'_>;
213
    /// Append to [`App`] so it can update `self`.
214
    ///
215
    /// This is used to implement `#[clap(flatten)]`
216
    ///
217
    /// See also [`IntoApp`].
218
    fn augment_args_for_update(app: App<'_>) -> App<'_>;
219
}
220

221
/// Parse a sub-command into a user-defined enum.
222
///
223
/// Implementing this trait let's a parent container delegate subcommand behavior to `Self`.
224
/// with:
225
/// - `#[clap(subcommand)] field: SubCmd`: Can be used with either struct fields or enum variants.
226
/// - `#[clap(flatten)] Variant(SubCmd)`: Can only be used with enum variants.
227
///
228
///
229
/// # Example
230
///
231
/// ```rust
232
/// #[derive(clap::Clap)]
233
/// struct Args {
234
///     #[clap(subcommand)]
235
///     action: Action,
236
/// }
237
///
238
/// #[derive(clap::Subcommand)]
239
/// enum Action {
240
///     Add,
241
///     Remove,
242
/// }
243
/// ```
244
pub trait Subcommand: FromArgMatches + Sized {
245
    /// Append to [`App`] so it can instantiate `Self`.
246
    ///
247
    /// See also [`IntoApp`].
248
    fn augment_subcommands(app: App<'_>) -> App<'_>;
249
    /// Append to [`App`] so it can update `self`.
250
    ///
251
    /// This is used to implement `#[clap(flatten)]`
252
    ///
253
    /// See also [`IntoApp`].
254
    fn augment_subcommands_for_update(app: App<'_>) -> App<'_>;
255
}
256

257
/// Parse arguments into enums.
258
///
259
/// When deriving [`Clap`], a field whose type implements `ArgEnum` can have the attribute
260
/// `#[clap(arg_enum)]`.  In addition to parsing, help and error messages may report possible
261
/// variants.
262
///
263
/// # Example
264
///
265
/// ```rust
266
/// #[derive(clap::Clap)]
267
/// struct Args {
268
///     #[clap(arg_enum)]
269
///     level: Level,
270
/// }
271
///
272
/// #[derive(clap::ArgEnum)]
273
/// enum Level {
274
///     Debug,
275
///     Info,
276
///     Warning,
277
///     Error,
278
/// }
279
/// ```
280
pub trait ArgEnum: Sized {
281
    /// All possible argument choices, in display order.
282
    const VARIANTS: &'static [&'static str];
283

284
    /// Parse an argument into `Self`.
285
    fn from_str(input: &str, case_insensitive: bool) -> Result<Self, String>;
286
}
287

288
impl<T: Clap> Clap for Box<T> {
289
    fn parse() -> Self {
×
290
        Box::new(<T as Clap>::parse())
×
291
    }
292

293
    fn try_parse() -> Result<Self, Error> {
×
294
        <T as Clap>::try_parse().map(Box::new)
×
295
    }
296

297
    fn parse_from<I, It>(itr: I) -> Self
298
    where
299
        I: IntoIterator<Item = It>,
300
        // TODO (@CreepySkeleton): discover a way to avoid cloning here
301
        It: Into<OsString> + Clone,
302
    {
303
        Box::new(<T as Clap>::parse_from(itr))
×
304
    }
305

306
    fn try_parse_from<I, It>(itr: I) -> Result<Self, Error>
307
    where
308
        I: IntoIterator<Item = It>,
309
        // TODO (@CreepySkeleton): discover a way to avoid cloning here
310
        It: Into<OsString> + Clone,
311
    {
312
        <T as Clap>::try_parse_from(itr).map(Box::new)
×
313
    }
314
}
315

316
impl<T: IntoApp> IntoApp for Box<T> {
317
    fn into_app<'help>() -> App<'help> {
×
318
        <T as IntoApp>::into_app()
×
319
    }
320
    fn into_app_for_update<'help>() -> App<'help> {
×
321
        <T as IntoApp>::into_app_for_update()
×
322
    }
323
}
324

325
impl<T: FromArgMatches> FromArgMatches for Box<T> {
326
    fn from_arg_matches(matches: &ArgMatches) -> Option<Self> {
4✔
327
        <T as FromArgMatches>::from_arg_matches(matches).map(Box::new)
4✔
328
    }
329
    fn update_from_arg_matches(&mut self, matches: &ArgMatches) {
2✔
330
        <T as FromArgMatches>::update_from_arg_matches(self, matches)
2✔
331
    }
332
}
333

334
impl<T: Args> Args for Box<T> {
335
    fn augment_args(app: App<'_>) -> App<'_> {
2✔
336
        <T as Args>::augment_args(app)
2✔
337
    }
338
    fn augment_args_for_update(app: App<'_>) -> App<'_> {
×
339
        <T as Args>::augment_args_for_update(app)
×
340
    }
341
}
342

343
impl<T: Subcommand> Subcommand for Box<T> {
344
    fn augment_subcommands(app: App<'_>) -> App<'_> {
2✔
345
        <T as Subcommand>::augment_subcommands(app)
2✔
346
    }
347
    fn augment_subcommands_for_update(app: App<'_>) -> App<'_> {
1✔
348
        <T as Subcommand>::augment_subcommands_for_update(app)
1✔
349
    }
350
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2024 Coveralls, Inc