2020-04-28 22:30:04 -04:00
|
|
|
//! This module contains traits that are usable with the `#[derive(...)].`
|
|
|
|
//! macros in [`clap_derive`].
|
2020-02-08 22:36:00 +03:00
|
|
|
|
|
|
|
use crate::{App, ArgMatches, Error};
|
2020-04-27 21:47:08 +03:00
|
|
|
|
2020-02-08 22:36:00 +03:00
|
|
|
use std::ffi::OsString;
|
|
|
|
|
2020-04-28 22:30:04 -04:00
|
|
|
/// The primary one-stop-shop trait used to create an instance of a `clap`
|
|
|
|
/// [`App`], conduct the parsing, and turn the resulting [`ArgMatches`] back
|
|
|
|
/// into concrete instance of the user struct.
|
|
|
|
///
|
2020-08-12 01:21:46 +02:00
|
|
|
/// This trait is primarily a convenience on top of [`FromArgMatches`] +
|
2020-04-28 22:30:04 -04:00
|
|
|
/// [`IntoApp`] which uses those two underlying traits to build the two
|
|
|
|
/// fundamental functions `parse` which uses the `std::env::args_os` iterator,
|
|
|
|
/// and `parse_from` which allows the consumer to supply the iterator (along
|
|
|
|
/// with fallible options for each).
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// The following example creates a `Context` struct that would be used
|
|
|
|
/// throughout the application representing the normalized values coming from
|
|
|
|
/// the CLI.
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// # use clap::{Clap};
|
|
|
|
/// /// My super CLI
|
|
|
|
/// #[derive(Clap)]
|
|
|
|
/// #[clap(name = "demo")]
|
|
|
|
/// struct Context {
|
|
|
|
/// /// More verbose output
|
|
|
|
/// #[clap(long)]
|
|
|
|
/// verbose: bool,
|
|
|
|
/// /// An optional name
|
|
|
|
/// #[clap(short, long)]
|
|
|
|
/// name: Option<String>,
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
2020-10-09 01:27:13 +03:00
|
|
|
/// The equivalent [`App`] struct + `From` implementation:
|
2020-04-28 22:30:04 -04:00
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// # use clap::{App, Arg, ArgMatches};
|
|
|
|
/// App::new("demo")
|
|
|
|
/// .about("My super CLI")
|
2020-08-11 16:08:05 +02:00
|
|
|
/// .arg(Arg::new("verbose")
|
2020-04-28 22:30:04 -04:00
|
|
|
/// .long("verbose")
|
2020-08-11 16:08:05 +02:00
|
|
|
/// .about("More verbose output"))
|
|
|
|
/// .arg(Arg::new("name")
|
2020-04-28 22:30:04 -04:00
|
|
|
/// .long("name")
|
2020-12-08 02:40:18 +01:00
|
|
|
/// .short('n')
|
2020-08-11 16:08:05 +02:00
|
|
|
/// .about("An optional name")
|
2020-04-28 22:30:04 -04:00
|
|
|
/// .takes_value(true));
|
|
|
|
///
|
|
|
|
/// struct Context {
|
|
|
|
/// verbose: bool,
|
|
|
|
/// name: Option<String>,
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// impl From<ArgMatches> for Context {
|
|
|
|
/// fn from(m: ArgMatches) -> Self {
|
|
|
|
/// Context {
|
|
|
|
/// verbose: m.is_present("verbose"),
|
|
|
|
/// name: m.value_of("name").map(|n| n.to_owned()),
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
/// ```
|
2020-09-12 10:15:14 -07:00
|
|
|
///
|
2020-02-08 22:36:00 +03:00
|
|
|
pub trait Clap: FromArgMatches + IntoApp + Sized {
|
2020-04-28 22:30:04 -04:00
|
|
|
/// Parse from `std::env::args_os()`, exit on error
|
2020-02-08 22:36:00 +03:00
|
|
|
fn parse() -> Self {
|
|
|
|
let matches = <Self as IntoApp>::into_app().get_matches();
|
|
|
|
<Self as FromArgMatches>::from_arg_matches(&matches)
|
|
|
|
}
|
|
|
|
|
2020-04-28 22:30:04 -04:00
|
|
|
/// Parse from `std::env::args_os()`, return Err on error.
|
2020-02-08 22:36:00 +03:00
|
|
|
fn try_parse() -> Result<Self, Error> {
|
|
|
|
let matches = <Self as IntoApp>::into_app().try_get_matches()?;
|
|
|
|
Ok(<Self as FromArgMatches>::from_arg_matches(&matches))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse from iterator, exit on error
|
|
|
|
fn parse_from<I, T>(itr: I) -> Self
|
|
|
|
where
|
|
|
|
I: IntoIterator<Item = T>,
|
|
|
|
// TODO (@CreepySkeleton): discover a way to avoid cloning here
|
|
|
|
T: Into<OsString> + Clone,
|
|
|
|
{
|
|
|
|
let matches = <Self as IntoApp>::into_app().get_matches_from(itr);
|
|
|
|
<Self as FromArgMatches>::from_arg_matches(&matches)
|
|
|
|
}
|
|
|
|
|
2020-02-25 16:50:47 +00:00
|
|
|
/// Parse from iterator, return Err on error.
|
2020-02-08 22:36:00 +03:00
|
|
|
fn try_parse_from<I, T>(itr: I) -> Result<Self, Error>
|
|
|
|
where
|
|
|
|
I: IntoIterator<Item = T>,
|
|
|
|
// TODO (@CreepySkeleton): discover a way to avoid cloning here
|
|
|
|
T: Into<OsString> + Clone,
|
|
|
|
{
|
|
|
|
let matches = <Self as IntoApp>::into_app().try_get_matches_from(itr)?;
|
|
|
|
Ok(<Self as FromArgMatches>::from_arg_matches(&matches))
|
|
|
|
}
|
2020-07-07 13:17:36 +02:00
|
|
|
|
|
|
|
/// Update from iterator, exit on error
|
|
|
|
fn update_from<I, T>(&mut self, itr: I)
|
|
|
|
where
|
|
|
|
I: IntoIterator<Item = T>,
|
|
|
|
// TODO (@CreepySkeleton): discover a way to avoid cloning here
|
|
|
|
T: Into<OsString> + Clone,
|
|
|
|
{
|
|
|
|
// TODO find a way to get partial matches
|
2020-11-14 10:58:30 +01:00
|
|
|
let matches = <Self as IntoApp>::into_app_for_update().get_matches_from(itr);
|
2020-07-07 13:17:36 +02:00
|
|
|
<Self as FromArgMatches>::update_from_arg_matches(self, &matches);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Update from iterator, return Err on error.
|
|
|
|
fn try_update_from<I, T>(&mut self, itr: I) -> Result<(), Error>
|
|
|
|
where
|
|
|
|
I: IntoIterator<Item = T>,
|
|
|
|
// TODO (@CreepySkeleton): discover a way to avoid cloning here
|
|
|
|
T: Into<OsString> + Clone,
|
|
|
|
{
|
2020-11-14 10:58:30 +01:00
|
|
|
let matches = <Self as IntoApp>::into_app_for_update().try_get_matches_from(itr)?;
|
2020-07-07 13:31:43 +02:00
|
|
|
<Self as FromArgMatches>::update_from_arg_matches(self, &matches);
|
|
|
|
Ok(())
|
2020-07-07 13:17:36 +02:00
|
|
|
}
|
2020-02-08 22:36:00 +03:00
|
|
|
}
|
|
|
|
|
2021-07-12 15:43:03 -05:00
|
|
|
/// Build an [`App`] according to the struct
|
2020-02-08 22:36:00 +03:00
|
|
|
pub trait IntoApp: Sized {
|
2021-07-12 15:43:03 -05:00
|
|
|
/// Build an [`App`] that can instantiate `Self`.
|
|
|
|
///
|
|
|
|
/// See [`FromArgMatches::from_arg_matches`] for instantiating `Self`.
|
2020-07-19 20:11:29 +03:00
|
|
|
fn into_app<'help>() -> App<'help>;
|
2021-07-12 15:43:03 -05:00
|
|
|
/// Build an [`App`] that can update `self`.
|
|
|
|
///
|
|
|
|
/// See [`FromArgMatches::update_from_arg_matches`] for updating `self`.
|
2020-11-14 10:58:30 +01:00
|
|
|
fn into_app_for_update<'help>() -> App<'help>;
|
2021-07-12 15:43:03 -05:00
|
|
|
/// Append to [`App`] so it can instantiate `Self`.
|
|
|
|
///
|
|
|
|
/// This is used to implement `#[clap(flatten)]`
|
|
|
|
fn augment_clap(app: App<'_>) -> App<'_>;
|
|
|
|
/// Append to [`App`] so it can update `self`.
|
|
|
|
///
|
|
|
|
/// This is used to implement `#[clap(flatten)]`
|
2020-11-14 10:58:30 +01:00
|
|
|
fn augment_clap_for_update(app: App<'_>) -> App<'_>;
|
2020-02-08 22:36:00 +03:00
|
|
|
}
|
|
|
|
|
2021-07-12 15:43:03 -05:00
|
|
|
/// Converts an instance of [`ArgMatches`] to a user-defined container.
|
2020-02-08 22:36:00 +03:00
|
|
|
pub trait FromArgMatches: Sized {
|
2021-07-12 15:43:03 -05:00
|
|
|
/// Instantiate `Self` from [`ArgMatches`], parsing the arguments as needed.
|
2020-04-28 22:42:54 -04:00
|
|
|
///
|
2021-07-12 15:43:03 -05:00
|
|
|
/// Motivation: If our application had two CLI options, `--name
|
|
|
|
/// <STRING>` and the flag `--debug`, we may create a struct as follows:
|
2020-04-28 22:42:54 -04:00
|
|
|
///
|
|
|
|
/// ```no_run
|
|
|
|
/// struct Context {
|
|
|
|
/// name: String,
|
|
|
|
/// debug: bool
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
2021-07-12 15:43:03 -05:00
|
|
|
/// We then need to convert the `ArgMatches` that `clap` generated into our struct.
|
|
|
|
/// `from_arg_matches` serves as the equivalent of:
|
2020-04-28 22:42:54 -04:00
|
|
|
///
|
|
|
|
/// ```no_run
|
2020-08-11 16:08:05 +02:00
|
|
|
/// # use clap::ArgMatches;
|
|
|
|
/// # struct Context {
|
|
|
|
/// # name: String,
|
|
|
|
/// # debug: bool
|
|
|
|
/// # }
|
2020-04-28 22:42:54 -04:00
|
|
|
/// impl From<ArgMatches> for Context {
|
|
|
|
/// fn from(m: ArgMatches) -> Self {
|
|
|
|
/// Context {
|
2020-08-11 16:08:05 +02:00
|
|
|
/// name: m.value_of("name").unwrap().to_string(),
|
2020-04-28 22:42:54 -04:00
|
|
|
/// debug: m.is_present("debug"),
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
/// ```
|
2020-02-08 22:36:00 +03:00
|
|
|
fn from_arg_matches(matches: &ArgMatches) -> Self;
|
2020-04-29 11:44:38 +02:00
|
|
|
|
2021-07-12 15:43:03 -05:00
|
|
|
/// Assign values from `ArgMatches` to `self`.
|
2020-04-29 11:44:38 +02:00
|
|
|
fn update_from_arg_matches(&mut self, matches: &ArgMatches);
|
2020-02-08 22:36:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/// @TODO @release @docs
|
|
|
|
pub trait Subcommand: Sized {
|
2021-07-12 15:43:03 -05:00
|
|
|
/// Instantiate `Self` from subcommand name and [`ArgMatches`].
|
|
|
|
///
|
|
|
|
/// Returns `None` if subcommand does not exist
|
2020-08-05 15:14:22 +03:00
|
|
|
fn from_subcommand(subcommand: Option<(&str, &ArgMatches)>) -> Option<Self>;
|
2021-07-12 15:43:03 -05:00
|
|
|
/// Assign values from `ArgMatches` to `self`.
|
2020-08-25 12:56:26 +02:00
|
|
|
fn update_from_subcommand(&mut self, subcommand: Option<(&str, &ArgMatches)>);
|
2021-07-12 15:43:03 -05:00
|
|
|
/// Append to [`App`] so it can instantiate `Self`.
|
|
|
|
///
|
|
|
|
/// This is used to implement `#[clap(flatten)]`
|
|
|
|
///
|
|
|
|
/// See also [`IntoApp`].
|
2020-02-08 22:36:00 +03:00
|
|
|
fn augment_subcommands(app: App<'_>) -> App<'_>;
|
2021-07-12 15:43:03 -05:00
|
|
|
/// Append to [`App`] so it can update `self`.
|
|
|
|
///
|
|
|
|
/// This is used to implement `#[clap(flatten)]`
|
|
|
|
///
|
|
|
|
/// See also [`IntoApp`].
|
2020-11-14 10:58:30 +01:00
|
|
|
fn augment_subcommands_for_update(app: App<'_>) -> App<'_>;
|
2020-02-08 22:36:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/// @TODO @release @docs
|
2020-04-22 09:25:41 +02:00
|
|
|
pub trait ArgEnum: Sized {
|
|
|
|
/// @TODO @release @docs
|
|
|
|
const VARIANTS: &'static [&'static str];
|
|
|
|
|
|
|
|
/// @TODO @release @docs
|
|
|
|
fn from_str(input: &str, case_insensitive: bool) -> Result<Self, String>;
|
|
|
|
}
|
2020-02-13 18:21:01 +03:00
|
|
|
|
|
|
|
impl<T: Clap> Clap for Box<T> {
|
|
|
|
fn parse() -> Self {
|
|
|
|
Box::new(<T as Clap>::parse())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn try_parse() -> Result<Self, Error> {
|
|
|
|
<T as Clap>::try_parse().map(Box::new)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_from<I, It>(itr: I) -> Self
|
|
|
|
where
|
|
|
|
I: IntoIterator<Item = It>,
|
|
|
|
// TODO (@CreepySkeleton): discover a way to avoid cloning here
|
|
|
|
It: Into<OsString> + Clone,
|
|
|
|
{
|
|
|
|
Box::new(<T as Clap>::parse_from(itr))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn try_parse_from<I, It>(itr: I) -> Result<Self, Error>
|
|
|
|
where
|
|
|
|
I: IntoIterator<Item = It>,
|
|
|
|
// TODO (@CreepySkeleton): discover a way to avoid cloning here
|
|
|
|
It: Into<OsString> + Clone,
|
|
|
|
{
|
|
|
|
<T as Clap>::try_parse_from(itr).map(Box::new)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: IntoApp> IntoApp for Box<T> {
|
2020-07-19 20:11:29 +03:00
|
|
|
fn into_app<'help>() -> App<'help> {
|
2020-02-13 18:21:01 +03:00
|
|
|
<T as IntoApp>::into_app()
|
|
|
|
}
|
|
|
|
fn augment_clap(app: App<'_>) -> App<'_> {
|
|
|
|
<T as IntoApp>::augment_clap(app)
|
|
|
|
}
|
2020-11-14 10:58:30 +01:00
|
|
|
fn into_app_for_update<'help>() -> App<'help> {
|
|
|
|
<T as IntoApp>::into_app_for_update()
|
2020-08-25 11:03:09 +02:00
|
|
|
}
|
2020-11-14 10:58:30 +01:00
|
|
|
fn augment_clap_for_update(app: App<'_>) -> App<'_> {
|
|
|
|
<T as IntoApp>::augment_clap_for_update(app)
|
2020-08-25 11:03:09 +02:00
|
|
|
}
|
2020-02-13 18:21:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: FromArgMatches> FromArgMatches for Box<T> {
|
|
|
|
fn from_arg_matches(matches: &ArgMatches) -> Self {
|
|
|
|
Box::new(<T as FromArgMatches>::from_arg_matches(matches))
|
|
|
|
}
|
2020-04-29 11:44:38 +02:00
|
|
|
fn update_from_arg_matches(&mut self, matches: &ArgMatches) {
|
|
|
|
<T as FromArgMatches>::update_from_arg_matches(self, matches);
|
|
|
|
}
|
2020-02-13 18:21:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: Subcommand> Subcommand for Box<T> {
|
2020-08-05 15:14:22 +03:00
|
|
|
fn from_subcommand(subcommand: Option<(&str, &ArgMatches)>) -> Option<Self> {
|
|
|
|
<T as Subcommand>::from_subcommand(subcommand).map(Box::new)
|
2020-02-13 18:21:01 +03:00
|
|
|
}
|
2020-08-25 12:56:26 +02:00
|
|
|
fn update_from_subcommand(&mut self, subcommand: Option<(&str, &ArgMatches)>) {
|
|
|
|
<T as Subcommand>::update_from_subcommand(self, subcommand);
|
2020-04-29 11:44:38 +02:00
|
|
|
}
|
2020-02-13 18:21:01 +03:00
|
|
|
fn augment_subcommands(app: App<'_>) -> App<'_> {
|
|
|
|
<T as Subcommand>::augment_subcommands(app)
|
|
|
|
}
|
2020-11-14 10:58:30 +01:00
|
|
|
fn augment_subcommands_for_update(app: App<'_>) -> App<'_> {
|
|
|
|
<T as Subcommand>::augment_subcommands_for_update(app)
|
2020-08-25 11:03:09 +02:00
|
|
|
}
|
2020-02-13 18:21:01 +03:00
|
|
|
}
|