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