el_macro/bind/
mod.rs

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