value_bag/internal/
error.rs1use crate::{
2 fill::Slot,
3 std::{any::Any, error},
4 ValueBag,
5};
6
7use super::Internal;
8
9impl<'v> ValueBag<'v> {
10 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 #[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 #[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 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 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}