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