taos_error/
lib.rs

1#![cfg_attr(nightly, feature(error_generic_member_access))]
2use std::{
3    any::Any,
4    borrow::Cow,
5    fmt::{self, Debug, Display},
6    str::FromStr,
7};
8
9use mdsn::DsnError;
10use source::Inner;
11use thiserror::Error;
12
13mod code;
14mod source;
15
16pub use code::Code;
17
18/// The `Error` type, a wrapper around raw libtaos.so client errors or
19/// dynamic error types that could be integrated into [anyhow::Error].
20///
21/// # Constructions
22///
23/// We prefer to use [format_err] to construct errors, but you can always use
24/// constructor API in your codes.
25///
26/// ## Constructor API
27///
28/// Use error code from native client. You can use it directly with error code
29///
30/// ```rust
31/// # use taos_error::Error;
32/// let error = Error::from_code(0x2603);
33/// ```
34///
35/// Or with error message from C API.
36///
37/// ```rust
38/// # use taos_error::Error;
39/// let error = Error::new(0x0216, r#"syntax error near "123);""#); // Syntax error in SQL
40/// ```
41///
42/// # Display representations
43#[derive(Error)]
44#[must_use]
45pub struct Error {
46    /// Error code, will be displayed when code is not 0xFFFF.
47    code: Code,
48    /// Error context, use this along with `.msg` or `.source`.
49    context: Option<String>,
50    /// Error source, from raw or other error type.
51    #[cfg_attr(nightly, backtrace)]
52    source: Inner,
53}
54
55unsafe impl Send for Error {}
56unsafe impl Sync for Error {}
57
58impl Debug for Error {
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        if f.alternate() {
61            f.debug_struct("Error")
62                .field("code", &self.code)
63                .field("context", &self.context)
64                .field("source", &self.source)
65                .finish()
66        } else {
67            // Error code prefix
68            if self.code != Code::FAILED {
69                write!(f, "[{:#06X}] ", self.code)?;
70            }
71            if let Some(context) = &self.context {
72                f.write_fmt(format_args!("{}", context))?;
73                writeln!(f)?;
74                writeln!(f)?;
75                writeln!(f, "Caused by:")?;
76
77                let chain = self.source.chain();
78                for (idx, source) in chain.enumerate() {
79                    writeln!(f, "{:4}: {}", idx, source)?;
80                }
81            } else {
82                let mut chain = self.source.chain();
83                if let Some(context) = chain.next() {
84                    f.write_fmt(format_args!("{}", context))?;
85                }
86
87                if self.source.deep() {
88                    writeln!(f)?;
89                    writeln!(f)?;
90                    writeln!(f, "Caused by:")?;
91                    for (idx, source) in chain.enumerate() {
92                        writeln!(f, "{:4}: {}", idx, source)?;
93                    }
94                }
95            }
96            #[cfg(nightly)]
97            {
98                writeln!(f)?;
99                writeln!(f, "Backtrace:")?;
100                writeln!(f, "{}", self.source.backtrace())?;
101            }
102
103            Ok(())
104        }
105    }
106}
107
108impl Display for Error {
109    #[inline]
110    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111        // Error code prefix
112        if self.code != Code::FAILED {
113            write!(f, "[{:#06X}] ", self.code)?;
114        }
115        // Error context
116        if let Some(context) = self.context.as_deref() {
117            write!(f, "{}", context)?;
118
119            if self.source.is_empty() {
120                return Ok(());
121            }
122            // pretty print error source.
123            f.write_str(": ")?;
124        } else if self.source.is_empty() {
125            return f.write_str("Unknown error");
126        }
127
128        if f.alternate() {
129            write!(f, "{:#}", self.source)?;
130        } else {
131            write!(f, "{}", self.source)?;
132        }
133        Ok(())
134    }
135}
136
137impl From<DsnError> for Error {
138    fn from(dsn: DsnError) -> Self {
139        Self::new(Code::FAILED, dsn.to_string())
140    }
141}
142
143impl From<anyhow::Error> for Error {
144    fn from(error: anyhow::Error) -> Self {
145        Self {
146            code: Code::FAILED,
147            context: None,
148            source: Inner::any(error),
149        }
150    }
151}
152
153impl<C: Into<Code>> From<C> for Error {
154    fn from(value: C) -> Self {
155        Self::from_code(value.into())
156    }
157}
158
159impl<'a> From<&'a str> for Error {
160    fn from(value: &'a str) -> Self {
161        Self::from_string(value.to_string())
162    }
163}
164
165pub type Result<T> = std::result::Result<T, Error>;
166
167impl Error {
168    #[inline(always)]
169    pub fn new_with_context(
170        code: impl Into<Code>,
171        err: impl Into<String>,
172        context: impl Into<String>,
173    ) -> Self {
174        Self {
175            code: code.into(),
176            context: Some(context.into()),
177            source: err.into().into(),
178        }
179    }
180    #[inline]
181    pub fn new(code: impl Into<Code>, err: impl Into<String>) -> Self {
182        Self {
183            code: code.into(),
184            context: None,
185            source: err.into().into(),
186        }
187    }
188
189    #[inline]
190    pub fn context(mut self, context: impl Into<String>) -> Self {
191        self.context = Some(match self.context {
192            Some(pre) => format!("{}: {}", context.into(), pre),
193            None => context.into(),
194        });
195        self
196    }
197
198    #[inline]
199    #[deprecated = "Use self.code() instead"]
200    pub fn errno(&self) -> Code {
201        self.code
202    }
203
204    #[inline]
205    pub const fn code(&self) -> Code {
206        self.code
207    }
208    #[inline]
209    pub fn message(&self) -> String {
210        self.source.to_string()
211    }
212
213    #[inline(always)]
214    pub fn from_code(code: impl Into<Code>) -> Self {
215        let code = code.into();
216        if let Some(str) = code._priv_err_str() {
217            Self::new(code, str)
218        } else {
219            Self {
220                code,
221                context: None,
222                source: Inner::empty(),
223            }
224        }
225    }
226
227    #[inline]
228    pub fn from_string(err: impl Into<Cow<'static, str>>) -> Self {
229        anyhow::format_err!("{}", err.into()).into()
230    }
231
232    #[inline]
233    pub fn from_any(err: impl Into<anyhow::Error>) -> Self {
234        err.into().into()
235    }
236
237    #[inline]
238    pub fn any(err: impl Into<anyhow::Error> + 'static) -> Self {
239        if err.type_id() == std::any::TypeId::of::<Self>() {
240            // let err = Box::new(&err as &dyn Any);
241            let err = &err as &dyn Any;
242            let err = err.downcast_ref::<Self>().unwrap();
243            dbg!(err);
244            return Self {
245                code: err.code,
246                context: err.context.clone(),
247                source: err.source.clone(),
248            };
249        }
250        err.into().into()
251    }
252
253    #[inline]
254    pub fn success(&self) -> bool {
255        self.code == 0
256    }
257
258    #[inline]
259    pub fn with_code(mut self, code: impl Into<Code>) -> Self {
260        self.code = code.into();
261        self
262    }
263}
264
265/// Format error with `code`, `raw`, and `context` messages.
266///
267/// - `code` is come from native C API for from websocket API.
268/// - `raw` is the error message which is treated as internal error.
269/// - `context` is some context message which is helpful to users.
270///
271/// We suggest to use all the three fields to construct a more human-readable and
272/// meaningful error. Suck as:
273///
274/// ```rust
275/// # use taos_error::*;
276/// let err = format_err!(
277///     code = 0x0618,
278///     raw = "Message error from native API",
279///     context = "Query with sql: `select server_status()`"
280/// );
281/// let err_str = err.to_string();
282/// assert_eq!(err_str, "[0x0618] Query with sql: `select server_status()`: Internal error: `Message error from native API`");
283/// ```
284///
285/// It will give the error:
286/// ```text
287/// [0x0618] Query with sql: `select server_status()`: Internal error: `Message error from native API`
288/// ```
289///
290/// For more complex error expressions, use a `format!` like API as this:
291///
292/// ```rust
293/// # use taos_error::*;
294/// # let sql = "select * from test.meters";
295/// # let context = "some context";
296/// let _ = format_err!(
297///     code = 0x0618,
298///     raw = ("Message error from native API while calling {}", "some_c_api"),
299///     context = ("Query with sql {:?} in {}", sql, context),
300/// );
301/// ```
302///
303/// In this kind of usage, `code = ` is optional, so you can use a shorter line:
304///
305/// ```rust
306/// # use taos_error::*;
307/// let _ = format_err!(0x0618, raw = "Some error", context = "Query error");
308/// ```
309///
310/// The `raw` or `context` is optional too:
311///
312/// ```rust
313/// # use taos_error::*;
314/// let _ = format_err!(0x0618, raw = "Some error");
315/// let _ = format_err!(0x0618, context = "Some error");
316/// ```
317///
318/// For non-internal errors, eg. if you prefer construct an [anyhow]-like error manually,
319/// you can use the same arguments like [anyhow::format_err] with this pattern:
320///
321/// ```rust
322/// # use taos_error::*;
323/// # let message = "message";
324/// let err = format_err!(any = "Error here: {}", message);
325/// # assert_eq!(err.to_string(), "Error here: message");
326/// let err = format_err!("Error here: {}", message);
327/// # assert_eq!(err.to_string(), "Error here: message");
328/// ```
329///
330/// It's equivalent to:
331///
332/// ```rust
333/// # use taos_error::*;
334/// # use anyhow;
335/// # let message = "message";
336/// let err = Error::from(anyhow::format_err!("Error here: {}", message));
337/// ```
338///
339#[macro_export]
340macro_rules! format_err {
341    (code = $c:expr, raw = $arg:expr, context = $arg2:expr) => {
342        $crate::Error::new_with_context($c, $arg, $arg2)
343    };
344    (code = $c:expr, raw = $arg:expr) => {
345        $crate::Error::new($c, $arg)
346    };
347    (code = $c:expr, raw = ($($arg:tt)*), context = ($($arg2:tt)*) $(,)?) => {
348        $crate::Error::new_with_context($c, __priv_format!($($arg)*), __priv_format!($($arg2)*))
349    };
350    (code = $c:expr, context = $($arg2:tt)*) => {
351        $crate::Error::from($c).context(format!($($arg2)*))
352    };
353    // // (code = $c:expr, raw = $arg:literal, context = $arg2:literal) => {
354    // //     $crate::Error::new_with_context($c, format!($arg), format!($arg2))
355    // // };
356    // // (code = $c:expr, raw = $arg:literal, context = $arg2:expr) => {
357    // //     $crate::Error::new_with_context($c, format!($arg), $arg2)
358    // // };
359    (code = $c:expr, raw = $arg:literal, context = $($arg2:tt)*) => {
360        $crate::Error::new_with_context($c, format!($arg), $crate::__priv_format!($($arg2)*))
361    };
362    (code = $c:expr, raw = $arg:ident, context = $($arg2:tt)*) => {
363        $crate::Error::new_with_context($c, $arg, $crate::__priv_format!($($arg2)*))
364    };
365    (code = $c:expr) => {
366        $crate::Error::from_code($c)
367    };
368    (code = $c:expr, raw = $($arg:tt)*) => {
369        $crate::Error::new($c, format!($($arg)*))
370    };
371    (code = $c:expr, $($arg:tt)*) => {
372        $crate::Error::new($c, format!($($arg)*))
373    };
374    (any = $($arg:tt)*) => {
375        $crate::Error::from_string(format!($($arg)*))
376    };
377    (raw = $($arg:tt)*) => {
378        compile_error!("`raw` error message must be used along with an error code!")
379    };
380
381    ($c:expr, raw = $arg:expr) => {
382        $crate::Error::new($c, $arg)
383    };
384    ($c:expr, raw = $arg:expr, context = $arg2:expr) => {
385        $crate::Error::new_with_context($c, $arg, $arg2)
386    };
387    ($c:expr, raw = ($($arg:tt)*), context = ($($arg2:tt)*) $(,)?) => {
388        $crate::Error::new_with_context($c, format!($($arg)*), format!($($arg2)*))
389    };
390    ($c:expr, context = $arg:expr) => {
391        $crate::Error::from($c).context($arg)
392    };
393    ($c:expr, context = $($arg2:tt)*) => {
394        $crate::Error::from($c).context(format!($($arg2)*))
395    };
396    ($c:expr, raw = $($arg:tt)*) => {
397        $crate::Error::new($c, format!($($arg)*))
398    };
399    ($c:expr) => {
400        $crate::Error::from($c)
401    };
402    ($($arg:tt)*) => {
403        $crate::Error::from_string(format!($($arg)*))
404    };
405}
406#[macro_export]
407macro_rules! __priv_format {
408    ($msg:literal $(,)?) => {
409        literal.to_string()
410    };
411    ($err:expr $(,)?) => {
412        $err
413    };
414    ($fmt:expr, $($arg:tt)*) => {
415        format!($fmt, $($arg)*)
416    };
417}
418
419#[macro_export]
420macro_rules! bail {
421    ($($arg:tt)*) => {
422        return std::result::Result::Err($crate::format_err!($($arg)*))
423    };
424}
425
426impl FromStr for Error {
427    type Err = ();
428
429    #[inline]
430    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
431        Ok(Self::from_string(s.to_string()))
432    }
433}
434
435#[cfg(feature = "serde")]
436impl serde::de::Error for Error {
437    #[inline]
438    fn custom<T: fmt::Display>(msg: T) -> Error {
439        Error::from_string(format!("{}", msg))
440    }
441}
442
443#[test]
444fn test_format_err() {
445    let code = 0xF000;
446    let raw = "Nothing";
447    let context = "Error";
448    let err = dbg!(format_err!(code, raw = raw, context = context));
449    assert_eq!(err.to_string(), "[0xF000] Error: Internal error: `Nothing`");
450    let err = dbg!(format_err!(code, raw = raw));
451    assert_eq!(err.to_string(), "[0xF000] Internal error: `Nothing`");
452    let err = dbg!(format_err!(code, context = context));
453    assert_eq!(err.to_string(), "[0xF000] Error");
454
455    let err = dbg!(format_err!(code = 0xF000, context = "Error here"));
456    assert_eq!(err.to_string(), "[0xF000] Error here");
457    let err = dbg!(format_err!(0x6789, context = "Error here: {}", 1));
458    assert_eq!(err.to_string(), "[0x6789] Error here: 1");
459    let err = dbg!(format_err!(code = 0x6789, context = "Error here: {}", 1));
460    assert_eq!(err.to_string(), "[0x6789] Error here: 1");
461
462    let err = dbg!(format_err!(code = 0x6789, raw = "Error here: {}", 1));
463    assert_eq!(err.to_string(), "[0x6789] Internal error: `Error here: 1`");
464
465    let err = dbg!(format_err!(0x6789, raw = "Error here: {}", 1));
466    assert_eq!(err.to_string(), "[0x6789] Internal error: `Error here: 1`");
467
468    let err = dbg!(format_err!(
469        code = 0x6789,
470        raw = ("Error here: {}", 1),
471        context = ("Query error with {:?}", "sql"),
472    ));
473    assert_eq!(
474        err.to_string(),
475        "[0x6789] Query error with \"sql\": Internal error: `Error here: 1`"
476    );
477
478    let err = dbg!(format_err!("Error here"));
479    assert_eq!(err.to_string(), "Error here");
480
481    let err = dbg!(format_err!(0x2603));
482    assert_eq!(
483        err.to_string(),
484        "[0x2603] Internal error: `Table does not exist`"
485    );
486
487    let err = dbg!(format_err!(0x6789));
488    assert_eq!(err.to_string(), "[0x6789] Unknown error");
489
490    let err = dbg!(format_err!(0x6789, context = "Error here"));
491    assert_eq!(err.to_string(), "[0x6789] Error here");
492}
493
494#[test]
495fn test_bail() {
496    fn use_bail() -> Result<()> {
497        bail!(code = 0x2603, context = "Failed to insert into table `abc`");
498    }
499    let err = use_bail();
500    dbg!(&err);
501    assert!(err.is_err());
502    println!("{:?}", err.unwrap_err());
503
504    println!("{:?}", Error::any(use_bail().unwrap_err()));
505}
506
507#[test]
508fn test_display() {
509    let err = Error::new(Code::SUCCESS, "Success").context("nothing");
510    assert!(dbg!(format!("{}", err)).contains("[0x0000] nothing"));
511    let result = std::panic::catch_unwind(|| {
512        let err = Error::new(Code::SUCCESS, "Success").context("nothing");
513        panic!("{:?}", err);
514    });
515    assert!(result.is_err());
516}
517
518#[test]
519fn test_error() {
520    let err = Error::new(Code::SUCCESS, "success");
521    assert_eq!(err.code(), Code::SUCCESS);
522    assert_eq!(err.message(), "Internal error: `success`");
523
524    let _ = Error::from_code(1);
525    assert_eq!(Error::from_string("any").to_string(), "any");
526    assert_eq!(Error::from_string("any").to_string(), "any");
527
528    fn raise_error() -> Result<()> {
529        Err(Error::from_any(DsnError::InvalidDriver("mq".to_string())))
530    }
531    assert_eq!(raise_error().unwrap_err().to_string(), "invalid driver mq");
532}
533
534#[cfg(feature = "serde")]
535#[test]
536fn test_serde_error() {
537    use serde::de::Error as DeError;
538
539    let _ = Error::custom("");
540}