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}