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 /// The primary one-stop-shop trait used to create an instance of a `clap`
9 /// [`App`], conduct the parsing, and turn the resulting [`ArgMatches`] back
10 /// into concrete instance of the user struct.
11 ///
12 /// This trait is primarily a convenience on top of [`FromArgMatches`] +
13 /// [`IntoApp`] which uses those two underlying traits to build the two
14 /// fundamental functions `parse` which uses the `std::env::args_os` iterator,
15 /// and `parse_from` which allows the consumer to supply the iterator (along
16 /// with fallible options for each).
17 ///
18 /// # Examples
19 ///
20 /// The following example creates a `Context` struct that would be used
21 /// throughout the application representing the normalized values coming from
22 /// the CLI.
23 ///
24 /// ```rust
25 /// # use clap::{Clap};
26 /// /// My super CLI
27 /// #[derive(Clap)]
28 /// #[clap(name = "demo")]
29 /// struct Context {
30 ///     /// More verbose output
31 ///     #[clap(long)]
32 ///     verbose: bool,
33 ///     /// An optional name
34 ///     #[clap(short, long)]
35 ///     name: Option<String>,
36 /// }
37 /// ```
38 ///
39 /// The equivilant [`App`] struct + `From` implementation:
40 ///
41 /// ```rust
42 /// # use clap::{App, Arg, ArgMatches};
43 /// App::new("demo")
44 ///     .about("My super CLI")
45 ///     .arg(Arg::new("verbose")
46 ///         .long("verbose")
47 ///         .about("More verbose output"))
48 ///     .arg(Arg::new("name")
49 ///         .long("name")
50 ///         .long("n")
51 ///         .about("An optional name")
52 ///         .takes_value(true));
53 ///
54 /// struct Context {
55 ///     verbose: bool,
56 ///     name: Option<String>,
57 /// }
58 ///
59 /// impl From<ArgMatches> for Context {
60 ///     fn from(m: ArgMatches) -> Self {
61 ///         Context {
62 ///             verbose: m.is_present("verbose"),
63 ///             name: m.value_of("name").map(|n| n.to_owned()),
64 ///         }
65 ///     }
66 /// }
67 /// ```
68 ///
69 /// [`App`]: ./struct.App.html
70 /// [`ArgMatches`]: ./struct.ArgMatches.html
71 /// [`FromArgMatches`]: ./trait.FromArgMatches.html
72 /// [`IntoApp`]: ./trait.IntoApp.html
73 pub trait Clap: FromArgMatches + IntoApp + Sized {
74     /// Parse from `std::env::args_os()`, exit on error
parse() -> Self75     fn parse() -> Self {
76         let matches = <Self as IntoApp>::into_app().get_matches();
77         <Self as FromArgMatches>::from_arg_matches(&matches)
78     }
79 
80     /// Parse from `std::env::args_os()`, return Err on error.
try_parse() -> Result<Self, 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     }
85 
86     /// Parse from iterator, exit on error
parse_from<I, T>(itr: I) -> Self where I: IntoIterator<Item = T>, T: Into<OsString> + Clone,87     fn parse_from<I, T>(itr: I) -> Self
88     where
89         I: IntoIterator<Item = T>,
90         // TODO (@CreepySkeleton): discover a way to avoid cloning here
91         T: Into<OsString> + Clone,
92     {
93         let matches = <Self as IntoApp>::into_app().get_matches_from(itr);
94         <Self as FromArgMatches>::from_arg_matches(&matches)
95     }
96 
97     /// Parse from iterator, return Err on error.
try_parse_from<I, T>(itr: I) -> Result<Self, Error> where I: IntoIterator<Item = T>, T: Into<OsString> + Clone,98     fn try_parse_from<I, T>(itr: I) -> Result<Self, Error>
99     where
100         I: IntoIterator<Item = T>,
101         // TODO (@CreepySkeleton): discover a way to avoid cloning here
102         T: Into<OsString> + Clone,
103     {
104         let matches = <Self as IntoApp>::into_app().try_get_matches_from(itr)?;
105         Ok(<Self as FromArgMatches>::from_arg_matches(&matches))
106     }
107 }
108 
109 /// Build an App according to the struct
110 ///
111 /// Also serves for flattening
112 pub trait IntoApp: Sized {
113     /// @TODO @release @docs
into_app<'help>() -> App<'help>114     fn into_app<'help>() -> App<'help>;
115     /// @TODO @release @docs
augment_clap(app: App<'_>) -> App<'_>116     fn augment_clap(app: App<'_>) -> App<'_>;
117 }
118 
119 /// Converts an instance of [`ArgMatches`] to a consumer defined struct.
120 ///
121 /// [`ArgMatches`]: ./struct.ArgMatches.html
122 pub trait FromArgMatches: Sized {
123     /// It's common to have an "application context" struct (sometimes called
124     /// config) that represents all the normalized values after being processed by
125     /// the CLI.
126     ///
127     /// For instance, if an application we made had two CLI options, `--name
128     /// <STRING>` and a flag `--debug` to distinguish "debugging mode" for our made
129     /// up CLI, we may create a context struct as follows:
130     ///
131     /// ```no_run
132     /// struct Context {
133     ///     name: String,
134     ///     debug: bool
135     /// }
136     /// ```
137     ///
138     /// And after letting `clap` parse the CLI, we get back and instance of
139     /// `ArgMatches`, we may create a `From` implementation like so:
140     ///
141     /// ```no_run
142     /// # use clap::ArgMatches;
143     /// # struct Context {
144     /// #   name: String,
145     /// #   debug: bool
146     /// # }
147     /// impl From<ArgMatches> for Context {
148     ///    fn from(m: ArgMatches) -> Self {
149     ///        Context {
150     ///            name: m.value_of("name").unwrap().to_string(),
151     ///            debug: m.is_present("debug"),
152     ///        }
153     ///    }
154     /// }
155     /// ```
from_arg_matches(matches: &ArgMatches) -> Self156     fn from_arg_matches(matches: &ArgMatches) -> Self;
157 }
158 
159 /// @TODO @release @docs
160 pub trait Subcommand: Sized {
161     /// @TODO @release @docs
from_subcommand(subcommand: Option<(&str, &ArgMatches)>) -> Option<Self>162     fn from_subcommand(subcommand: Option<(&str, &ArgMatches)>) -> Option<Self>;
163     /// @TODO @release @docs
augment_subcommands(app: App<'_>) -> App<'_>164     fn augment_subcommands(app: App<'_>) -> App<'_>;
165 }
166 
167 /// @TODO @release @docs
168 pub trait ArgEnum: Sized {
169     /// @TODO @release @docs
170     const VARIANTS: &'static [&'static str];
171 
172     /// @TODO @release @docs
from_str(input: &str, case_insensitive: bool) -> Result<Self, String>173     fn from_str(input: &str, case_insensitive: bool) -> Result<Self, String>;
174 }
175 
176 impl<T: Clap> Clap for Box<T> {
parse() -> Self177     fn parse() -> Self {
178         Box::new(<T as Clap>::parse())
179     }
180 
try_parse() -> Result<Self, Error>181     fn try_parse() -> Result<Self, Error> {
182         <T as Clap>::try_parse().map(Box::new)
183     }
184 
parse_from<I, It>(itr: I) -> Self where I: IntoIterator<Item = It>, It: Into<OsString> + Clone,185     fn parse_from<I, It>(itr: I) -> Self
186     where
187         I: IntoIterator<Item = It>,
188         // TODO (@CreepySkeleton): discover a way to avoid cloning here
189         It: Into<OsString> + Clone,
190     {
191         Box::new(<T as Clap>::parse_from(itr))
192     }
193 
try_parse_from<I, It>(itr: I) -> Result<Self, Error> where I: IntoIterator<Item = It>, It: Into<OsString> + Clone,194     fn try_parse_from<I, It>(itr: I) -> Result<Self, Error>
195     where
196         I: IntoIterator<Item = It>,
197         // TODO (@CreepySkeleton): discover a way to avoid cloning here
198         It: Into<OsString> + Clone,
199     {
200         <T as Clap>::try_parse_from(itr).map(Box::new)
201     }
202 }
203 
204 impl<T: IntoApp> IntoApp for Box<T> {
into_app<'help>() -> App<'help>205     fn into_app<'help>() -> App<'help> {
206         <T as IntoApp>::into_app()
207     }
augment_clap(app: App<'_>) -> App<'_>208     fn augment_clap(app: App<'_>) -> App<'_> {
209         <T as IntoApp>::augment_clap(app)
210     }
211 }
212 
213 impl<T: FromArgMatches> FromArgMatches for Box<T> {
from_arg_matches(matches: &ArgMatches) -> Self214     fn from_arg_matches(matches: &ArgMatches) -> Self {
215         Box::new(<T as FromArgMatches>::from_arg_matches(matches))
216     }
217 }
218 
219 impl<T: Subcommand> Subcommand for Box<T> {
from_subcommand(subcommand: Option<(&str, &ArgMatches)>) -> Option<Self>220     fn from_subcommand(subcommand: Option<(&str, &ArgMatches)>) -> Option<Self> {
221         <T as Subcommand>::from_subcommand(subcommand).map(Box::new)
222     }
augment_subcommands(app: App<'_>) -> App<'_>223     fn augment_subcommands(app: App<'_>) -> App<'_> {
224         <T as Subcommand>::augment_subcommands(app)
225     }
226 }
227