mirror of
https://github.com/clap-rs/clap
synced 2024-12-15 23:32:32 +00:00
490 lines
16 KiB
Rust
490 lines
16 KiB
Rust
//! This module contains traits that are usable with the `#[derive(...)].`
|
|
//! macros in [`clap_derive`].
|
|
|
|
use crate::{App, ArgMatches, Error, PossibleValue};
|
|
|
|
use std::ffi::OsString;
|
|
|
|
/// Parse command-line arguments into `Self`.
|
|
///
|
|
/// 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.
|
|
///
|
|
/// This trait is primarily a convenience on top of [`FromArgMatches`] +
|
|
/// [`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).
|
|
///
|
|
/// See also [`Subcommand`] and [`Args`].
|
|
///
|
|
/// See the
|
|
/// [derive reference](https://github.com/clap-rs/clap/blob/v3.0.8/examples/derive_ref/README.md)
|
|
/// for attributes and best practices.
|
|
///
|
|
/// **NOTE:** Deriving requires the `derive` feature flag
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// The following example creates a `Context` struct that would be used
|
|
/// throughout the application representing the normalized values coming from
|
|
/// the CLI.
|
|
///
|
|
#[cfg_attr(not(feature = "derive"), doc = " ```ignore")]
|
|
#[cfg_attr(feature = "derive", doc = " ```")]
|
|
/// /// My super CLI
|
|
/// #[derive(clap::Parser)]
|
|
/// #[clap(name = "demo")]
|
|
/// struct Context {
|
|
/// /// More verbose output
|
|
/// #[clap(long)]
|
|
/// verbose: bool,
|
|
/// /// An optional name
|
|
/// #[clap(short, long)]
|
|
/// name: Option<String>,
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// The equivalent [`App`] struct + `From` implementation:
|
|
///
|
|
/// ```rust
|
|
/// # use clap::{App, Arg, ArgMatches};
|
|
/// App::new("demo")
|
|
/// .about("My super CLI")
|
|
/// .arg(Arg::new("verbose")
|
|
/// .long("verbose")
|
|
/// .help("More verbose output"))
|
|
/// .arg(Arg::new("name")
|
|
/// .long("name")
|
|
/// .short('n')
|
|
/// .help("An optional name")
|
|
/// .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()),
|
|
/// }
|
|
/// }
|
|
/// }
|
|
/// ```
|
|
///
|
|
pub trait Parser: FromArgMatches + IntoApp + Sized {
|
|
/// Parse from `std::env::args_os()`, exit on error
|
|
fn parse() -> Self {
|
|
let matches = <Self as IntoApp>::into_app().get_matches();
|
|
let res =
|
|
<Self as FromArgMatches>::from_arg_matches(&matches).map_err(format_error::<Self>);
|
|
match res {
|
|
Ok(s) => s,
|
|
Err(e) => {
|
|
// Since this is more of a development-time error, we aren't doing as fancy of a quit
|
|
// as `get_matches`
|
|
e.exit()
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Parse from `std::env::args_os()`, return Err on error.
|
|
fn try_parse() -> Result<Self, Error> {
|
|
let matches = <Self as IntoApp>::into_app().try_get_matches()?;
|
|
<Self as FromArgMatches>::from_arg_matches(&matches).map_err(format_error::<Self>)
|
|
}
|
|
|
|
/// Parse from iterator, exit on error
|
|
fn parse_from<I, T>(itr: I) -> Self
|
|
where
|
|
I: IntoIterator<Item = T>,
|
|
T: Into<OsString> + Clone,
|
|
{
|
|
let matches = <Self as IntoApp>::into_app().get_matches_from(itr);
|
|
let res =
|
|
<Self as FromArgMatches>::from_arg_matches(&matches).map_err(format_error::<Self>);
|
|
match res {
|
|
Ok(s) => s,
|
|
Err(e) => {
|
|
// Since this is more of a development-time error, we aren't doing as fancy of a quit
|
|
// as `get_matches_from`
|
|
e.exit()
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Parse from iterator, return Err on error.
|
|
fn try_parse_from<I, T>(itr: I) -> Result<Self, Error>
|
|
where
|
|
I: IntoIterator<Item = T>,
|
|
T: Into<OsString> + Clone,
|
|
{
|
|
let matches = <Self as IntoApp>::into_app().try_get_matches_from(itr)?;
|
|
<Self as FromArgMatches>::from_arg_matches(&matches).map_err(format_error::<Self>)
|
|
}
|
|
|
|
/// Update from iterator, exit on error
|
|
fn update_from<I, T>(&mut self, itr: I)
|
|
where
|
|
I: IntoIterator<Item = T>,
|
|
T: Into<OsString> + Clone,
|
|
{
|
|
let matches = <Self as IntoApp>::into_app_for_update().get_matches_from(itr);
|
|
let res = <Self as FromArgMatches>::update_from_arg_matches(self, &matches)
|
|
.map_err(format_error::<Self>);
|
|
if let Err(e) = res {
|
|
// Since this is more of a development-time error, we aren't doing as fancy of a quit
|
|
// as `get_matches_from`
|
|
e.exit()
|
|
}
|
|
}
|
|
|
|
/// Update from iterator, return Err on error.
|
|
fn try_update_from<I, T>(&mut self, itr: I) -> Result<(), Error>
|
|
where
|
|
I: IntoIterator<Item = T>,
|
|
T: Into<OsString> + Clone,
|
|
{
|
|
let matches = <Self as IntoApp>::into_app_for_update().try_get_matches_from(itr)?;
|
|
<Self as FromArgMatches>::update_from_arg_matches(self, &matches)
|
|
.map_err(format_error::<Self>)
|
|
}
|
|
|
|
/// Deprecated, `StructOpt::clap` replaced with [`IntoApp::into_app`] (derive as part of
|
|
/// [`Parser`])
|
|
#[deprecated(
|
|
since = "3.0.0",
|
|
note = "`StructOpt::clap` is replaced with `IntoApp::into_app` (derived as part of `Parser`)"
|
|
)]
|
|
fn clap<'help>() -> App<'help> {
|
|
<Self as IntoApp>::into_app()
|
|
}
|
|
|
|
/// Deprecated, `StructOpt::from_clap` replaced with [`FromArgMatches::from_arg_matches`] (derive as part of
|
|
/// [`Parser`])
|
|
#[deprecated(
|
|
since = "3.0.0",
|
|
note = "`StructOpt::from_clap` is replaced with `FromArgMatches::from_arg_matches` (derived as part of `Parser`)"
|
|
)]
|
|
fn from_clap(matches: &ArgMatches) -> Self {
|
|
<Self as FromArgMatches>::from_arg_matches(matches).unwrap()
|
|
}
|
|
|
|
/// Deprecated, `StructOpt::from_args` replaced with `Parser::parse` (note the change in derives)
|
|
#[deprecated(
|
|
since = "3.0.0",
|
|
note = "`StructOpt::from_args` is replaced with `Parser::parse` (note the change in derives)"
|
|
)]
|
|
fn from_args() -> Self {
|
|
Self::parse()
|
|
}
|
|
|
|
/// Deprecated, `StructOpt::from_args_safe` replaced with `Parser::try_parse` (note the change in derives)
|
|
#[deprecated(
|
|
since = "3.0.0",
|
|
note = "`StructOpt::from_args_safe` is replaced with `Parser::try_parse` (note the change in derives)"
|
|
)]
|
|
fn from_args_safe() -> Result<Self, Error> {
|
|
Self::try_parse()
|
|
}
|
|
|
|
/// Deprecated, `StructOpt::from_iter` replaced with `Parser::parse_from` (note the change in derives)
|
|
#[deprecated(
|
|
since = "3.0.0",
|
|
note = "`StructOpt::from_iter` is replaced with `Parser::parse_from` (note the change in derives)"
|
|
)]
|
|
fn from_iter<I, T>(itr: I) -> Self
|
|
where
|
|
I: IntoIterator<Item = T>,
|
|
T: Into<OsString> + Clone,
|
|
{
|
|
Self::parse_from(itr)
|
|
}
|
|
|
|
/// Deprecated, `StructOpt::from_iter_safe` replaced with `Parser::try_parse_from` (note the
|
|
/// change in derives)
|
|
#[deprecated(
|
|
since = "3.0.0",
|
|
note = "`StructOpt::from_iter_safe` is replaced with `Parser::try_parse_from` (note the change in derives)"
|
|
)]
|
|
fn from_iter_safe<I, T>(itr: I) -> Result<Self, Error>
|
|
where
|
|
I: IntoIterator<Item = T>,
|
|
T: Into<OsString> + Clone,
|
|
{
|
|
Self::try_parse_from(itr)
|
|
}
|
|
}
|
|
|
|
/// Create an [`App`] relevant for a user-defined container.
|
|
///
|
|
/// Derived as part of [`Parser`].
|
|
pub trait IntoApp: Sized {
|
|
/// Build an [`App`] that can instantiate `Self`.
|
|
///
|
|
/// See [`FromArgMatches::from_arg_matches`] for instantiating `Self`.
|
|
fn into_app<'help>() -> App<'help>;
|
|
/// Build an [`App`] that can update `self`.
|
|
///
|
|
/// See [`FromArgMatches::update_from_arg_matches`] for updating `self`.
|
|
fn into_app_for_update<'help>() -> App<'help>;
|
|
}
|
|
|
|
/// Converts an instance of [`ArgMatches`] to a user-defined container.
|
|
///
|
|
/// Derived as part of [`Parser`], [`Args`], and [`Subcommand`].
|
|
pub trait FromArgMatches: Sized {
|
|
/// Instantiate `Self` from [`ArgMatches`], parsing the arguments as needed.
|
|
///
|
|
/// Motivation: If our application had two CLI options, `--name
|
|
/// <STRING>` and the flag `--debug`, we may create a struct as follows:
|
|
///
|
|
#[cfg_attr(not(feature = "derive"), doc = " ```ignore")]
|
|
#[cfg_attr(feature = "derive", doc = " ```no_run")]
|
|
/// struct Context {
|
|
/// name: String,
|
|
/// debug: bool
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// We then need to convert the `ArgMatches` that `clap` generated into our struct.
|
|
/// `from_arg_matches` serves as the equivalent of:
|
|
///
|
|
#[cfg_attr(not(feature = "derive"), doc = " ```ignore")]
|
|
#[cfg_attr(feature = "derive", doc = " ```no_run")]
|
|
/// # use clap::ArgMatches;
|
|
/// # struct Context {
|
|
/// # name: String,
|
|
/// # debug: bool
|
|
/// # }
|
|
/// impl From<ArgMatches> for Context {
|
|
/// fn from(m: ArgMatches) -> Self {
|
|
/// Context {
|
|
/// name: m.value_of("name").unwrap().to_string(),
|
|
/// debug: m.is_present("debug"),
|
|
/// }
|
|
/// }
|
|
/// }
|
|
/// ```
|
|
fn from_arg_matches(matches: &ArgMatches) -> Result<Self, Error>;
|
|
|
|
/// Assign values from `ArgMatches` to `self`.
|
|
fn update_from_arg_matches(&mut self, matches: &ArgMatches) -> Result<(), Error>;
|
|
}
|
|
|
|
/// Parse a set of arguments into a user-defined container.
|
|
///
|
|
/// Implementing this trait lets a parent container delegate argument parsing behavior to `Self`.
|
|
/// with:
|
|
/// - `#[clap(flatten)] args: ChildArgs`: Attribute can only be used with struct fields that impl
|
|
/// `Args`.
|
|
/// - `Variant(ChildArgs)`: No attribute is used with enum variants that impl `Args`.
|
|
///
|
|
/// See the
|
|
/// [derive reference](https://github.com/clap-rs/clap/blob/v3.0.8/examples/derive_ref/README.md)
|
|
/// for attributes and best practices.
|
|
///
|
|
/// **NOTE:** Deriving requires the `derive` feature flag
|
|
///
|
|
/// # Example
|
|
///
|
|
#[cfg_attr(not(feature = "derive"), doc = " ```ignore")]
|
|
#[cfg_attr(feature = "derive", doc = " ```")]
|
|
/// #[derive(clap::Parser)]
|
|
/// struct Args {
|
|
/// #[clap(flatten)]
|
|
/// logging: LogArgs,
|
|
/// }
|
|
///
|
|
/// #[derive(clap::Args)]
|
|
/// struct LogArgs {
|
|
/// #[clap(long, short = 'v', parse(from_occurrences))]
|
|
/// verbose: i8,
|
|
/// }
|
|
/// ```
|
|
pub trait Args: FromArgMatches + Sized {
|
|
/// Append to [`App`] so it can instantiate `Self`.
|
|
///
|
|
/// See also [`IntoApp`].
|
|
fn augment_args(app: App<'_>) -> App<'_>;
|
|
/// Append to [`App`] so it can update `self`.
|
|
///
|
|
/// This is used to implement `#[clap(flatten)]`
|
|
///
|
|
/// See also [`IntoApp`].
|
|
fn augment_args_for_update(app: App<'_>) -> App<'_>;
|
|
}
|
|
|
|
/// Parse a sub-command into a user-defined enum.
|
|
///
|
|
/// Implementing this trait lets a parent container delegate subcommand behavior to `Self`.
|
|
/// with:
|
|
/// - `#[clap(subcommand)] field: SubCmd`: Attribute can be used with either struct fields or enum
|
|
/// variants that impl `Subcommand`.
|
|
/// - `#[clap(flatten)] Variant(SubCmd)`: Attribute can only be used with enum variants that impl
|
|
/// `Subcommand`.
|
|
///
|
|
/// See the
|
|
/// [derive reference](https://github.com/clap-rs/clap/blob/v3.0.8/examples/derive_ref/README.md)
|
|
/// for attributes and best practices.
|
|
///
|
|
/// **NOTE:** Deriving requires the `derive` feature flag
|
|
///
|
|
/// # Example
|
|
///
|
|
#[cfg_attr(not(feature = "derive"), doc = " ```ignore")]
|
|
#[cfg_attr(feature = "derive", doc = " ```")]
|
|
/// #[derive(clap::Parser)]
|
|
/// struct Args {
|
|
/// #[clap(subcommand)]
|
|
/// action: Action,
|
|
/// }
|
|
///
|
|
/// #[derive(clap::Subcommand)]
|
|
/// enum Action {
|
|
/// Add,
|
|
/// Remove,
|
|
/// }
|
|
/// ```
|
|
pub trait Subcommand: FromArgMatches + Sized {
|
|
/// Append to [`App`] so it can instantiate `Self`.
|
|
///
|
|
/// See also [`IntoApp`].
|
|
fn augment_subcommands(app: App<'_>) -> App<'_>;
|
|
/// Append to [`App`] so it can update `self`.
|
|
///
|
|
/// This is used to implement `#[clap(flatten)]`
|
|
///
|
|
/// See also [`IntoApp`].
|
|
fn augment_subcommands_for_update(app: App<'_>) -> App<'_>;
|
|
/// Test whether `Self` can parse a specific subcommand
|
|
fn has_subcommand(name: &str) -> bool;
|
|
}
|
|
|
|
/// Parse arguments into enums.
|
|
///
|
|
/// When deriving [`Parser`], a field whose type implements `ArgEnum` can have the attribute
|
|
/// `#[clap(arg_enum)]` which will
|
|
/// - Call [`Arg::possible_values`][crate::Arg::possible_values]
|
|
/// - Allowing using the `#[clap(default_value_t)]` attribute without implementing `Display`.
|
|
///
|
|
/// See the
|
|
/// [derive reference](https://github.com/clap-rs/clap/blob/v3.0.8/examples/derive_ref/README.md)
|
|
/// for attributes and best practices.
|
|
///
|
|
/// **NOTE:** Deriving requires the `derive` feature flag
|
|
///
|
|
/// # Example
|
|
///
|
|
#[cfg_attr(not(feature = "derive"), doc = " ```ignore")]
|
|
#[cfg_attr(feature = "derive", doc = " ```")]
|
|
/// #[derive(clap::Parser)]
|
|
/// struct Args {
|
|
/// #[clap(arg_enum)]
|
|
/// level: Level,
|
|
/// }
|
|
///
|
|
/// #[derive(clap::ArgEnum, Clone)]
|
|
/// enum Level {
|
|
/// Debug,
|
|
/// Info,
|
|
/// Warning,
|
|
/// Error,
|
|
/// }
|
|
/// ```
|
|
pub trait ArgEnum: Sized + Clone {
|
|
/// All possible argument values, in display order.
|
|
fn value_variants<'a>() -> &'a [Self];
|
|
|
|
/// Parse an argument into `Self`.
|
|
fn from_str(input: &str, ignore_case: bool) -> Result<Self, String> {
|
|
Self::value_variants()
|
|
.iter()
|
|
.find(|v| {
|
|
v.to_possible_value()
|
|
.expect("ArgEnum::value_variants contains only values with a corresponding ArgEnum::to_possible_value")
|
|
.matches(input, ignore_case)
|
|
})
|
|
.cloned()
|
|
.ok_or_else(|| format!("Invalid variant: {}", input))
|
|
}
|
|
|
|
/// The canonical argument value.
|
|
///
|
|
/// The value is `None` for skipped variants.
|
|
fn to_possible_value<'a>(&self) -> Option<PossibleValue<'a>>;
|
|
}
|
|
|
|
impl<T: Parser> Parser for Box<T> {
|
|
fn parse() -> Self {
|
|
Box::new(<T as Parser>::parse())
|
|
}
|
|
|
|
fn try_parse() -> Result<Self, Error> {
|
|
<T as Parser>::try_parse().map(Box::new)
|
|
}
|
|
|
|
fn parse_from<I, It>(itr: I) -> Self
|
|
where
|
|
I: IntoIterator<Item = It>,
|
|
It: Into<OsString> + Clone,
|
|
{
|
|
Box::new(<T as Parser>::parse_from(itr))
|
|
}
|
|
|
|
fn try_parse_from<I, It>(itr: I) -> Result<Self, Error>
|
|
where
|
|
I: IntoIterator<Item = It>,
|
|
It: Into<OsString> + Clone,
|
|
{
|
|
<T as Parser>::try_parse_from(itr).map(Box::new)
|
|
}
|
|
}
|
|
|
|
impl<T: IntoApp> IntoApp for Box<T> {
|
|
fn into_app<'help>() -> App<'help> {
|
|
<T as IntoApp>::into_app()
|
|
}
|
|
fn into_app_for_update<'help>() -> App<'help> {
|
|
<T as IntoApp>::into_app_for_update()
|
|
}
|
|
}
|
|
|
|
impl<T: FromArgMatches> FromArgMatches for Box<T> {
|
|
fn from_arg_matches(matches: &ArgMatches) -> Result<Self, Error> {
|
|
<T as FromArgMatches>::from_arg_matches(matches).map(Box::new)
|
|
}
|
|
fn update_from_arg_matches(&mut self, matches: &ArgMatches) -> Result<(), Error> {
|
|
<T as FromArgMatches>::update_from_arg_matches(self, matches)
|
|
}
|
|
}
|
|
|
|
impl<T: Args> Args for Box<T> {
|
|
fn augment_args(app: App<'_>) -> App<'_> {
|
|
<T as Args>::augment_args(app)
|
|
}
|
|
fn augment_args_for_update(app: App<'_>) -> App<'_> {
|
|
<T as Args>::augment_args_for_update(app)
|
|
}
|
|
}
|
|
|
|
impl<T: Subcommand> Subcommand for Box<T> {
|
|
fn augment_subcommands(app: App<'_>) -> App<'_> {
|
|
<T as Subcommand>::augment_subcommands(app)
|
|
}
|
|
fn augment_subcommands_for_update(app: App<'_>) -> App<'_> {
|
|
<T as Subcommand>::augment_subcommands_for_update(app)
|
|
}
|
|
fn has_subcommand(name: &str) -> bool {
|
|
<T as Subcommand>::has_subcommand(name)
|
|
}
|
|
}
|
|
|
|
fn format_error<I: IntoApp>(err: crate::Error) -> crate::Error {
|
|
let mut app = I::into_app();
|
|
err.format(&mut app)
|
|
}
|