neuer_error/
macros.rs

1//! Macros for the users.
2
3/// Create a helper trait `NeuErrAttachments` that is implemented for
4/// [`NeuErr`](crate::NeuErr), which allows to directly retrieve your attachments. You can
5/// modify visibility and name by re-exporting via `pub use` if needed.
6///
7/// This improves discoverability and allows you to unwrap potential new-types you might have had to
8/// use (or wanted to use).
9///
10/// ## Usage
11///
12/// Simple getters without type transformation:
13///
14/// ```rust
15/// # use neuer_error::provided_attachments;
16/// #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
17/// enum Retryable { Yes, No }
18///
19/// provided_attachments!(
20/// 	retryable(single: Retryable) -> Option<&Retryable> { |v| v };
21/// );
22/// ```
23///
24/// This will create a method `fn retryable(&self) -> Option<&Retryable>` on `NeuErr`.
25///
26/// You can also make use of the transformation expression that will be applied to the attachment
27/// before returning it:
28///
29/// ```rust
30/// # use neuer_error::provided_attachments;
31/// #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
32/// enum Retryable { Yes, No }
33///
34/// provided_attachments!(
35/// 	retryable(single: Retryable) -> Retryable { |retry| retry.copied().unwrap_or(Retryable::No) };
36/// );
37/// ```
38///
39/// This will create a method `fn retryable(&self) -> Retryable` on `NeuErr`. The closure receives
40/// the `Option<&Retryable>` and returns a `Retryable`.
41///
42/// Finally, you can also retrieve multiple attachments of the same type and transform the iterator
43/// into your return type:
44///
45/// ```rust
46/// # use neuer_error::provided_attachments;
47/// #[derive(Debug, PartialEq, Clone)]
48/// struct UserInfo(String);
49///
50/// provided_attachments!(
51/// 	user_info(multiple: UserInfo) -> String { |iter| iter.map(|UserInfo(s)| s.as_str()).collect() };
52/// );
53/// ```
54///
55/// This will create a method `fn user_info(&self) -> String` on `NeuErr`, which collects all
56/// `UserInfo` attachments, unpacks them and collects them into a single `String`.
57#[macro_export]
58macro_rules! provided_attachments {
59	// Declare rule for single attachment.
60	(@declare $getter_name:ident (single: $attachment_type:ty) -> $return_type:ty {
61		// Transformation closure, receiving type Option<&$attachment_type> and returning $return_type.
62		|$bind:ident| $transform:expr
63	}) => {
64		#[doc = concat!("Get attachment `", stringify!($getter_name), "` via type `", stringify!($attachment_type), "` from the error.")]
65		fn $getter_name(&self) -> $return_type;
66	};
67
68	// Implement rule for single attachment.
69	(@implement $getter_name:ident (single: $attachment_type:ty) -> $return_type:ty {
70		// Transformation closure, receiving type Option<&$attachment_type> and returning $return_type.
71		|$bind:ident| $transform:expr
72	}) => {
73		fn $getter_name(&self) -> $return_type {
74			let $bind = Self::attachment::<$attachment_type>(self);
75			$transform
76		}
77	};
78
79	// Declare rule for multiple attachment.
80	(@declare $getter_name:ident (multiple: $attachment_type:ty) -> $return_type:ty {
81		// Transformation closure, receiving type impl Iterator<Item = &$attachment_type> and returning $return_type.
82		|$bind:ident| $transform:expr
83	}) => {
84		#[doc = concat!("Get attachment `", stringify!($getter_name), "` via type `", stringify!($attachment_type), "` from the error.")]
85		fn $getter_name(&self) -> $return_type;
86	};
87
88	// Implement rule for multiple attachment.
89	(@implement $getter_name:ident (multiple: $attachment_type:ty) -> $return_type:ty {
90		// Transformation closure, receiving type impl Iterator<Item = &$attachment_type> and returning $return_type.
91		|$bind:ident| $transform:expr
92	}) => {
93		fn $getter_name(&self) -> $return_type {
94			let $bind = Self::attachments::<$attachment_type>(self);
95			$transform
96		}
97	};
98
99	// Main matcher, splitting into attachment list.
100	($(
101		$getter_name:ident ($multiplicity_matcher:ident : $attachment_type:ty) -> $return_type:ty { |$bind:ident| $transform:expr }
102	);* $(;)?) => {
103		#[doc = "Helper trait that is implemented for [`NeuErr`], which allows to comfortably retrieve typed context information."]
104		pub trait NeuErrAttachments {
105			$(
106				$crate::provided_attachments!(@declare $getter_name($multiplicity_matcher: $attachment_type) -> $return_type {
107					|$bind| $transform
108				});
109			)*
110		}
111
112		impl NeuErrAttachments for $crate::NeuErr {
113			$(
114				$crate::provided_attachments!(@implement $getter_name($multiplicity_matcher: $attachment_type) -> $return_type {
115					|$bind| $transform
116				});
117			)*
118		}
119	};
120}