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