1#![doc = include_str!("../README.md")]
2#![deny(
3 macro_use_extern_crate,
4 nonstandard_style,
5 rust_2018_idioms,
6 rustdoc::all,
7 trivial_casts,
8 trivial_numeric_casts
9)]
10#![forbid(non_ascii_idents, unsafe_code)]
11#![warn(
12 clippy::absolute_paths,
13 clippy::allow_attributes,
14 clippy::allow_attributes_without_reason,
15 clippy::as_conversions,
16 clippy::as_ptr_cast_mut,
17 clippy::assertions_on_result_states,
18 clippy::branches_sharing_code,
19 clippy::cfg_not_test,
20 clippy::clear_with_drain,
21 clippy::clone_on_ref_ptr,
22 clippy::collection_is_never_read,
23 clippy::create_dir,
24 clippy::dbg_macro,
25 clippy::debug_assert_with_mut_call,
26 clippy::decimal_literal_representation,
27 clippy::default_union_representation,
28 clippy::derive_partial_eq_without_eq,
29 clippy::else_if_without_else,
30 clippy::empty_drop,
31 clippy::empty_structs_with_brackets,
32 clippy::equatable_if_let,
33 clippy::empty_enum_variants_with_brackets,
34 clippy::exit,
35 clippy::expect_used,
36 clippy::fallible_impl_from,
37 clippy::filetype_is_file,
38 clippy::float_cmp_const,
39 clippy::fn_to_numeric_cast_any,
40 clippy::format_push_string,
41 clippy::get_unwrap,
42 clippy::if_then_some_else_none,
43 clippy::imprecise_flops,
44 clippy::infinite_loop,
45 clippy::iter_on_empty_collections,
46 clippy::iter_on_single_items,
47 clippy::iter_over_hash_type,
48 clippy::iter_with_drain,
49 clippy::large_include_file,
50 clippy::large_stack_frames,
51 clippy::let_underscore_untyped,
52 clippy::lossy_float_literal,
53 clippy::map_err_ignore,
54 clippy::map_with_unused_argument_over_ranges,
55 clippy::mem_forget,
56 clippy::missing_assert_message,
57 clippy::missing_asserts_for_indexing,
58 clippy::missing_const_for_fn,
59 clippy::missing_docs_in_private_items,
60 clippy::module_name_repetitions,
61 clippy::multiple_inherent_impl,
62 clippy::multiple_unsafe_ops_per_block,
63 clippy::mutex_atomic,
64 clippy::mutex_integer,
65 clippy::needless_collect,
66 clippy::needless_pass_by_ref_mut,
67 clippy::needless_raw_strings,
68 clippy::non_zero_suggestions,
69 clippy::nonstandard_macro_braces,
70 clippy::option_if_let_else,
71 clippy::or_fun_call,
72 clippy::panic_in_result_fn,
73 clippy::partial_pub_fields,
74 clippy::pathbuf_init_then_push,
75 clippy::pedantic,
76 clippy::print_stderr,
77 clippy::print_stdout,
78 clippy::pub_without_shorthand,
79 clippy::rc_buffer,
80 clippy::rc_mutex,
81 clippy::read_zero_byte_vec,
82 clippy::redundant_clone,
83 clippy::redundant_type_annotations,
84 clippy::renamed_function_params,
85 clippy::ref_patterns,
86 clippy::rest_pat_in_fully_bound_structs,
87 clippy::same_name_method,
88 clippy::semicolon_inside_block,
89 clippy::set_contains_or_insert,
90 clippy::shadow_unrelated,
91 clippy::significant_drop_in_scrutinee,
92 clippy::significant_drop_tightening,
93 clippy::str_to_string,
94 clippy::string_add,
95 clippy::string_lit_as_bytes,
96 clippy::string_lit_chars_any,
97 clippy::string_slice,
98 clippy::string_to_string,
99 clippy::suboptimal_flops,
100 clippy::suspicious_operation_groupings,
101 clippy::suspicious_xor_used_as_pow,
102 clippy::tests_outside_test_module,
103 clippy::todo,
104 clippy::too_long_first_doc_paragraph,
105 clippy::trailing_empty_array,
106 clippy::transmute_undefined_repr,
107 clippy::trivial_regex,
108 clippy::try_err,
109 clippy::undocumented_unsafe_blocks,
110 clippy::unimplemented,
111 clippy::uninhabited_references,
112 clippy::unnecessary_safety_comment,
113 clippy::unnecessary_safety_doc,
114 clippy::unnecessary_self_imports,
115 clippy::unnecessary_struct_initialization,
116 clippy::unneeded_field_pattern,
117 clippy::unused_peekable,
118 clippy::unused_result_ok,
119 clippy::unused_trait_names,
120 clippy::unwrap_in_result,
121 clippy::unwrap_used,
122 clippy::use_debug,
123 clippy::use_self,
124 clippy::useless_let_if_seq,
125 clippy::verbose_file_reads,
126 clippy::while_float,
127 clippy::wildcard_enum_match_arm,
128 explicit_outlives_requirements,
129 future_incompatible,
130 let_underscore_drop,
131 meta_variable_misuse,
132 missing_abi,
133 missing_copy_implementations,
134 missing_debug_implementations,
135 missing_docs,
136 redundant_lifetimes,
137 semicolon_in_expressions_from_macros,
138 single_use_lifetimes,
139 unit_bindings,
140 unnameable_types,
141 unreachable_pub,
142 unsafe_op_in_unsafe_fn,
143 unstable_features,
144 unused_crate_dependencies,
145 unused_extern_crates,
146 unused_import_braces,
147 unused_lifetimes,
148 unused_macro_rules,
149 unused_qualifications,
150 unused_results,
151 variant_size_differences
152)]
153
154mod trace;
155
156use std::{
157 error::Error,
158 sync::atomic::{AtomicUsize, Ordering},
159};
160
161use derive_more::with_trait::{AsMut, AsRef, Display};
162use sealed::sealed;
163
164#[doc(inline)]
165pub use self::trace::*;
166
167pub static DEFAULT_FRAMES_CAPACITY: AtomicUsize = AtomicUsize::new(10);
172
173#[derive(AsMut, AsRef, Clone, Debug, Display)]
175#[display("{err}")]
176pub struct Traced<E: ?Sized> {
177 trace: Trace,
179
180 #[as_mut]
182 #[as_ref]
183 err: E,
184}
185
186impl<E: ?Sized> Traced<E> {
187 #[must_use]
192 pub const fn trace(&self) -> &Trace {
193 &self.trace
194 }
195}
196
197impl<E> Traced<E> {
198 #[must_use]
201 pub fn into_inner(self) -> E {
202 self.err
203 }
204
205 #[must_use]
208 pub fn split(self) -> (E, Trace) {
209 (self.err, self.trace)
210 }
211
212 #[must_use]
215 pub const fn compose(error: E, trace: Trace) -> Self {
216 Self { err: error, trace }
217 }
218}
219
220impl<E> From<(E, Frame)> for Traced<E> {
221 fn from((err, f): (E, Frame)) -> Self {
222 err.wrap_traced(f)
223 }
224}
225
226impl<E> From<(E, Trace)> for Traced<E> {
227 fn from((err, trace): (E, Trace)) -> Self {
228 Self::compose(err, trace)
229 }
230}
231
232impl<E: Error + ?Sized> Error for Traced<E> {
244 fn source(&self) -> Option<&(dyn Error + 'static)> {
245 self.err.source()
246 }
247}
248
249#[sealed]
259pub trait WrapTraced<E> {
260 #[must_use]
263 fn wrap_traced(self, f: Frame) -> Traced<E>;
264}
265
266#[sealed]
267impl<E> WrapTraced<E> for E {
268 fn wrap_traced(self, f: Frame) -> Traced<Self> {
269 let mut trace = Trace::new(Vec::with_capacity(
270 DEFAULT_FRAMES_CAPACITY.load(Ordering::Relaxed),
271 ));
272 trace.push(f);
273 Traced { err: self, trace }
274 }
275}
276
277#[sealed]
278impl<E> WrapTraced<E> for Traced<E> {
279 fn wrap_traced(mut self, f: Frame) -> Self {
282 self.trace.push(f);
283 self
284 }
285}
286
287#[must_use]
296pub fn map_from<F, T: From<F>>(e: Traced<F>) -> Traced<T> {
297 Traced {
298 err: T::from(e.err),
299 trace: e.trace,
300 }
301}
302
303#[macro_export]
331macro_rules! new {
332 ($e:expr) => {
333 $crate::WrapTraced::wrap_traced($e, $crate::new_frame!())
334 };
335}
336
337#[macro_export]
353macro_rules! map_from_and_new {
354 ($e:expr) => {
355 $crate::new!($crate::map_from($e))
356 };
357}
358
359#[macro_export]
378macro_rules! wrap {
379 () => ($crate::wrap!(_ => _));
380 ($from:ty) => ($crate::wrap!($from => _));
381 (=> $to:ty) => ($crate::wrap!(_ => $to));
382 ($from:ty => $to:ty) => {
383 |err: $from| -> $crate::Traced<$to> {
384 $crate::new!(err)
385 }
386 };
387}
388
389#[macro_export]
403macro_rules! map_from_and_wrap {
404 () => ($crate::map_from_and_wrap!(_ => _));
405 ($from:ty) => ($crate::map_from_and_wrap!($from => _));
406 (=> $to:ty) => ($crate::map_from_and_wrap!(_ => $to));
407 ($from:ty => $to:ty) => {
408 |err: $crate::Traced<$from>| -> $crate::Traced<$to> {
409 $crate::new!($crate::map_from::<$from, $to>(err))
410 }
411 };
412}
413
414#[macro_export]
430macro_rules! from_and_wrap {
431 () => ($crate::from_and_wrap!(_ => _));
432 ($from:ty) => ($crate::from_and_wrap!($from => _));
433 (=> $to:ty) => ($crate::from_and_wrap!(_ => $to));
434 ($from:ty => $to:ty) => {
435 |err: $from| -> $crate::Traced<$to> {
436 $crate::map_from::<$from, $to>($crate::new!(err))
437 }
438 };
439}
440
441#[cfg(test)]
442mod new_macro_spec {
443 use super::Traced;
444
445 #[test]
446 fn creates_new_trace_frame_on_initialization() {
447 let err = super::new!(());
448 assert_eq!(err.trace.len(), 1, "creates initial frame");
449 }
450
451 #[test]
452 fn fills_trace_on_subsequent_calls() {
453 let err = super::new!(());
454 let err = super::new!(err);
455 let err = super::new!(err);
456 let err: Traced<()> = super::new!(err);
457 assert_eq!(err.trace.len(), 4, "trace growths with each call");
458 }
459}
460
461#[cfg(test)]
462mod map_from_and_new_macro_spec {
463 use super::Traced;
464
465 #[test]
466 fn fills_trace_on_subsequent_calls() {
467 let err = super::new!(());
468 let err = super::map_from_and_new!(err);
469 let err = super::map_from_and_new!(err);
470 let err: Traced<()> = super::map_from_and_new!(err);
471 assert_eq!(err.trace.len(), 4, "trace growths with each call");
472 }
473
474 #[test]
475 fn applies_required_from_implementation() {
476 let err = super::new!(4u8);
477 let err: Traced<u32> = super::map_from_and_new!(err);
478 assert!(!err.trace.is_empty(), "captures frames");
479 }
480}
481
482#[cfg(test)]
483mod wrap_macro_spec {
484 use super::Traced;
485
486 #[test]
487 fn creates_new_trace_frame_on_initialization() {
488 let res: Result<(), ()> = Err(());
489 let err = res.map_err(super::wrap!()).unwrap_err();
490 assert_eq!(err.trace.len(), 1, "creates initial frame");
491 }
492
493 #[test]
494 fn fills_trace_on_subsequent_calls() {
495 let res: Result<(), ()> = Err(());
496 let res = res.map_err(super::wrap!());
497 let res = res.map_err(super::wrap!());
498 let res = res.map_err(super::wrap!(Traced<_>));
499 let err = res.map_err(super::wrap!(=> ())).unwrap_err();
500 assert_eq!(err.trace.len(), 4, "trace growths with each call");
501 }
502}
503
504#[cfg(test)]
505mod map_from_and_wrap_macro_spec {
506 use super::Traced;
507
508 #[test]
509 fn fills_trace_on_subsequent_calls() {
510 let res: Result<(), ()> = Err(());
511 let res = res.map_err(super::wrap!());
512 let res = res.map_err(super::map_from_and_wrap!());
513 let res = res.map_err(super::map_from_and_wrap!());
514 let err = res.map_err(super::map_from_and_wrap!(=> ())).unwrap_err();
515 assert_eq!(err.trace.len(), 4, "trace growths with each call");
516 }
517
518 #[test]
519 fn applies_required_from_implementation() {
520 let res: Result<(), u8> = Err(54);
521 let res = res.map_err(super::wrap!());
522 let err: Traced<u64> =
523 res.map_err(super::map_from_and_wrap!()).unwrap_err();
524 assert!(!err.trace.is_empty(), "captures frames");
525 }
526}
527
528#[cfg(test)]
529mod from_and_wrap_macro_spec {
530 use super::Traced;
531
532 #[test]
533 fn fills_trace_on_subsequent_calls() {
534 let res: Result<(), ()> = Err(());
535 let res = res.map_err(super::wrap!());
536 let res = res.map_err(super::from_and_wrap!());
537 let res = res.map_err(super::from_and_wrap!());
538 let err = res.map_err(super::from_and_wrap!(=> ())).unwrap_err();
539 assert_eq!(err.trace.len(), 4, "trace growths with each call");
540 }
541
542 #[test]
543 fn applies_required_from_implementation() {
544 let res: Result<(), u8> = Err(54);
545 let err: Traced<u64> =
546 res.map_err(super::from_and_wrap!()).unwrap_err();
547 assert!(!err.trace.is_empty(), "captures frames");
548 }
549}