kitsune2_api/
error.rs

1//! Kitsune2 error types.
2
3use std::sync::Arc;
4
5/// A clonable trait-object inner error.
6#[derive(Clone, Default)]
7pub struct DynInnerError(
8    pub Option<Arc<dyn std::error::Error + 'static + Send + Sync>>,
9);
10
11impl std::fmt::Debug for DynInnerError {
12    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
13        self.0.fmt(f)
14    }
15}
16
17impl std::fmt::Display for DynInnerError {
18    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19        match self.0.as_ref() {
20            None => f.write_str("None"),
21            Some(s) => s.fmt(f),
22        }
23    }
24}
25
26impl std::error::Error for DynInnerError {
27    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
28        self.0.as_ref().map(|s| {
29            let out: &(dyn std::error::Error + 'static) = &**s;
30            out
31        })
32    }
33}
34
35impl DynInnerError {
36    /// Construct a new DynInnerError from a source error.
37    pub fn new<E: std::error::Error + 'static + Send + Sync>(e: E) -> Self {
38        Self(Some(Arc::new(e)))
39    }
40}
41
42/// The core kitsune2 error type. This type is used in all external
43/// kitsune apis as well as internally in some modules.
44///
45/// This type is required to implement `Clone` to ease the use of
46/// shared futures, which require the entire `Result` to be `Clone`.
47#[derive(Debug, Clone, thiserror::Error)]
48pub enum K2Error {
49    /// Generic Kitsune2 internal error.
50    #[error("{ctx} (src: {src})")]
51    Other {
52        /// Any context associated with this error.
53        ctx: Arc<str>,
54
55        /// The inner error (if any).
56        #[source]
57        src: DynInnerError,
58    },
59}
60
61impl K2Error {
62    /// Construct an "other" error with an inner source error.
63    pub fn other_src<
64        C: std::fmt::Display,
65        S: std::error::Error + 'static + Send + Sync,
66    >(
67        ctx: C,
68        src: S,
69    ) -> Self {
70        Self::Other {
71            ctx: ctx.to_string().into_boxed_str().into(),
72            src: DynInnerError::new(src),
73        }
74    }
75
76    /// Construct an "other" error.
77    pub fn other<C: std::fmt::Display>(ctx: C) -> Self {
78        Self::Other {
79            ctx: ctx.to_string().into_boxed_str().into(),
80            src: DynInnerError::default(),
81        }
82    }
83}
84
85/// The core kitsune2 result type.
86pub type K2Result<T> = Result<T, K2Error>;
87
88#[cfg(test)]
89mod test {
90    use super::*;
91
92    #[test]
93    fn error_display() {
94        assert_eq!(
95            "bla (src: None)",
96            K2Error::other("bla").to_string().as_str(),
97        );
98        assert_eq!(
99            "bla (src: None)",
100            K2Error::other("bla".to_string()).to_string().as_str(),
101        );
102        assert_eq!(
103            "foo (src: bar)",
104            K2Error::other_src("foo", std::io::Error::other("bar"))
105                .to_string()
106                .as_str(),
107        );
108    }
109
110    #[test]
111    fn error_debug() {
112        assert_eq!(
113            "Other { ctx: \"bla\", src: None }",
114            format!("{:?}", K2Error::other("bla")).as_str(),
115        );
116        assert_eq!(
117            "Other { ctx: \"foo\", src: Some(Custom { kind: Other, error: \"bar\" }) }",
118            format!(
119                "{:?}",
120                K2Error::other_src("foo", std::io::Error::other("bar"))
121            )
122            .as_str(),
123        );
124    }
125
126    #[test]
127    fn ensure_k2error_type_is_send_and_sync() {
128        fn ensure<T: std::fmt::Display + Send + Sync>(_t: T) {}
129        ensure(K2Error::other("bla"));
130    }
131}