]>
Commit | Line | Data |
---|---|---|
8bb4bdeb | 1 | // Std |
a2a8927a XL |
2 | use std::{ |
3 | convert::From, | |
4 | error::Error as StdError, | |
5 | fmt as std_fmt, | |
6 | fmt::Display, | |
7 | io::{self, Write}, | |
8 | process, | |
9 | result::Result as StdResult, | |
10 | }; | |
8bb4bdeb XL |
11 | |
12 | // Internal | |
a2a8927a XL |
13 | use crate::{ |
14 | args::AnyArg, | |
15 | fmt::{ColorWhen, Colorizer, ColorizerOption}, | |
16 | suggestions, | |
17 | }; | |
8bb4bdeb XL |
18 | |
19 | /// Short hand for [`Result`] type | |
0531ce1d | 20 | /// |
8bb4bdeb XL |
21 | /// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html |
22 | pub type Result<T> = StdResult<T, Error>; | |
23 | ||
24 | /// Command line argument parser kind of error | |
25 | #[derive(Debug, Copy, Clone, PartialEq)] | |
26 | pub enum ErrorKind { | |
27 | /// Occurs when an [`Arg`] has a set of possible values, | |
28 | /// and the user provides a value which isn't in that set. | |
29 | /// | |
30 | /// # Examples | |
31 | /// | |
32 | /// ```rust | |
33 | /// # use clap::{App, Arg, ErrorKind}; | |
041b39d2 | 34 | /// let result = App::new("prog") |
8bb4bdeb XL |
35 | /// .arg(Arg::with_name("speed") |
36 | /// .possible_value("fast") | |
37 | /// .possible_value("slow")) | |
041b39d2 | 38 | /// .get_matches_from_safe(vec!["prog", "other"]); |
8bb4bdeb XL |
39 | /// assert!(result.is_err()); |
40 | /// assert_eq!(result.unwrap_err().kind, ErrorKind::InvalidValue); | |
41 | /// ``` | |
42 | /// [`Arg`]: ./struct.Arg.html | |
43 | InvalidValue, | |
44 | ||
45 | /// Occurs when a user provides a flag, option, argument or subcommand which isn't defined. | |
46 | /// | |
47 | /// # Examples | |
48 | /// | |
49 | /// ```rust | |
50 | /// # use clap::{App, Arg, ErrorKind}; | |
041b39d2 | 51 | /// let result = App::new("prog") |
8bb4bdeb | 52 | /// .arg(Arg::from_usage("--flag 'some flag'")) |
041b39d2 | 53 | /// .get_matches_from_safe(vec!["prog", "--other"]); |
8bb4bdeb XL |
54 | /// assert!(result.is_err()); |
55 | /// assert_eq!(result.unwrap_err().kind, ErrorKind::UnknownArgument); | |
56 | /// ``` | |
57 | UnknownArgument, | |
58 | ||
59 | /// Occurs when the user provides an unrecognized [`SubCommand`] which meets the threshold for | |
60 | /// being similar enough to an existing subcommand. | |
61 | /// If it doesn't meet the threshold, or the 'suggestions' feature is disabled, | |
62 | /// the more general [`UnknownArgument`] error is returned. | |
63 | /// | |
64 | /// # Examples | |
65 | /// | |
ff7c6d11 XL |
66 | #[cfg_attr(not(feature = "suggestions"), doc = " ```no_run")] |
67 | #[cfg_attr(feature = "suggestions", doc = " ```")] | |
8bb4bdeb | 68 | /// # use clap::{App, Arg, ErrorKind, SubCommand}; |
041b39d2 | 69 | /// let result = App::new("prog") |
8bb4bdeb XL |
70 | /// .subcommand(SubCommand::with_name("config") |
71 | /// .about("Used for configuration") | |
72 | /// .arg(Arg::with_name("config_file") | |
73 | /// .help("The configuration file to use") | |
74 | /// .index(1))) | |
041b39d2 | 75 | /// .get_matches_from_safe(vec!["prog", "confi"]); |
8bb4bdeb XL |
76 | /// assert!(result.is_err()); |
77 | /// assert_eq!(result.unwrap_err().kind, ErrorKind::InvalidSubcommand); | |
78 | /// ``` | |
79 | /// [`SubCommand`]: ./struct.SubCommand.html | |
80 | /// [`UnknownArgument`]: ./enum.ErrorKind.html#variant.UnknownArgument | |
81 | InvalidSubcommand, | |
82 | ||
83 | /// Occurs when the user provides an unrecognized [`SubCommand`] which either | |
84 | /// doesn't meet the threshold for being similar enough to an existing subcommand, | |
0531ce1d | 85 | /// or the 'suggestions' feature is disabled. |
8bb4bdeb XL |
86 | /// Otherwise the more detailed [`InvalidSubcommand`] error is returned. |
87 | /// | |
88 | /// This error typically happens when passing additional subcommand names to the `help` | |
89 | /// subcommand. Otherwise, the more general [`UnknownArgument`] error is used. | |
90 | /// | |
91 | /// # Examples | |
92 | /// | |
93 | /// ```rust | |
94 | /// # use clap::{App, Arg, ErrorKind, SubCommand}; | |
041b39d2 | 95 | /// let result = App::new("prog") |
8bb4bdeb XL |
96 | /// .subcommand(SubCommand::with_name("config") |
97 | /// .about("Used for configuration") | |
98 | /// .arg(Arg::with_name("config_file") | |
99 | /// .help("The configuration file to use") | |
100 | /// .index(1))) | |
041b39d2 | 101 | /// .get_matches_from_safe(vec!["prog", "help", "nothing"]); |
8bb4bdeb XL |
102 | /// assert!(result.is_err()); |
103 | /// assert_eq!(result.unwrap_err().kind, ErrorKind::UnrecognizedSubcommand); | |
104 | /// ``` | |
105 | /// [`SubCommand`]: ./struct.SubCommand.html | |
106 | /// [`InvalidSubcommand`]: ./enum.ErrorKind.html#variant.InvalidSubcommand | |
107 | /// [`UnknownArgument`]: ./enum.ErrorKind.html#variant.UnknownArgument | |
108 | UnrecognizedSubcommand, | |
109 | ||
110 | /// Occurs when the user provides an empty value for an option that does not allow empty | |
111 | /// values. | |
112 | /// | |
113 | /// # Examples | |
114 | /// | |
115 | /// ```rust | |
116 | /// # use clap::{App, Arg, ErrorKind}; | |
041b39d2 | 117 | /// let res = App::new("prog") |
8bb4bdeb XL |
118 | /// .arg(Arg::with_name("color") |
119 | /// .long("color") | |
120 | /// .empty_values(false)) | |
041b39d2 | 121 | /// .get_matches_from_safe(vec!["prog", "--color="]); |
8bb4bdeb XL |
122 | /// assert!(res.is_err()); |
123 | /// assert_eq!(res.unwrap_err().kind, ErrorKind::EmptyValue); | |
124 | /// ``` | |
125 | EmptyValue, | |
126 | ||
127 | /// Occurs when the user provides a value for an argument with a custom validation and the | |
128 | /// value fails that validation. | |
129 | /// | |
130 | /// # Examples | |
131 | /// | |
132 | /// ```rust | |
133 | /// # use clap::{App, Arg, ErrorKind}; | |
134 | /// fn is_numeric(val: String) -> Result<(), String> { | |
135 | /// match val.parse::<i64>() { | |
136 | /// Ok(..) => Ok(()), | |
137 | /// Err(..) => Err(String::from("Value wasn't a number!")), | |
138 | /// } | |
139 | /// } | |
140 | /// | |
041b39d2 | 141 | /// let result = App::new("prog") |
8bb4bdeb XL |
142 | /// .arg(Arg::with_name("num") |
143 | /// .validator(is_numeric)) | |
041b39d2 | 144 | /// .get_matches_from_safe(vec!["prog", "NotANumber"]); |
8bb4bdeb XL |
145 | /// assert!(result.is_err()); |
146 | /// assert_eq!(result.unwrap_err().kind, ErrorKind::ValueValidation); | |
147 | /// ``` | |
148 | ValueValidation, | |
149 | ||
150 | /// Occurs when a user provides more values for an argument than were defined by setting | |
151 | /// [`Arg::max_values`]. | |
152 | /// | |
153 | /// # Examples | |
154 | /// | |
155 | /// ```rust | |
156 | /// # use clap::{App, Arg, ErrorKind}; | |
041b39d2 | 157 | /// let result = App::new("prog") |
8bb4bdeb XL |
158 | /// .arg(Arg::with_name("arg") |
159 | /// .multiple(true) | |
160 | /// .max_values(2)) | |
041b39d2 | 161 | /// .get_matches_from_safe(vec!["prog", "too", "many", "values"]); |
8bb4bdeb XL |
162 | /// assert!(result.is_err()); |
163 | /// assert_eq!(result.unwrap_err().kind, ErrorKind::TooManyValues); | |
164 | /// ``` | |
165 | /// [`Arg::max_values`]: ./struct.Arg.html#method.max_values | |
166 | TooManyValues, | |
167 | ||
168 | /// Occurs when the user provides fewer values for an argument than were defined by setting | |
169 | /// [`Arg::min_values`]. | |
170 | /// | |
171 | /// # Examples | |
172 | /// | |
173 | /// ```rust | |
174 | /// # use clap::{App, Arg, ErrorKind}; | |
041b39d2 | 175 | /// let result = App::new("prog") |
8bb4bdeb XL |
176 | /// .arg(Arg::with_name("some_opt") |
177 | /// .long("opt") | |
178 | /// .min_values(3)) | |
041b39d2 | 179 | /// .get_matches_from_safe(vec!["prog", "--opt", "too", "few"]); |
8bb4bdeb XL |
180 | /// assert!(result.is_err()); |
181 | /// assert_eq!(result.unwrap_err().kind, ErrorKind::TooFewValues); | |
182 | /// ``` | |
183 | /// [`Arg::min_values`]: ./struct.Arg.html#method.min_values | |
184 | TooFewValues, | |
185 | ||
186 | /// Occurs when the user provides a different number of values for an argument than what's | |
187 | /// been defined by setting [`Arg::number_of_values`] or than was implicitly set by | |
188 | /// [`Arg::value_names`]. | |
189 | /// | |
190 | /// # Examples | |
191 | /// | |
192 | /// ```rust | |
193 | /// # use clap::{App, Arg, ErrorKind}; | |
041b39d2 | 194 | /// let result = App::new("prog") |
8bb4bdeb XL |
195 | /// .arg(Arg::with_name("some_opt") |
196 | /// .long("opt") | |
197 | /// .takes_value(true) | |
198 | /// .number_of_values(2)) | |
041b39d2 | 199 | /// .get_matches_from_safe(vec!["prog", "--opt", "wrong"]); |
8bb4bdeb XL |
200 | /// assert!(result.is_err()); |
201 | /// assert_eq!(result.unwrap_err().kind, ErrorKind::WrongNumberOfValues); | |
202 | /// ``` | |
e1599b0c | 203 | /// |
8bb4bdeb XL |
204 | /// [`Arg::number_of_values`]: ./struct.Arg.html#method.number_of_values |
205 | /// [`Arg::value_names`]: ./struct.Arg.html#method.value_names | |
206 | WrongNumberOfValues, | |
207 | ||
208 | /// Occurs when the user provides two values which conflict with each other and can't be used | |
209 | /// together. | |
210 | /// | |
211 | /// # Examples | |
212 | /// | |
213 | /// ```rust | |
214 | /// # use clap::{App, Arg, ErrorKind}; | |
041b39d2 | 215 | /// let result = App::new("prog") |
8bb4bdeb XL |
216 | /// .arg(Arg::with_name("debug") |
217 | /// .long("debug") | |
218 | /// .conflicts_with("color")) | |
219 | /// .arg(Arg::with_name("color") | |
220 | /// .long("color")) | |
041b39d2 | 221 | /// .get_matches_from_safe(vec!["prog", "--debug", "--color"]); |
8bb4bdeb XL |
222 | /// assert!(result.is_err()); |
223 | /// assert_eq!(result.unwrap_err().kind, ErrorKind::ArgumentConflict); | |
224 | /// ``` | |
225 | ArgumentConflict, | |
226 | ||
227 | /// Occurs when the user does not provide one or more required arguments. | |
228 | /// | |
229 | /// # Examples | |
230 | /// | |
231 | /// ```rust | |
232 | /// # use clap::{App, Arg, ErrorKind}; | |
041b39d2 | 233 | /// let result = App::new("prog") |
8bb4bdeb XL |
234 | /// .arg(Arg::with_name("debug") |
235 | /// .required(true)) | |
041b39d2 | 236 | /// .get_matches_from_safe(vec!["prog"]); |
8bb4bdeb XL |
237 | /// assert!(result.is_err()); |
238 | /// assert_eq!(result.unwrap_err().kind, ErrorKind::MissingRequiredArgument); | |
239 | /// ``` | |
240 | MissingRequiredArgument, | |
241 | ||
242 | /// Occurs when a subcommand is required (as defined by [`AppSettings::SubcommandRequired`]), | |
243 | /// but the user does not provide one. | |
244 | /// | |
245 | /// # Examples | |
246 | /// | |
247 | /// ```rust | |
248 | /// # use clap::{App, AppSettings, SubCommand, ErrorKind}; | |
041b39d2 | 249 | /// let err = App::new("prog") |
8bb4bdeb XL |
250 | /// .setting(AppSettings::SubcommandRequired) |
251 | /// .subcommand(SubCommand::with_name("test")) | |
252 | /// .get_matches_from_safe(vec![ | |
253 | /// "myprog", | |
254 | /// ]); | |
255 | /// assert!(err.is_err()); | |
256 | /// assert_eq!(err.unwrap_err().kind, ErrorKind::MissingSubcommand); | |
257 | /// # ; | |
258 | /// ``` | |
259 | /// [`AppSettings::SubcommandRequired`]: ./enum.AppSettings.html#variant.SubcommandRequired | |
260 | MissingSubcommand, | |
261 | ||
262 | /// Occurs when either an argument or [`SubCommand`] is required, as defined by | |
263 | /// [`AppSettings::ArgRequiredElseHelp`], but the user did not provide one. | |
264 | /// | |
265 | /// # Examples | |
266 | /// | |
267 | /// ```rust | |
268 | /// # use clap::{App, Arg, AppSettings, ErrorKind, SubCommand}; | |
041b39d2 | 269 | /// let result = App::new("prog") |
8bb4bdeb XL |
270 | /// .setting(AppSettings::ArgRequiredElseHelp) |
271 | /// .subcommand(SubCommand::with_name("config") | |
272 | /// .about("Used for configuration") | |
273 | /// .arg(Arg::with_name("config_file") | |
274 | /// .help("The configuration file to use"))) | |
041b39d2 | 275 | /// .get_matches_from_safe(vec!["prog"]); |
8bb4bdeb XL |
276 | /// assert!(result.is_err()); |
277 | /// assert_eq!(result.unwrap_err().kind, ErrorKind::MissingArgumentOrSubcommand); | |
278 | /// ``` | |
279 | /// [`SubCommand`]: ./struct.SubCommand.html | |
280 | /// [`AppSettings::ArgRequiredElseHelp`]: ./enum.AppSettings.html#variant.ArgRequiredElseHelp | |
281 | MissingArgumentOrSubcommand, | |
282 | ||
283 | /// Occurs when the user provides multiple values to an argument which doesn't allow that. | |
284 | /// | |
285 | /// # Examples | |
286 | /// | |
287 | /// ```rust | |
288 | /// # use clap::{App, Arg, ErrorKind}; | |
041b39d2 | 289 | /// let result = App::new("prog") |
8bb4bdeb XL |
290 | /// .arg(Arg::with_name("debug") |
291 | /// .long("debug") | |
292 | /// .multiple(false)) | |
041b39d2 | 293 | /// .get_matches_from_safe(vec!["prog", "--debug", "--debug"]); |
8bb4bdeb XL |
294 | /// assert!(result.is_err()); |
295 | /// assert_eq!(result.unwrap_err().kind, ErrorKind::UnexpectedMultipleUsage); | |
296 | /// ``` | |
297 | UnexpectedMultipleUsage, | |
298 | ||
299 | /// Occurs when the user provides a value containing invalid UTF-8 for an argument and | |
300 | /// [`AppSettings::StrictUtf8`] is set. | |
301 | /// | |
0531ce1d | 302 | /// # Platform Specific |
8bb4bdeb | 303 | /// |
8faf50e0 | 304 | /// Non-Windows platforms only (such as Linux, Unix, macOS, etc.) |
8bb4bdeb XL |
305 | /// |
306 | /// # Examples | |
307 | /// | |
ff7c6d11 XL |
308 | #[cfg_attr(not(unix), doc = " ```ignore")] |
309 | #[cfg_attr(unix, doc = " ```")] | |
8bb4bdeb XL |
310 | /// # use clap::{App, Arg, ErrorKind, AppSettings}; |
311 | /// # use std::os::unix::ffi::OsStringExt; | |
312 | /// # use std::ffi::OsString; | |
041b39d2 | 313 | /// let result = App::new("prog") |
8bb4bdeb XL |
314 | /// .setting(AppSettings::StrictUtf8) |
315 | /// .arg(Arg::with_name("utf8") | |
316 | /// .short("u") | |
317 | /// .takes_value(true)) | |
318 | /// .get_matches_from_safe(vec![OsString::from("myprog"), | |
319 | /// OsString::from("-u"), | |
320 | /// OsString::from_vec(vec![0xE9])]); | |
321 | /// assert!(result.is_err()); | |
322 | /// assert_eq!(result.unwrap_err().kind, ErrorKind::InvalidUtf8); | |
323 | /// ``` | |
324 | /// [`AppSettings::StrictUtf8`]: ./enum.AppSettings.html#variant.StrictUtf8 | |
325 | InvalidUtf8, | |
326 | ||
327 | /// Not a true "error" as it means `--help` or similar was used. | |
328 | /// The help message will be sent to `stdout`. | |
329 | /// | |
330 | /// **Note**: If the help is displayed due to an error (such as missing subcommands) it will | |
331 | /// be sent to `stderr` instead of `stdout`. | |
332 | /// | |
333 | /// # Examples | |
334 | /// | |
335 | /// ```rust | |
336 | /// # use clap::{App, Arg, ErrorKind}; | |
041b39d2 XL |
337 | /// let result = App::new("prog") |
338 | /// .get_matches_from_safe(vec!["prog", "--help"]); | |
8bb4bdeb XL |
339 | /// assert!(result.is_err()); |
340 | /// assert_eq!(result.unwrap_err().kind, ErrorKind::HelpDisplayed); | |
341 | /// ``` | |
342 | HelpDisplayed, | |
343 | ||
344 | /// Not a true "error" as it means `--version` or similar was used. | |
345 | /// The message will be sent to `stdout`. | |
346 | /// | |
347 | /// # Examples | |
348 | /// | |
349 | /// ```rust | |
350 | /// # use clap::{App, Arg, ErrorKind}; | |
041b39d2 XL |
351 | /// let result = App::new("prog") |
352 | /// .get_matches_from_safe(vec!["prog", "--version"]); | |
8bb4bdeb XL |
353 | /// assert!(result.is_err()); |
354 | /// assert_eq!(result.unwrap_err().kind, ErrorKind::VersionDisplayed); | |
355 | /// ``` | |
356 | VersionDisplayed, | |
357 | ||
358 | /// Occurs when using the [`value_t!`] and [`values_t!`] macros to convert an argument value | |
359 | /// into type `T`, but the argument you requested wasn't used. I.e. you asked for an argument | |
360 | /// with name `config` to be converted, but `config` wasn't used by the user. | |
361 | /// [`value_t!`]: ./macro.value_t!.html | |
362 | /// [`values_t!`]: ./macro.values_t!.html | |
363 | ArgumentNotFound, | |
364 | ||
365 | /// Represents an [I/O error]. | |
366 | /// Can occur when writing to `stderr` or `stdout` or reading a configuration file. | |
367 | /// [I/O error]: https://doc.rust-lang.org/std/io/struct.Error.html | |
368 | Io, | |
369 | ||
370 | /// Represents a [Format error] (which is a part of [`Display`]). | |
371 | /// Typically caused by writing to `stderr` or `stdout`. | |
e1599b0c | 372 | /// |
8bb4bdeb XL |
373 | /// [`Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html |
374 | /// [Format error]: https://doc.rust-lang.org/std/fmt/struct.Error.html | |
375 | Format, | |
376 | } | |
377 | ||
378 | /// Command Line Argument Parser Error | |
379 | #[derive(Debug)] | |
380 | pub struct Error { | |
0531ce1d | 381 | /// Formatted error message |
8bb4bdeb XL |
382 | pub message: String, |
383 | /// The type of error | |
384 | pub kind: ErrorKind, | |
385 | /// Any additional information passed along, such as the argument name that caused the error | |
386 | pub info: Option<Vec<String>>, | |
387 | } | |
388 | ||
389 | impl Error { | |
390 | /// Should the message be written to `stdout` or not | |
391 | pub fn use_stderr(&self) -> bool { | |
a2a8927a XL |
392 | !matches!( |
393 | self.kind, | |
394 | ErrorKind::HelpDisplayed | ErrorKind::VersionDisplayed | |
395 | ) | |
8bb4bdeb XL |
396 | } |
397 | ||
a2a8927a XL |
398 | /// Prints the error message and exits. If `Error::use_stderr` evaluates to `true`, the message |
399 | /// will be written to `stderr` and exits with a status of `1`. Otherwise, `stdout` is used | |
400 | /// with a status of `0`. | |
8bb4bdeb XL |
401 | pub fn exit(&self) -> ! { |
402 | if self.use_stderr() { | |
a2a8927a | 403 | wlnerr!(@nopanic "{}", self.message); |
8bb4bdeb XL |
404 | process::exit(1); |
405 | } | |
a2a8927a XL |
406 | // We are deliberately dropping errors here. We could match on the error kind, and only |
407 | // drop things such as `std::io::ErrorKind::BrokenPipe`, however nothing is being bubbled | |
408 | // up or reported back to the caller and we will be exit'ing the process anyways. | |
409 | // Additionally, changing this API to bubble up the result would be a breaking change. | |
410 | // | |
411 | // Another approach could be to try and write to stdout, if that fails due to a broken pipe | |
412 | // then use stderr. However, that would change the semantics in what could be argued is a | |
413 | // breaking change. Simply dropping the error, can always be changed to this "use stderr if | |
414 | // stdout is closed" approach later if desired. | |
415 | // | |
416 | // A good explanation of the types of errors are SIGPIPE where the read side of the pipe | |
417 | // closes before the write side. See the README in `calm_io` for a good explanation: | |
418 | // | |
419 | // https://github.com/myrrlyn/calm_io/blob/a42845575a04cd8b65e92c19d104627f5fcad3d7/README.md | |
420 | let _ = writeln!(&mut io::stdout().lock(), "{}", self.message); | |
8bb4bdeb XL |
421 | process::exit(0); |
422 | } | |
423 | ||
424 | #[doc(hidden)] | |
3dfed10e XL |
425 | pub fn write_to<W: Write>(&self, w: &mut W) -> io::Result<()> { |
426 | write!(w, "{}", self.message) | |
427 | } | |
8bb4bdeb XL |
428 | |
429 | #[doc(hidden)] | |
8faf50e0 | 430 | pub fn argument_conflict<O, U>( |
0531ce1d | 431 | arg: &AnyArg, |
ff7c6d11 XL |
432 | other: Option<O>, |
433 | usage: U, | |
434 | color: ColorWhen, | |
435 | ) -> Self | |
436 | where | |
ff7c6d11 XL |
437 | O: Into<String>, |
438 | U: Display, | |
8bb4bdeb XL |
439 | { |
440 | let mut v = vec![arg.name().to_owned()]; | |
041b39d2 | 441 | let c = Colorizer::new(ColorizerOption { |
8bb4bdeb XL |
442 | use_stderr: true, |
443 | when: color, | |
041b39d2 | 444 | }); |
8bb4bdeb | 445 | Error { |
ff7c6d11 XL |
446 | message: format!( |
447 | "{} The argument '{}' cannot be used with {}\n\n\ | |
448 | {}\n\n\ | |
449 | For more information try {}", | |
450 | c.error("error:"), | |
451 | c.warning(&*arg.to_string()), | |
452 | match other { | |
453 | Some(name) => { | |
454 | let n = name.into(); | |
455 | v.push(n.clone()); | |
456 | c.warning(format!("'{}'", n)) | |
457 | } | |
458 | None => c.none("one or more of the other specified arguments".to_owned()), | |
459 | }, | |
460 | usage, | |
461 | c.good("--help") | |
462 | ), | |
8bb4bdeb XL |
463 | kind: ErrorKind::ArgumentConflict, |
464 | info: Some(v), | |
465 | } | |
466 | } | |
467 | ||
468 | #[doc(hidden)] | |
8faf50e0 | 469 | pub fn empty_value<U>(arg: &AnyArg, usage: U, color: ColorWhen) -> Self |
ff7c6d11 | 470 | where |
ff7c6d11 | 471 | U: Display, |
8bb4bdeb | 472 | { |
041b39d2 | 473 | let c = Colorizer::new(ColorizerOption { |
8bb4bdeb XL |
474 | use_stderr: true, |
475 | when: color, | |
041b39d2 | 476 | }); |
8bb4bdeb | 477 | Error { |
ff7c6d11 XL |
478 | message: format!( |
479 | "{} The argument '{}' requires a value but none was supplied\ | |
480 | \n\n\ | |
481 | {}\n\n\ | |
482 | For more information try {}", | |
483 | c.error("error:"), | |
484 | c.warning(arg.to_string()), | |
485 | usage, | |
486 | c.good("--help") | |
487 | ), | |
8bb4bdeb XL |
488 | kind: ErrorKind::EmptyValue, |
489 | info: Some(vec![arg.name().to_owned()]), | |
490 | } | |
491 | } | |
492 | ||
493 | #[doc(hidden)] | |
8faf50e0 | 494 | pub fn invalid_value<B, G, U>( |
ff7c6d11 XL |
495 | bad_val: B, |
496 | good_vals: &[G], | |
0531ce1d | 497 | arg: &AnyArg, |
ff7c6d11 XL |
498 | usage: U, |
499 | color: ColorWhen, | |
500 | ) -> Self | |
501 | where | |
502 | B: AsRef<str>, | |
503 | G: AsRef<str> + Display, | |
ff7c6d11 | 504 | U: Display, |
8bb4bdeb | 505 | { |
041b39d2 | 506 | let c = Colorizer::new(ColorizerOption { |
8bb4bdeb XL |
507 | use_stderr: true, |
508 | when: color, | |
041b39d2 | 509 | }); |
ff7c6d11 | 510 | let suffix = suggestions::did_you_mean_value_suffix(bad_val.as_ref(), good_vals.iter()); |
8bb4bdeb XL |
511 | |
512 | let mut sorted = vec![]; | |
513 | for v in good_vals { | |
514 | let val = format!("{}", c.good(v)); | |
515 | sorted.push(val); | |
516 | } | |
517 | sorted.sort(); | |
518 | let valid_values = sorted.join(", "); | |
519 | Error { | |
ff7c6d11 XL |
520 | message: format!( |
521 | "{} '{}' isn't a valid value for '{}'\n\t\ | |
0531ce1d | 522 | [possible values: {}]\n\ |
ff7c6d11 XL |
523 | {}\n\n\ |
524 | {}\n\n\ | |
525 | For more information try {}", | |
526 | c.error("error:"), | |
527 | c.warning(bad_val.as_ref()), | |
528 | c.warning(arg.to_string()), | |
529 | valid_values, | |
530 | suffix.0, | |
531 | usage, | |
532 | c.good("--help") | |
533 | ), | |
8bb4bdeb XL |
534 | kind: ErrorKind::InvalidValue, |
535 | info: Some(vec![arg.name().to_owned(), bad_val.as_ref().to_owned()]), | |
536 | } | |
537 | } | |
538 | ||
539 | #[doc(hidden)] | |
ff7c6d11 XL |
540 | pub fn invalid_subcommand<S, D, N, U>( |
541 | subcmd: S, | |
542 | did_you_mean: D, | |
543 | name: N, | |
544 | usage: U, | |
545 | color: ColorWhen, | |
546 | ) -> Self | |
547 | where | |
548 | S: Into<String>, | |
549 | D: AsRef<str> + Display, | |
550 | N: Display, | |
551 | U: Display, | |
8bb4bdeb XL |
552 | { |
553 | let s = subcmd.into(); | |
041b39d2 | 554 | let c = Colorizer::new(ColorizerOption { |
8bb4bdeb XL |
555 | use_stderr: true, |
556 | when: color, | |
041b39d2 | 557 | }); |
8bb4bdeb | 558 | Error { |
ff7c6d11 XL |
559 | message: format!( |
560 | "{} The subcommand '{}' wasn't recognized\n\t\ | |
561 | Did you mean '{}'?\n\n\ | |
562 | If you believe you received this message in error, try \ | |
563 | re-running with '{} {} {}'\n\n\ | |
564 | {}\n\n\ | |
565 | For more information try {}", | |
566 | c.error("error:"), | |
567 | c.warning(&*s), | |
568 | c.good(did_you_mean.as_ref()), | |
569 | name, | |
570 | c.good("--"), | |
571 | &*s, | |
572 | usage, | |
573 | c.good("--help") | |
574 | ), | |
8bb4bdeb XL |
575 | kind: ErrorKind::InvalidSubcommand, |
576 | info: Some(vec![s]), | |
577 | } | |
578 | } | |
579 | ||
580 | #[doc(hidden)] | |
041b39d2 | 581 | pub fn unrecognized_subcommand<S, N>(subcmd: S, name: N, color: ColorWhen) -> Self |
ff7c6d11 XL |
582 | where |
583 | S: Into<String>, | |
584 | N: Display, | |
8bb4bdeb XL |
585 | { |
586 | let s = subcmd.into(); | |
041b39d2 | 587 | let c = Colorizer::new(ColorizerOption { |
8bb4bdeb XL |
588 | use_stderr: true, |
589 | when: color, | |
041b39d2 | 590 | }); |
8bb4bdeb | 591 | Error { |
ff7c6d11 XL |
592 | message: format!( |
593 | "{} The subcommand '{}' wasn't recognized\n\n\ | |
594 | {}\n\t\ | |
595 | {} help <subcommands>...\n\n\ | |
596 | For more information try {}", | |
597 | c.error("error:"), | |
598 | c.warning(&*s), | |
599 | c.warning("USAGE:"), | |
600 | name, | |
601 | c.good("--help") | |
602 | ), | |
8bb4bdeb XL |
603 | kind: ErrorKind::UnrecognizedSubcommand, |
604 | info: Some(vec![s]), | |
605 | } | |
606 | } | |
607 | ||
608 | #[doc(hidden)] | |
041b39d2 | 609 | pub fn missing_required_argument<R, U>(required: R, usage: U, color: ColorWhen) -> Self |
ff7c6d11 XL |
610 | where |
611 | R: Display, | |
612 | U: Display, | |
8bb4bdeb | 613 | { |
041b39d2 | 614 | let c = Colorizer::new(ColorizerOption { |
8bb4bdeb XL |
615 | use_stderr: true, |
616 | when: color, | |
041b39d2 | 617 | }); |
8bb4bdeb | 618 | Error { |
ff7c6d11 XL |
619 | message: format!( |
620 | "{} The following required arguments were not provided:{}\n\n\ | |
621 | {}\n\n\ | |
622 | For more information try {}", | |
623 | c.error("error:"), | |
624 | required, | |
625 | usage, | |
626 | c.good("--help") | |
627 | ), | |
8bb4bdeb XL |
628 | kind: ErrorKind::MissingRequiredArgument, |
629 | info: None, | |
630 | } | |
631 | } | |
632 | ||
633 | #[doc(hidden)] | |
041b39d2 | 634 | pub fn missing_subcommand<N, U>(name: N, usage: U, color: ColorWhen) -> Self |
ff7c6d11 XL |
635 | where |
636 | N: AsRef<str> + Display, | |
637 | U: Display, | |
8bb4bdeb | 638 | { |
041b39d2 | 639 | let c = Colorizer::new(ColorizerOption { |
8bb4bdeb XL |
640 | use_stderr: true, |
641 | when: color, | |
041b39d2 | 642 | }); |
8bb4bdeb | 643 | Error { |
ff7c6d11 XL |
644 | message: format!( |
645 | "{} '{}' requires a subcommand, but one was not provided\n\n\ | |
646 | {}\n\n\ | |
647 | For more information try {}", | |
648 | c.error("error:"), | |
649 | c.warning(name), | |
650 | usage, | |
651 | c.good("--help") | |
652 | ), | |
8bb4bdeb XL |
653 | kind: ErrorKind::MissingSubcommand, |
654 | info: None, | |
655 | } | |
656 | } | |
657 | ||
8bb4bdeb | 658 | #[doc(hidden)] |
041b39d2 | 659 | pub fn invalid_utf8<U>(usage: U, color: ColorWhen) -> Self |
ff7c6d11 XL |
660 | where |
661 | U: Display, | |
8bb4bdeb | 662 | { |
041b39d2 | 663 | let c = Colorizer::new(ColorizerOption { |
8bb4bdeb XL |
664 | use_stderr: true, |
665 | when: color, | |
041b39d2 | 666 | }); |
8bb4bdeb | 667 | Error { |
ff7c6d11 XL |
668 | message: format!( |
669 | "{} Invalid UTF-8 was detected in one or more arguments\n\n\ | |
670 | {}\n\n\ | |
671 | For more information try {}", | |
672 | c.error("error:"), | |
673 | usage, | |
674 | c.good("--help") | |
675 | ), | |
8bb4bdeb XL |
676 | kind: ErrorKind::InvalidUtf8, |
677 | info: None, | |
678 | } | |
679 | } | |
680 | ||
681 | #[doc(hidden)] | |
8faf50e0 | 682 | pub fn too_many_values<V, U>(val: V, arg: &AnyArg, usage: U, color: ColorWhen) -> Self |
ff7c6d11 XL |
683 | where |
684 | V: AsRef<str> + Display + ToOwned, | |
ff7c6d11 | 685 | U: Display, |
8bb4bdeb XL |
686 | { |
687 | let v = val.as_ref(); | |
041b39d2 | 688 | let c = Colorizer::new(ColorizerOption { |
8bb4bdeb XL |
689 | use_stderr: true, |
690 | when: color, | |
041b39d2 | 691 | }); |
8bb4bdeb | 692 | Error { |
ff7c6d11 XL |
693 | message: format!( |
694 | "{} The value '{}' was provided to '{}', but it wasn't expecting \ | |
695 | any more values\n\n\ | |
696 | {}\n\n\ | |
697 | For more information try {}", | |
698 | c.error("error:"), | |
699 | c.warning(v), | |
700 | c.warning(arg.to_string()), | |
701 | usage, | |
702 | c.good("--help") | |
703 | ), | |
8bb4bdeb XL |
704 | kind: ErrorKind::TooManyValues, |
705 | info: Some(vec![arg.name().to_owned(), v.to_owned()]), | |
706 | } | |
707 | } | |
708 | ||
709 | #[doc(hidden)] | |
8faf50e0 | 710 | pub fn too_few_values<U>( |
0531ce1d | 711 | arg: &AnyArg, |
ff7c6d11 XL |
712 | min_vals: u64, |
713 | curr_vals: usize, | |
714 | usage: U, | |
715 | color: ColorWhen, | |
716 | ) -> Self | |
717 | where | |
ff7c6d11 | 718 | U: Display, |
8bb4bdeb | 719 | { |
041b39d2 | 720 | let c = Colorizer::new(ColorizerOption { |
8bb4bdeb XL |
721 | use_stderr: true, |
722 | when: color, | |
041b39d2 | 723 | }); |
8bb4bdeb | 724 | Error { |
ff7c6d11 XL |
725 | message: format!( |
726 | "{} The argument '{}' requires at least {} values, but only {} w{} \ | |
727 | provided\n\n\ | |
728 | {}\n\n\ | |
729 | For more information try {}", | |
730 | c.error("error:"), | |
731 | c.warning(arg.to_string()), | |
732 | c.warning(min_vals.to_string()), | |
733 | c.warning(curr_vals.to_string()), | |
734 | if curr_vals > 1 { "ere" } else { "as" }, | |
735 | usage, | |
736 | c.good("--help") | |
737 | ), | |
8bb4bdeb XL |
738 | kind: ErrorKind::TooFewValues, |
739 | info: Some(vec![arg.name().to_owned()]), | |
740 | } | |
741 | } | |
742 | ||
743 | #[doc(hidden)] | |
3dfed10e | 744 | pub fn value_validation(arg: Option<&AnyArg>, err: String, color: ColorWhen) -> Self { |
041b39d2 | 745 | let c = Colorizer::new(ColorizerOption { |
8bb4bdeb XL |
746 | use_stderr: true, |
747 | when: color, | |
041b39d2 | 748 | }); |
8bb4bdeb | 749 | Error { |
ff7c6d11 XL |
750 | message: format!( |
751 | "{} Invalid value{}: {}", | |
752 | c.error("error:"), | |
753 | if let Some(a) = arg { | |
754 | format!(" for '{}'", c.warning(a.to_string())) | |
755 | } else { | |
756 | "".to_string() | |
757 | }, | |
758 | err | |
759 | ), | |
8bb4bdeb XL |
760 | kind: ErrorKind::ValueValidation, |
761 | info: None, | |
762 | } | |
763 | } | |
764 | ||
765 | #[doc(hidden)] | |
766 | pub fn value_validation_auto(err: String) -> Self { | |
0531ce1d | 767 | let n: Option<&AnyArg> = None; |
041b39d2 | 768 | Error::value_validation(n, err, ColorWhen::Auto) |
8bb4bdeb XL |
769 | } |
770 | ||
771 | #[doc(hidden)] | |
8faf50e0 | 772 | pub fn wrong_number_of_values<S, U>( |
0531ce1d | 773 | arg: &AnyArg, |
ff7c6d11 XL |
774 | num_vals: u64, |
775 | curr_vals: usize, | |
776 | suffix: S, | |
777 | usage: U, | |
778 | color: ColorWhen, | |
779 | ) -> Self | |
780 | where | |
ff7c6d11 XL |
781 | S: Display, |
782 | U: Display, | |
8bb4bdeb | 783 | { |
041b39d2 | 784 | let c = Colorizer::new(ColorizerOption { |
8bb4bdeb XL |
785 | use_stderr: true, |
786 | when: color, | |
041b39d2 | 787 | }); |
8bb4bdeb | 788 | Error { |
ff7c6d11 XL |
789 | message: format!( |
790 | "{} The argument '{}' requires {} values, but {} w{} \ | |
791 | provided\n\n\ | |
792 | {}\n\n\ | |
793 | For more information try {}", | |
794 | c.error("error:"), | |
795 | c.warning(arg.to_string()), | |
796 | c.warning(num_vals.to_string()), | |
797 | c.warning(curr_vals.to_string()), | |
798 | suffix, | |
799 | usage, | |
800 | c.good("--help") | |
801 | ), | |
8bb4bdeb XL |
802 | kind: ErrorKind::WrongNumberOfValues, |
803 | info: Some(vec![arg.name().to_owned()]), | |
804 | } | |
805 | } | |
806 | ||
807 | #[doc(hidden)] | |
8faf50e0 | 808 | pub fn unexpected_multiple_usage<U>(arg: &AnyArg, usage: U, color: ColorWhen) -> Self |
ff7c6d11 | 809 | where |
ff7c6d11 | 810 | U: Display, |
8bb4bdeb | 811 | { |
041b39d2 | 812 | let c = Colorizer::new(ColorizerOption { |
8bb4bdeb XL |
813 | use_stderr: true, |
814 | when: color, | |
041b39d2 | 815 | }); |
8bb4bdeb | 816 | Error { |
ff7c6d11 XL |
817 | message: format!( |
818 | "{} The argument '{}' was provided more than once, but cannot \ | |
819 | be used multiple times\n\n\ | |
820 | {}\n\n\ | |
821 | For more information try {}", | |
822 | c.error("error:"), | |
823 | c.warning(arg.to_string()), | |
824 | usage, | |
825 | c.good("--help") | |
826 | ), | |
8bb4bdeb XL |
827 | kind: ErrorKind::UnexpectedMultipleUsage, |
828 | info: Some(vec![arg.name().to_owned()]), | |
829 | } | |
830 | } | |
831 | ||
832 | #[doc(hidden)] | |
ff7c6d11 XL |
833 | pub fn unknown_argument<A, U>(arg: A, did_you_mean: &str, usage: U, color: ColorWhen) -> Self |
834 | where | |
835 | A: Into<String>, | |
836 | U: Display, | |
8bb4bdeb XL |
837 | { |
838 | let a = arg.into(); | |
041b39d2 | 839 | let c = Colorizer::new(ColorizerOption { |
8bb4bdeb XL |
840 | use_stderr: true, |
841 | when: color, | |
041b39d2 | 842 | }); |
8bb4bdeb | 843 | Error { |
ff7c6d11 XL |
844 | message: format!( |
845 | "{} Found argument '{}' which wasn't expected, or isn't valid in \ | |
846 | this context{}\n\ | |
847 | {}\n\n\ | |
848 | For more information try {}", | |
849 | c.error("error:"), | |
850 | c.warning(&*a), | |
851 | if did_you_mean.is_empty() { | |
852 | "\n".to_owned() | |
853 | } else { | |
854 | format!("{}\n", did_you_mean) | |
855 | }, | |
856 | usage, | |
857 | c.good("--help") | |
858 | ), | |
8bb4bdeb XL |
859 | kind: ErrorKind::UnknownArgument, |
860 | info: Some(vec![a]), | |
861 | } | |
862 | } | |
863 | ||
864 | #[doc(hidden)] | |
041b39d2 XL |
865 | pub fn io_error(e: &Error, color: ColorWhen) -> Self { |
866 | let c = Colorizer::new(ColorizerOption { | |
8bb4bdeb XL |
867 | use_stderr: true, |
868 | when: color, | |
041b39d2 | 869 | }); |
8bb4bdeb XL |
870 | Error { |
871 | message: format!("{} {}", c.error("error:"), e.description()), | |
872 | kind: ErrorKind::Io, | |
873 | info: None, | |
874 | } | |
875 | } | |
876 | ||
877 | #[doc(hidden)] | |
878 | pub fn argument_not_found_auto<A>(arg: A) -> Self | |
ff7c6d11 XL |
879 | where |
880 | A: Into<String>, | |
8bb4bdeb XL |
881 | { |
882 | let a = arg.into(); | |
041b39d2 | 883 | let c = Colorizer::new(ColorizerOption { |
8bb4bdeb | 884 | use_stderr: true, |
041b39d2 XL |
885 | when: ColorWhen::Auto, |
886 | }); | |
8bb4bdeb | 887 | Error { |
a2a8927a | 888 | message: format!("{} The argument '{}' wasn't found", c.error("error:"), a), |
8bb4bdeb XL |
889 | kind: ErrorKind::ArgumentNotFound, |
890 | info: Some(vec![a]), | |
891 | } | |
892 | } | |
893 | ||
894 | /// Create an error with a custom description. | |
895 | /// | |
896 | /// This can be used in combination with `Error::exit` to exit your program | |
897 | /// with a custom error message. | |
898 | pub fn with_description(description: &str, kind: ErrorKind) -> Self { | |
041b39d2 | 899 | let c = Colorizer::new(ColorizerOption { |
8bb4bdeb | 900 | use_stderr: true, |
041b39d2 XL |
901 | when: ColorWhen::Auto, |
902 | }); | |
8bb4bdeb XL |
903 | Error { |
904 | message: format!("{} {}", c.error("error:"), description), | |
a2a8927a | 905 | kind, |
8bb4bdeb XL |
906 | info: None, |
907 | } | |
908 | } | |
909 | } | |
910 | ||
911 | impl StdError for Error { | |
3dfed10e XL |
912 | fn description(&self) -> &str { |
913 | &*self.message | |
914 | } | |
8bb4bdeb XL |
915 | } |
916 | ||
917 | impl Display for Error { | |
3dfed10e XL |
918 | fn fmt(&self, f: &mut std_fmt::Formatter) -> std_fmt::Result { |
919 | writeln!(f, "{}", self.message) | |
920 | } | |
8bb4bdeb XL |
921 | } |
922 | ||
923 | impl From<io::Error> for Error { | |
3dfed10e XL |
924 | fn from(e: io::Error) -> Self { |
925 | Error::with_description(e.description(), ErrorKind::Io) | |
926 | } | |
8bb4bdeb XL |
927 | } |
928 | ||
929 | impl From<std_fmt::Error> for Error { | |
930 | fn from(e: std_fmt::Error) -> Self { | |
931 | Error::with_description(e.description(), ErrorKind::Format) | |
932 | } | |
933 | } |