aopt_core/
err.rs

1use std::ffi::OsStr;
2use std::fmt::Display;
3use std::num::ParseFloatError;
4use std::num::ParseIntError;
5use std::ops::Deref;
6use std::thread::AccessError;
7
8use crate::str::display_of_osstr;
9use crate::Uid;
10
11pub type Result<T> = std::result::Result<T, Error>;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub enum Kind {
15    MissingValue,
16
17    PosRequired,
18
19    OptRequired,
20
21    CmdRequired,
22
23    OptionNotFound,
24
25    ExtractValue,
26
27    RawValParse,
28
29    Arg,
30
31    IndexParse,
32
33    CreateStrParse,
34
35    Failure,
36
37    Error,
38
39    NoParserMatched,
40
41    UnexceptedPos,
42
43    ThreadLocalAccess,
44}
45
46impl Kind {
47    const fn desp(&self) -> Option<&'static str> {
48        match self {
49            Kind::UnexceptedPos => Some("can not insert Pos@1 if Cmd exist"),
50            Kind::ThreadLocalAccess => Some("failed access thread local variable"),
51            Kind::NoParserMatched => Some("all parser passed to `getopt!` match failed"),
52            _ => None,
53        }
54    }
55}
56
57#[derive(Debug, Clone)]
58pub struct Error {
59    uid: Option<Uid>,
60
61    kind: Kind,
62
63    desp: Option<String>,
64
65    cause: Option<Box<Error>>,
66}
67
68impl std::error::Error for Error {
69    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
70        self.cause
71            .as_ref()
72            .map(|v| v.deref() as &(dyn std::error::Error + 'static))
73    }
74}
75
76impl Display for Error {
77    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78        let desp = self.desp.as_deref().or(self.kind.desp());
79
80        assert!(
81            desp.is_some(),
82            "need description for error `{:?}`",
83            self.kind
84        );
85
86        if let Some(uid) = self.uid {
87            write!(f, "{} (uid = {})", desp.unwrap(), uid)
88        } else {
89            write!(f, "{}", desp.unwrap())
90        }
91    }
92}
93
94impl Error {
95    pub fn new(kind: Kind) -> Self {
96        Self {
97            kind,
98            uid: None,
99            desp: None,
100            cause: None,
101        }
102    }
103
104    pub fn cause(self, error: Self) -> Self {
105        error.cause_by(self)
106    }
107
108    pub fn cause_by(mut self, cause_by: Self) -> Self {
109        let _ = self.cause.insert(Box::new(cause_by));
110        self
111    }
112
113    pub fn with_uid(mut self, uid: Uid) -> Self {
114        self.uid = Some(uid);
115        self
116    }
117
118    pub fn with_desp(mut self, desp: String) -> Self {
119        self.desp = Some(desp);
120        self
121    }
122
123    pub fn uid(&self) -> Option<Uid> {
124        self.uid
125    }
126
127    pub fn kind(&self) -> &Kind {
128        &self.kind
129    }
130
131    pub fn caused_by(&self) -> Option<&Error> {
132        self.cause.as_deref()
133    }
134
135    /// The error can be moitted if [`is_failure`](Error::is_failure) return true.
136    pub fn is_failure(&self) -> bool {
137        let kind = &self.kind;
138
139        matches!(
140            kind,
141            Kind::RawValParse
142                | Kind::Failure
143                | Kind::ExtractValue
144                | Kind::OptionNotFound
145                | Kind::CmdRequired
146                | Kind::PosRequired
147                | Kind::OptRequired
148                | Kind::MissingValue
149        )
150    }
151
152    /// No Pos@1 allowed if the option set has cmd.
153    pub fn unexcepted_pos() -> Self {
154        Self::new(Kind::UnexceptedPos)
155    }
156
157    pub fn thread_local_access() -> Self {
158        Self::new(Kind::ThreadLocalAccess)
159    }
160
161    pub fn no_parser_matched() -> Self {
162        Self::new(Kind::NoParserMatched)
163    }
164
165    pub fn from<E: std::error::Error + Display>(error: E) -> Self {
166        Self::raise_error(error.to_string())
167    }
168
169    pub fn arg(arg: impl Into<String>, hint: impl Into<String>) -> Self {
170        let desp = format!("invalid argument `{}`: {}", arg.into(), hint.into());
171
172        Self::new(Kind::Arg).with_desp(desp)
173    }
174
175    pub fn sp_rawval(val: Option<&OsStr>, hint: impl Into<String>) -> Self {
176        let desp = format!("invalid value `{}`: {}", display_of_osstr(val), hint.into());
177
178        Self::new(Kind::RawValParse).with_desp(desp)
179    }
180
181    pub fn index_parse(pat: impl Into<String>, hint: impl Into<String>) -> Self {
182        let desp = format!("invalid index string `{}`: {}", pat.into(), hint.into());
183
184        Self::new(Kind::IndexParse).with_desp(desp)
185    }
186
187    pub fn create_str(pat: impl Into<String>, hint: impl Into<String>) -> Self {
188        let desp = format!(
189            "invalid option create string `{}`: {}",
190            pat.into(),
191            hint.into()
192        );
193
194        Self::new(Kind::CreateStrParse).with_desp(desp)
195    }
196
197    pub fn raise_error(msg: impl Into<String>) -> Self {
198        Self::new(Kind::Error).with_desp(msg.into())
199    }
200
201    pub fn raise_failure(msg: impl Into<String>) -> Self {
202        Self::new(Kind::Failure).with_desp(msg.into())
203    }
204
205    pub fn sp_missing_value(name: impl Into<String>) -> Self {
206        let desp = format!("missing value for option `{}`", name.into());
207
208        Self::new(Kind::MissingValue).with_desp(desp)
209    }
210
211    pub fn sp_pos_require<S: Into<String>>(names: Vec<S>) -> Self {
212        let names: Vec<_> = names.into_iter().map(Into::into).collect();
213        let desp = match names.len() {
214            1 => {
215                format!("positional `{}` is force required", names[0])
216            }
217            _ => {
218                format!("positional `{}` are force required", names.join(", "))
219            }
220        };
221
222        Self::new(Kind::PosRequired).with_desp(desp)
223    }
224
225    pub fn sp_opt_require<S: Into<String>>(names: Vec<S>) -> Self {
226        let names: Vec<_> = names.into_iter().map(Into::into).collect();
227        let desp = match names.len() {
228            1 => {
229                format!("option `{}` is force required", names[0])
230            }
231            _ => {
232                format!("option `{}` are force required", names.join(", "))
233            }
234        };
235
236        Self::new(Kind::OptRequired).with_desp(desp)
237    }
238
239    pub fn sp_cmd_require<S: Into<String>>(names: Vec<S>) -> Self {
240        let names: Vec<_> = names.into_iter().map(Into::into).collect();
241        let desp = match names.len() {
242            1 => {
243                format!("command `{}` is force required", names[0])
244            }
245            _ => {
246                format!("command `{}` are force required", names.join(", "))
247            }
248        };
249
250        Self::new(Kind::CmdRequired).with_desp(desp)
251    }
252
253    pub fn sp_not_found(name: impl Into<String>) -> Self {
254        let desp = format!("can not find option `{}`", name.into());
255
256        Self::new(Kind::OptionNotFound).with_desp(desp)
257    }
258
259    pub fn sp_extract(msg: impl Into<String>) -> Self {
260        let desp = format!("extract value failed: `{}`", msg.into());
261
262        Self::new(Kind::ExtractValue).with_desp(desp)
263    }
264}
265
266impl From<ParseIntError> for Error {
267    fn from(value: ParseIntError) -> Self {
268        Error::from(value)
269    }
270}
271
272impl From<ParseFloatError> for Error {
273    fn from(value: ParseFloatError) -> Self {
274        Error::from(value)
275    }
276}
277
278impl From<AccessError> for Error {
279    fn from(value: AccessError) -> Self {
280        Error::from(value)
281    }
282}
283
284#[macro_export]
285macro_rules! error {
286    ($($arg:tt)*) => {
287        $crate::Error::raise_error(format!($($arg)*))
288    };
289}
290
291#[macro_export]
292macro_rules! failure {
293    ($($arg:tt)*) => {
294        $crate::Error::raise_failure(format!($($arg)*))
295    };
296}