macro_map/
lib.rs

1//! A small toolbelt of macros that implement the [`Option::ok_or_else`] and
2//! [`Result::map_err`] with macros instead of functions taking callbacks.
3//!
4//! This reduces the boilerplate when you can't use the abovementioned functions
5//! because the error condition handling logic you need requires you that
6//! you move certain values into the closures which you can't affort.
7//!
8//! People would normally work around it by `.clone()`-ing the value
9//! to be passed into the closure, but that's bad because:
10//!
11//! - you need to `.clone()` the item, and that's not always possible;
12//! - the `.clone()` is not really necessary, you can rewrite the code with
13//!   a manual `match` that would not require and ownership transfers.
14//!
15//! But writing a match is vebose, and who wants that?!
16//! This is where this crate comes to help.
17//!
18//! For best look and feel, combine with
19//! [`postfix-macros`](https://docs.rs/postfix-macros) crate.
20
21/// An analog to [`Result::map_err`] but without a closure.
22///
23/// ```
24/// use macro_map::map_err;
25///
26/// let myresult: Result<&str, &str> = Err("hello");
27///
28/// let mapped = map_err!(myresult, |myerr| 123);
29///
30/// assert_eq!(mapped, Err(123));
31/// ```
32///
33/// Or with [`postfix-macros`](https://docs.rs/postfix-macros):
34///
35/// ```
36/// use macro_map::map_err;
37/// use postfix_macros::postfix_macros;
38///
39/// let myresult: Result<&str, &str> = Err("hello");
40///
41/// postfix_macros! {
42///   let mapped = myresult.map_err!(|myerr| 123);
43/// }
44///
45/// assert_eq!(mapped, Err(123));
46/// ```
47#[macro_export]
48macro_rules! map_err {
49    ($result:expr, |$err:pat_param| $closure:expr) => {
50        match $result {
51            Ok(val) => Ok(val),
52            Err($err) => Err($closure),
53        }
54    };
55}
56
57/// An analog to [`Option::ok_or_else`] but without a closure.
58///
59/// ```
60/// use macro_map::ok_or_else;
61///
62/// let myoption: Option<&str> = None;
63///
64/// let mapped = ok_or_else!(myoption, || 123);
65///
66/// assert_eq!(mapped, Err(123));
67/// ```
68///
69/// Or with [`postfix-macros`](https://docs.rs/postfix-macros):
70///
71/// ```
72/// use macro_map::ok_or_else;
73/// use postfix_macros::postfix_macros;
74///
75/// let myoption: Option<&str> = None;
76///
77/// postfix_macros! {
78///   let mapped = ok_or_else!(myoption, || 123);
79/// }
80///
81/// assert_eq!(mapped, Err(123));
82/// ```
83#[macro_export]
84macro_rules! ok_or_else {
85    ($result:expr, || $closure:expr) => {
86        match $result {
87            Some(val) => Ok(val),
88            None => Err($closure),
89        }
90    };
91}
92
93/// An analog to [`Result::map_err`] followed by a `?` but without a closure.
94///
95/// ```
96/// use macro_map::try_map_err;
97///
98/// fn myfn() -> Result<(), i64> {
99///     let myresult: Result<&str, &str> = Err("hello");
100///     try_map_err!(myresult, |myerr| 123);
101///     Ok(())
102/// }
103///
104/// let mapped = myfn();
105///
106/// assert_eq!(mapped, Err(123));
107/// ```
108///
109/// Or with [`postfix-macros`](https://docs.rs/postfix-macros):
110///
111/// ```
112/// use macro_map::try_map_err;
113/// use postfix_macros::postfix_macros;
114///
115/// fn myfn() -> Result<(), i64> {
116///     postfix_macros! {
117///         let myresult: Result<&str, &str> = Err("hello");
118///         myresult.try_map_err!(|myerr| 123);
119///         Ok(())
120///     }
121/// }
122///
123///
124/// let mapped = myfn();
125///
126/// assert_eq!(mapped, Err(123));
127/// ```
128#[macro_export]
129macro_rules! try_map_err {
130    ($result:expr, |$err:pat_param| $closure:expr) => {
131        match $result {
132            Ok(val) => val,
133            Err($err) => return Err($closure.into()),
134        }
135    };
136}
137
138/// An analog to [`Result::map_err`] followed by a `?` but without a closure.
139///
140/// ```
141/// use macro_map::try_ok_or_else;
142///
143/// fn myfn() -> Result<(), i64> {
144///     let myoption: Option<&str> = None;
145///     try_ok_or_else!(myoption, || 123);
146///     Ok(())
147/// }
148///
149/// let mapped = myfn();
150///
151/// assert_eq!(mapped, Err(123));
152/// ```
153///
154/// Or with [`postfix-macros`](https://docs.rs/postfix-macros):
155///
156/// ```
157/// use macro_map::try_ok_or_else;
158/// use postfix_macros::postfix_macros;
159///
160/// fn myfn() -> Result<(), i64> {
161///     postfix_macros! {
162///         let myoption: Option<&str> = None;
163///         myoption.try_ok_or_else!(|| 123);
164///         Ok(())
165///     }
166/// }
167///
168///
169/// let mapped = myfn();
170///
171/// assert_eq!(mapped, Err(123));
172/// ```
173#[macro_export]
174macro_rules! try_ok_or_else {
175    ($result:expr, || $closure:expr) => {
176        match $result {
177            Some(val) => val,
178            None => return Err($closure.into()),
179        }
180    };
181}
182
183#[cfg(test)]
184mod tests {
185    use super::*;
186
187    use postfix_macros::postfix_macros;
188
189    #[derive(Debug)]
190    struct MyNonCopy;
191
192    fn result(a: MyNonCopy, cond: Result<(), ()>) -> Result<MyNonCopy, (MyNonCopy,)> {
193        postfix_macros! {
194            cond.try_map_err!(|_| (a,));
195            Ok(a)
196        }
197    }
198
199    fn option(a: MyNonCopy, cond: Option<()>) -> Result<MyNonCopy, (MyNonCopy,)> {
200        postfix_macros! {
201            cond.try_ok_or_else!(|| (a,));
202            Ok(a)
203        }
204    }
205
206    #[test]
207    fn test_result() {
208        result(MyNonCopy, Ok(())).unwrap();
209        result(MyNonCopy, Err(())).unwrap_err();
210    }
211
212    #[test]
213    fn test_option() {
214        option(MyNonCopy, Some(())).unwrap();
215        option(MyNonCopy, None).unwrap_err();
216    }
217}