1use conjure_object::Any;
16use serde::Serialize;
17use std::borrow::Cow;
18use std::collections::hash_map::{self, HashMap};
19use std::fmt;
20use std::ops::Index;
21use std::time::Duration;
22use std::{backtrace, error};
23
24use crate::{ErrorType, Internal, SerializableError};
25
26#[derive(Debug)]
28pub struct ThrottleError {
29 duration: Option<Duration>,
30}
31
32impl ThrottleError {
33 #[inline]
35 pub fn duration(&self) -> Option<Duration> {
36 self.duration
37 }
38}
39
40#[derive(Debug)]
42pub struct UnavailableError(());
43
44#[derive(Debug)]
46#[non_exhaustive]
47pub enum ErrorKind {
48 Service(SerializableError),
50 Throttle(ThrottleError),
52 Unavailable(UnavailableError),
54}
55
56#[derive(Debug)]
57struct Inner {
58 cause: Box<dyn error::Error + Sync + Send>,
59 cause_safe: bool,
60 kind: ErrorKind,
61 safe_params: HashMap<Cow<'static, str>, Any>,
62 unsafe_params: HashMap<Cow<'static, str>, Any>,
63 backtraces: Vec<Backtrace>,
64}
65
66#[derive(Debug)]
78pub struct Error(Box<Inner>);
79
80impl Error {
81 pub fn service<E, T>(cause: E, error_type: T) -> Error
83 where
84 E: Into<Box<dyn error::Error + Sync + Send>>,
85 T: ErrorType + Serialize,
86 {
87 Error::service_inner(
88 cause.into(),
89 false,
90 crate::encode(&error_type),
91 T::safe_args(),
92 )
93 }
94
95 pub fn service_safe<E, T>(cause: E, error_type: T) -> Error
97 where
98 E: Into<Box<dyn error::Error + Sync + Send>>,
99 T: ErrorType + Serialize,
100 {
101 Error::service_inner(
102 cause.into(),
103 true,
104 crate::encode(&error_type),
105 T::safe_args(),
106 )
107 }
108
109 pub fn propagated_service<E>(cause: E, error: SerializableError) -> Error
111 where
112 E: Into<Box<dyn error::Error + Sync + Send>>,
113 {
114 Error::service_inner(cause.into(), false, error, &[])
115 }
116
117 pub fn propagated_service_safe<E>(cause: E, error: SerializableError) -> Error
119 where
120 E: Into<Box<dyn error::Error + Sync + Send>>,
121 {
122 Error::service_inner(cause.into(), true, error, &[])
123 }
124
125 fn service_inner(
126 cause: Box<dyn error::Error + Sync + Send>,
127 cause_safe: bool,
128 error: SerializableError,
129 safe_args: &[&str],
130 ) -> Error {
131 let mut safe_params = HashMap::new();
132 let mut unsafe_params = HashMap::new();
133
134 for (key, value) in error.parameters() {
135 let key = Cow::Owned(key.clone());
136 let value = Any::new(value).unwrap();
137 if safe_args.contains(&&*key) {
138 safe_params.insert(key, value);
139 } else {
140 unsafe_params.insert(key, value);
141 }
142 }
143
144 let mut error = Error::new(cause, cause_safe, ErrorKind::Service(error));
145 error.0.safe_params = safe_params;
146 error.0.unsafe_params = unsafe_params;
147 error
148 }
149
150 pub fn throttle<E>(cause: E) -> Error
152 where
153 E: Into<Box<dyn error::Error + Sync + Send>>,
154 {
155 Error::new(
156 cause.into(),
157 false,
158 ErrorKind::Throttle(ThrottleError { duration: None }),
159 )
160 }
161
162 pub fn throttle_safe<E>(cause: E) -> Error
164 where
165 E: Into<Box<dyn error::Error + Sync + Send>>,
166 {
167 Error::new(
168 cause.into(),
169 true,
170 ErrorKind::Throttle(ThrottleError { duration: None }),
171 )
172 }
173
174 pub fn throttle_for<E>(cause: E, duration: Duration) -> Error
177 where
178 E: Into<Box<dyn error::Error + Sync + Send>>,
179 {
180 Error::new(
181 cause.into(),
182 false,
183 ErrorKind::Throttle(ThrottleError {
184 duration: Some(duration),
185 }),
186 )
187 }
188
189 pub fn throttle_for_safe<E>(cause: E, duration: Duration) -> Error
192 where
193 E: Into<Box<dyn error::Error + Sync + Send>>,
194 {
195 Error::new(
196 cause.into(),
197 true,
198 ErrorKind::Throttle(ThrottleError {
199 duration: Some(duration),
200 }),
201 )
202 }
203
204 pub fn unavailable<E>(cause: E) -> Error
206 where
207 E: Into<Box<dyn error::Error + Sync + Send>>,
208 {
209 Error::new(
210 cause.into(),
211 false,
212 ErrorKind::Unavailable(UnavailableError(())),
213 )
214 }
215
216 pub fn unavailable_safe<E>(cause: E) -> Error
218 where
219 E: Into<Box<dyn error::Error + Sync + Send>>,
220 {
221 Error::new(
222 cause.into(),
223 true,
224 ErrorKind::Unavailable(UnavailableError(())),
225 )
226 }
227
228 pub fn internal<E>(cause: E) -> Error
230 where
231 E: Into<Box<dyn error::Error + Sync + Send>>,
232 {
233 Error::service(cause, Internal::new())
234 }
235
236 pub fn internal_safe<E>(cause: E) -> Error
238 where
239 E: Into<Box<dyn error::Error + Sync + Send>>,
240 {
241 Error::service_safe(cause, Internal::new())
242 }
243
244 fn new(cause: Box<dyn error::Error + Sync + Send>, cause_safe: bool, kind: ErrorKind) -> Error {
245 let inner = Inner {
246 cause,
247 cause_safe,
248 kind,
249 safe_params: HashMap::new(),
250 unsafe_params: HashMap::new(),
251 backtraces: vec![],
252 };
253 Error(Box::new(inner)).with_backtrace()
254 }
255
256 #[inline]
260 pub fn cause(&self) -> &(dyn error::Error + 'static + Sync + Send) {
261 &*self.0.cause
262 }
263
264 #[inline]
266 pub fn cause_safe(&self) -> bool {
267 self.0.cause_safe
268 }
269
270 #[inline]
272 pub fn kind(&self) -> &ErrorKind {
273 &self.0.kind
274 }
275
276 pub fn with_safe_param<T>(mut self, key: &'static str, value: T) -> Error
282 where
283 T: Serialize,
284 {
285 let value = Any::new(value).expect("value failed to serialize");
286 self.0.safe_params.insert(Cow::Borrowed(key), value);
287 self
288 }
289
290 pub fn with_unsafe_param<T>(mut self, key: &'static str, value: T) -> Error
296 where
297 T: Serialize,
298 {
299 let value = Any::new(value).expect("value failed to serialize");
300 self.0.unsafe_params.insert(Cow::Borrowed(key), value);
301 self
302 }
303
304 #[inline]
306 pub fn safe_params(&self) -> Params<'_> {
307 Params(&self.0.safe_params)
308 }
309
310 #[inline]
312 pub fn unsafe_params(&self) -> Params<'_> {
313 Params(&self.0.unsafe_params)
314 }
315
316 #[inline]
321 pub fn with_backtrace(mut self) -> Error {
322 self.0.backtraces.push(Backtrace::new());
323 self
324 }
325
326 #[inline]
328 pub fn with_custom_safe_backtrace(mut self, backtrace: String) -> Error {
329 self.0.backtraces.push(Backtrace::custom(backtrace));
330 self
331 }
332
333 #[inline]
335 pub fn backtraces(&self) -> &[Backtrace] {
336 &self.0.backtraces
337 }
338}
339
340#[derive(Debug)]
342pub struct Params<'a>(&'a HashMap<Cow<'static, str>, Any>);
343
344impl<'a> Params<'a> {
345 #[inline]
347 pub fn iter(&self) -> ParamsIter<'a> {
348 ParamsIter(self.0.iter())
349 }
350
351 #[inline]
353 pub fn len(&self) -> usize {
354 self.0.len()
355 }
356
357 #[inline]
359 pub fn is_empty(&self) -> bool {
360 self.0.is_empty()
361 }
362}
363
364impl Index<&str> for Params<'_> {
365 type Output = Any;
366
367 #[inline]
368 fn index(&self, key: &str) -> &Any {
369 &self.0[key]
370 }
371}
372
373impl<'a> IntoIterator for &Params<'a> {
374 type Item = (&'a str, &'a Any);
375 type IntoIter = ParamsIter<'a>;
376
377 #[inline]
378 fn into_iter(self) -> ParamsIter<'a> {
379 self.iter()
380 }
381}
382
383pub struct ParamsIter<'a>(hash_map::Iter<'a, Cow<'static, str>, Any>);
385
386impl<'a> Iterator for ParamsIter<'a> {
387 type Item = (&'a str, &'a Any);
388
389 #[inline]
390 fn next(&mut self) -> Option<(&'a str, &'a Any)> {
391 self.0.next().map(|(a, b)| (&**a, b))
392 }
393
394 #[inline]
395 fn size_hint(&self) -> (usize, Option<usize>) {
396 self.0.size_hint()
397 }
398}
399
400pub struct Backtrace(BacktraceInner);
402
403impl fmt::Debug for Backtrace {
404 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
405 match &self.0 {
406 BacktraceInner::Rust(b) => fmt::Display::fmt(b, fmt),
407 BacktraceInner::Custom(b) => fmt::Display::fmt(b, fmt),
408 }
409 }
410}
411
412impl Backtrace {
413 #[inline]
414 fn new() -> Backtrace {
415 Backtrace(BacktraceInner::Rust(backtrace::Backtrace::force_capture()))
416 }
417
418 #[inline]
419 fn custom(s: String) -> Backtrace {
420 Backtrace(BacktraceInner::Custom(s))
421 }
422}
423
424enum BacktraceInner {
425 Rust(backtrace::Backtrace),
426 Custom(String),
427}