Skip to main content

value_bag/internal/
error.rs

1use crate::{
2    fill::Slot,
3    std::{any::Any, error},
4    ValueBag,
5};
6
7use super::Internal;
8
9impl<'v> ValueBag<'v> {
10    /// Get a value from an error.
11    pub fn capture_error<T>(value: &'v T) -> Self
12    where
13        T: error::Error + 'static,
14    {
15        ValueBag {
16            inner: Internal::Error(value),
17        }
18    }
19
20    /// Get a value from an erased value.
21    #[inline]
22    pub const fn from_dyn_error(value: &'v (dyn Error + 'static)) -> Self {
23        ValueBag {
24            inner: Internal::AnonError(value),
25        }
26    }
27
28    /// Try get an error from this value.
29    #[inline]
30    pub fn to_borrowed_error(&self) -> Option<&'v (dyn Error + 'static)> {
31        match self.inner {
32            Internal::Error(value) => Some(value.as_super()),
33            Internal::AnonError(value) => Some(value),
34            _ => None,
35        }
36    }
37}
38
39pub(crate) trait DowncastError {
40    fn as_any(&self) -> &dyn Any;
41    fn as_super(&self) -> &(dyn Error + 'static);
42}
43
44impl<T: error::Error + 'static> DowncastError for T {
45    fn as_any(&self) -> &dyn Any {
46        self
47    }
48
49    fn as_super(&self) -> &(dyn Error + 'static) {
50        self
51    }
52}
53
54impl<'s, 'f> Slot<'s, 'f> {
55    /// Fill the slot with an error.
56    ///
57    /// The given value doesn't need to satisfy any particular lifetime constraints.
58    pub fn fill_error<T>(self, value: T) -> Result<(), crate::Error>
59    where
60        T: error::Error + 'static,
61    {
62        self.fill(|visitor| visitor.error(&value))
63    }
64
65    /// Fill the slot with an error.
66    pub fn fill_dyn_error(self, value: &(dyn Error + 'static)) -> Result<(), crate::Error> {
67        self.fill(|visitor| visitor.error(value))
68    }
69}
70
71pub use self::error::Error;
72
73#[cfg(feature = "owned")]
74pub(crate) mod owned {
75    use super::*;
76    use crate::std::{boxed::Box, fmt, string::ToString};
77
78    #[derive(Clone, Debug)]
79    pub(crate) struct OwnedError {
80        display: Box<str>,
81        source: Option<Box<OwnedError>>,
82    }
83
84    impl fmt::Display for OwnedError {
85        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
86            fmt::Display::fmt(&self.display, f)
87        }
88    }
89
90    impl Error for OwnedError {
91        fn source(&self) -> Option<&(dyn Error + 'static)> {
92            if let Some(ref source) = self.source {
93                Some(&**source)
94            } else {
95                None
96            }
97        }
98    }
99
100    pub(crate) fn buffer(err: &(dyn Error + 'static)) -> OwnedError {
101        OwnedError {
102            display: err.to_string().into(),
103            source: err.source().map(buffer).map(Box::new),
104        }
105    }
106}
107
108impl<'v> From<&'v (dyn Error + 'static)> for ValueBag<'v> {
109    #[inline]
110    fn from(v: &'v (dyn Error + 'static)) -> Self {
111        ValueBag::from_dyn_error(v)
112    }
113}
114
115impl<'v> From<Option<&'v (dyn Error + 'static)>> for ValueBag<'v> {
116    #[inline]
117    fn from(v: Option<&'v (dyn Error + 'static)>) -> Self {
118        ValueBag::from_option(v)
119    }
120}
121
122impl<'v> TryFrom<ValueBag<'v>> for &'v (dyn Error + 'static) {
123    type Error = crate::Error;
124
125    #[inline]
126    fn try_from(v: ValueBag<'v>) -> Result<Self, Self::Error> {
127        v.to_borrowed_error()
128            .ok_or_else(|| Self::Error::msg("conversion failed"))
129    }
130}
131
132impl<'v, 'u> From<&'v &'u (dyn Error + 'static)> for ValueBag<'v>
133where
134    'u: 'v,
135{
136    #[inline]
137    fn from(v: &'v &'u (dyn Error + 'static)) -> Self {
138        ValueBag::from_dyn_error(*v)
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    #[cfg(target_arch = "wasm32")]
145    use wasm_bindgen_test::*;
146
147    use super::*;
148
149    use crate::{
150        std::{io, string::ToString},
151        test::*,
152    };
153
154    #[test]
155    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
156    fn error_capture() {
157        let err = io::Error::from(io::ErrorKind::Other);
158
159        assert_eq!(
160            err.to_string(),
161            ValueBag::capture_error(&err)
162                .to_borrowed_error()
163                .expect("invalid value")
164                .to_string()
165        );
166
167        assert_eq!(
168            err.to_string(),
169            ValueBag::from_dyn_error(&err)
170                .to_borrowed_error()
171                .expect("invalid value")
172                .to_string()
173        );
174    }
175
176    #[test]
177    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
178    fn error_downcast() {
179        let err = io::Error::from(io::ErrorKind::Other);
180
181        assert!(ValueBag::capture_error(&err)
182            .downcast_ref::<io::Error>()
183            .is_some());
184    }
185
186    #[test]
187    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
188    fn error_visit() {
189        let err = io::Error::from(io::ErrorKind::Other);
190
191        ValueBag::from_dyn_error(&err)
192            .visit(TestVisit::default())
193            .expect("failed to visit value");
194    }
195}