1- //! Argument error type for use with lexarg
1+ //! Error type for use with lexarg
22//!
33//! Inspired by [lexopt](https://crates.io/crates/lexopt), `lexarg` simplifies the formula down
44//! further so it can be used for CLI plugin systems.
55//!
66//! ## Example
7- //! ```no_run
8- //! use lexarg_error::Error;
9- //! use lexarg_error::Result;
10- //!
11- //! struct Args {
12- //! thing: String,
13- //! number: u32,
14- //! shout: bool,
15- //! }
16- //!
17- //! fn parse_args() -> Result<Args> {
18- //! use lexarg::Arg::*;
197//!
20- //! let mut thing = None;
21- //! let mut number = 1;
22- //! let mut shout = false;
23- //! let mut raw = std::env::args_os().collect::<Vec<_>>();
24- //! let mut parser = lexarg::Parser::new(&raw);
25- //! parser.bin();
26- //! while let Some(arg) = parser.next() {
27- //! match arg {
28- //! Short('n') | Long("number") => {
29- //! number = parser
30- //! .flag_value().ok_or_else(|| Error::msg("`--number` requires a value"))?
31- //! .to_str().ok_or_else(|| Error::msg("invalid number"))?
32- //! .parse().map_err(|e| Error::msg(e))?;
33- //! }
34- //! Long("shout") => {
35- //! shout = true;
36- //! }
37- //! Value(val) if thing.is_none() => {
38- //! thing = Some(val.to_str().ok_or_else(|| Error::msg("invalid number"))?);
39- //! }
40- //! Long("help") => {
41- //! println!("Usage: hello [-n|--number=NUM] [--shout] THING");
42- //! std::process::exit(0);
43- //! }
44- //! _ => {
45- //! return Err(Error::msg("unexpected argument"));
46- //! }
47- //! }
48- //! }
49- //!
50- //! Ok(Args {
51- //! thing: thing.ok_or_else(|| Error::msg("missing argument THING"))?.to_owned(),
52- //! number,
53- //! shout,
54- //! })
55- //! }
56- //!
57- //! fn main() -> Result<()> {
58- //! let args = parse_args()?;
59- //! let mut message = format!("Hello {}", args.thing);
60- //! if args.shout {
61- //! message = message.to_uppercase();
62- //! }
63- //! for _ in 0..args.number {
64- //! println!("{}", message);
65- //! }
66- //! Ok(())
67- //! }
8+ //! ```no_run
9+ #![ doc = include_str ! ( "../examples/hello-error.rs" ) ]
6810//! ```
6911
7012#![ cfg_attr( docsrs, feature( doc_auto_cfg) ) ]
7719#[ cfg( doctest) ]
7820pub struct ReadmeDoctests ;
7921
80- /// `Result<T, Error>`
81- ///
82- /// `lexarg_error::Result` may be used with one *or* two type parameters.
83- ///
84- /// ```rust
85- /// use lexarg_error::Result;
86- ///
87- /// # const IGNORE: &str = stringify! {
88- /// fn demo1() -> Result<T> {...}
89- /// // ^ equivalent to std::result::Result<T, lexarg_error::Error>
90- ///
91- /// fn demo2() -> Result<T, OtherError> {...}
92- /// // ^ equivalent to std::result::Result<T, OtherError>
93- /// # };
94- /// ```
95- ///
96- /// # Example
97- ///
98- /// ```
99- /// # pub trait Deserialize {}
100- /// #
101- /// # mod serde_json {
102- /// # use super::Deserialize;
103- /// # use std::io;
104- /// #
105- /// # pub fn from_str<T: Deserialize>(json: &str) -> io::Result<T> {
106- /// # unimplemented!()
107- /// # }
108- /// # }
109- /// #
110- /// # #[derive(Debug)]
111- /// # struct ClusterMap;
112- /// #
113- /// # impl Deserialize for ClusterMap {}
114- /// #
115- /// use lexarg_error::Result;
116- ///
117- /// fn main() -> Result<()> {
118- /// # return Ok(());
119- /// let config = std::fs::read_to_string("cluster.json")?;
120- /// let map: ClusterMap = serde_json::from_str(&config)?;
121- /// println!("cluster info: {:#?}", map);
122- /// Ok(())
123- /// }
124- /// ```
22+ /// `Result` that defaults to [`Error`]
12523pub type Result < T , E = Error > = std:: result:: Result < T , E > ;
12624
12725/// Argument error type for use with lexarg
128- #[ derive( Debug ) ]
12926pub struct Error {
13027 msg : String ,
13128}
@@ -137,24 +34,105 @@ impl Error {
13734 where
13835 M : std:: fmt:: Display ,
13936 {
140- Error {
37+ Self {
14138 msg : message. to_string ( ) ,
14239 }
14340 }
14441}
14542
146- impl < E > From < E > for Error
43+ impl From < ErrorContext < ' _ > > for Error {
44+ #[ cold]
45+ fn from ( error : ErrorContext < ' _ > ) -> Self {
46+ Self :: msg ( error. to_string ( ) )
47+ }
48+ }
49+
50+ impl std:: fmt:: Debug for Error {
51+ fn fmt ( & self , formatter : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
52+ self . msg . fmt ( formatter)
53+ }
54+ }
55+
56+ impl std:: fmt:: Display for Error {
57+ fn fmt ( & self , formatter : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
58+ self . msg . fmt ( formatter)
59+ }
60+ }
61+
62+ /// Collect context for creating an [`Error`]
63+ #[ derive( Debug ) ]
64+ pub struct ErrorContext < ' a > {
65+ msg : String ,
66+ within : Option < lexarg:: Arg < ' a > > ,
67+ unexpected : Option < lexarg:: Arg < ' a > > ,
68+ }
69+
70+ impl < ' a > ErrorContext < ' a > {
71+ /// Create a new error object from a printable error message.
72+ #[ cold]
73+ pub fn msg < M > ( message : M ) -> Self
74+ where
75+ M : std:: fmt:: Display ,
76+ {
77+ Self {
78+ msg : message. to_string ( ) ,
79+ within : None ,
80+ unexpected : None ,
81+ }
82+ }
83+
84+ /// [`Arg`][lexarg::Arg] the error occurred within
85+ #[ cold]
86+ pub fn within ( mut self , within : lexarg:: Arg < ' a > ) -> Self {
87+ self . within = Some ( within) ;
88+ self
89+ }
90+
91+ /// The failing [`Arg`][lexarg::Arg]
92+ #[ cold]
93+ pub fn unexpected ( mut self , unexpected : lexarg:: Arg < ' a > ) -> Self {
94+ self . unexpected = Some ( unexpected) ;
95+ self
96+ }
97+ }
98+
99+ impl < E > From < E > for ErrorContext < ' _ >
147100where
148101 E : std:: error:: Error + Send + Sync + ' static ,
149102{
150103 #[ cold]
151104 fn from ( error : E ) -> Self {
152- Error :: msg ( error)
105+ Self :: msg ( error)
153106 }
154107}
155108
156- impl std:: fmt:: Display for Error {
109+ impl std:: fmt:: Display for ErrorContext < ' _ > {
157110 fn fmt ( & self , formatter : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
158- self . msg . fmt ( formatter)
111+ self . msg . fmt ( formatter) ?;
112+ if let Some ( unexpected) = & self . unexpected {
113+ write ! ( formatter, ", found `" ) ?;
114+ match unexpected {
115+ lexarg:: Arg :: Short ( short) => write ! ( formatter, "-{short}" ) ?,
116+ lexarg:: Arg :: Long ( long) => write ! ( formatter, "--{long}" ) ?,
117+ lexarg:: Arg :: Escape ( value) => write ! ( formatter, "{value}" ) ?,
118+ lexarg:: Arg :: Value ( value) | lexarg:: Arg :: Unexpected ( value) => {
119+ write ! ( formatter, "{}" , value. to_string_lossy( ) ) ?;
120+ }
121+ }
122+ write ! ( formatter, "`" ) ?;
123+ }
124+ if let Some ( within) = & self . within {
125+ write ! ( formatter, " when parsing `" ) ?;
126+ match within {
127+ lexarg:: Arg :: Short ( short) => write ! ( formatter, "-{short}" ) ?,
128+ lexarg:: Arg :: Long ( long) => write ! ( formatter, "--{long}" ) ?,
129+ lexarg:: Arg :: Escape ( value) => write ! ( formatter, "{value}" ) ?,
130+ lexarg:: Arg :: Value ( value) | lexarg:: Arg :: Unexpected ( value) => {
131+ write ! ( formatter, "{}" , value. to_string_lossy( ) ) ?;
132+ }
133+ }
134+ write ! ( formatter, "`" ) ?;
135+ }
136+ Ok ( ( ) )
159137 }
160138}
0 commit comments