el_macro/bind/
mod.rs

1//! The [`crate::bind!`] macro and related [`crate::bind::IntoResult`] trait
2
3
4mod into_result;
5
6#[cfg(test)]
7mod test;
8
9
10pub use into_result::IntoResult;
11
12
13/// Binds the unwrapped value.
14///
15/// Provides the ability to write concise code to get the value or get goin' in a context where
16/// [ErrorPropagationExpression (`?`)](https://doc.rust-lang.org/reference/expressions/operator-expr.html#r-expr.try)
17/// is not sufficient, such as when controlling execution flow with `break` or `continue`,
18/// or not applicable, as in function that does not return [`Result`] or [`Option`].
19///
20/// [Tests](IntoResult) whether the value of the provided expression can be unwrapped.
21/// Creates a variable binding if the value can be unwrapped. Otherwise, executes
22/// the error handler and evaluates the execution flow control expression.
23///
24/// # Syntax
25///
26/// ```text
27/// bind!([mut] <var-name> [= <value-expr>], or [<err-handler>,] <flow-ctl>);
28/// ```
29///
30/// - `mut` — indicator keyword to make the binding mutable.
31/// - `<var-name>` — name of the newly created variable.
32/// - `<value-expr>` — expression whose value is [being tested](IntoResult) to contain
33///   an unwrappable value. If not specified, the existing value of the variable `<var-name>`
34///   will be used to create new variable with the same name.
35/// - `<err-handler>` — optional error handler that is called if there's no value to unwrap,
36///   with error object passed as the only argument.
37/// - `<flow-ctl>` — expression used to control the execution flow in a case
38///   when there's no value to unwrap.
39///
40/// # Examples
41///
42/// Basic usage:
43/// ```
44/// # use el_macro::bind;
45/// #
46/// // binds `x` to the value 42, does not return
47/// bind!(x = Some(42), or return);
48/// assert_eq!(x, 42);
49///
50/// // creates a mutable binding `x` to the value 42, does not return
51/// bind!(mut x = Some(42), or return);
52/// x += 3;
53/// assert_eq!(x, 45);
54///
55/// // returns
56/// bind!(x = None::<i32>, or return);
57/// unreachable!();
58/// // returns as well, why wouldn't it
59/// bind!(mut x = None::<i32>, or return);
60/// unreachable!();
61/// ```
62///
63/// Handling error values:
64/// ```
65/// # use el_macro::bind;
66/// #
67/// let okish = Some(42).ok_or("error");
68/// let errorish = None::<i32>.ok_or("error");
69///
70/// let handle_error = |err: &str| eprintln!("{err}!");
71///
72/// // binds `x` to the value 42, does not return
73/// bind!(x = okish, or handle_error, return);
74/// assert_eq!(x, 42);
75///
76/// 'omit_handler: {
77///     bind!(x = None::<i32>, or {
78///         // it's possible to omit the error handler
79///         // and perform handling in the flow control block
80///         eprintln!("no value for x");
81///         // you can control the execution flow however you like
82///         break 'omit_handler
83///     });
84///     unreachable!();
85/// }
86///
87/// // prints 'error!' and returns
88/// bind!(x = errorish, or handle_error, return);
89/// unreachable!();
90/// ```
91///
92/// Omitting the `<value-expr>`:
93/// ```
94/// # use el_macro::bind;
95/// #
96/// let x = Some(42);
97/// bind!(x /* = x */, or return);
98/// assert_eq!(x, 42);
99/// ```
100///
101/// Using with a custom type:
102/// ```
103/// # use el_macro::{bind, bind::IntoResult};
104/// #
105/// struct NegativeIsError(i32);
106///
107/// // returns some external descriptor on success,
108/// // negative number on failure
109/// fn external_get_descriptor(must_succeed: bool) -> i32 {
110///     // actual external call here
111///     if must_succeed { 42 } else { -1 }
112/// }
113///
114/// // successfully binds `x` to the value 42
115/// bind!(x = NegativeIsError(external_get_descriptor(true)), or return);
116/// assert_eq!(x, 42);
117///
118/// // prints 'error -1: unknown error' and returns
119/// bind!(x = NegativeIsError(external_get_descriptor(false)), or print_error, return);
120/// unreachable!();
121///
122/// // specifies how to determine whether `NegativeIsError`
123/// // contains a valid descriptor or an error
124/// impl IntoResult for NegativeIsError {
125///
126///     type Value = i32;
127///     type Error = ExternalCallError;
128///
129///     fn into_result(self) -> Result<Self::Value, Self::Error> {
130///         (self.0 >= 0)
131///             .then_some(self.0)
132///             .ok_or(ExternalCallError {
133///                 code: self.0,
134///                 desc: get_error_desc(self.0),
135///             })
136///     }
137///
138/// }
139///
140/// struct ExternalCallError {
141///     code: i32,
142///     desc: String,
143/// }
144///
145/// fn print_error(err: ExternalCallError) {
146///     let ExternalCallError { code, desc, .. } = err;
147///     eprintln!("error {code}: {desc}");
148/// }
149///
150/// fn get_error_desc(error_code: i32) -> String {
151///     if error_code >= 0 { "no error" } else { "unknown error "}.into()
152/// }
153/// ```
154#[macro_export]
155macro_rules! bind {
156
157    ($n: ident = $e: expr, or $f: expr) => {
158        let $n = {
159            use $crate::bind::IntoResult;
160            match $e.into_result() {
161                Ok(val) => val,
162                Err(_) => $f,
163            }
164        };
165    };
166
167    (mut $n: ident = $e: expr, or $f: expr) => {
168        let mut $n = {
169            $crate::bind!($n = $e, or $f);
170            $n
171        };
172    };
173
174    ($n: ident, or $f: expr) => {
175        $crate::bind!($n = $n, or $f);
176    };
177
178    (mut $n: ident, or $f: expr) => {
179        $crate::bind!(mut $n = $n, or $f);
180    };
181
182    ($n: ident = $e: expr, or $h: expr, $f: expr) => {
183        let $n = {
184            use $crate::bind::IntoResult;
185            match $e.into_result() {
186                Ok($n) => { $n },
187                Err(err) => {
188                    $h(err);
189                    $f
190                },
191            }
192        };
193    };
194
195    (mut $n: ident = $e: expr, or $h: expr, $f: expr) => {
196        let mut $n = {
197            $crate::bind!($n = $e, or $h, $f);
198            $n
199        };
200    };
201
202    ($n: ident, or $h: expr, $f: expr) => {
203        $crate::bind!($n = $n, or $h, $f);
204    };
205
206    (mut $n: ident, or $h: expr, $f: expr) => {
207        $crate::bind!(mut $n = $n, or $h, $f);
208    };
209
210}