grit_junk_drawer/
lib.rs

1
2
3//MACRO chain_match!
4/// Recursively match one or more patterns, each with optional guard clauses,
5/// and resolve to the final expresion or statement if all matches succeed, else resolve to the previous default
6/// expression or statement
7/// 
8/// The syntax is as follows:
9/// ```ignore
10/// where (default) is literally typing out (default)
11/// where <DEFAULT_DEFINITION>     = (default) <pattern>, (<expression>|<statement>);
12/// where <MATCH_DEFINITION>       = <expression>, <pattern> (if <expression>)? =>
13/// where <FINAL_MATCH_DEFINITION> = <MATCH_DEFINITION> (<expression>|<statement>)
14/// 
15/// chain_match!(
16///   <DEFAULT_DEFINITION>
17///   [<DEFAULT_DEFINITION> | <MATCH_DEFINITION>]*
18///   <FINAL_MATCH_DEFINITION>
19/// )
20/// ```
21/// ### Examples
22/// ```rust
23/// use grit_junk_drawer::chain_match;
24/// 
25/// type Deep = Option<Result<Option<Result<u64,()>>,()>>;
26/// type Simple = Option<u64>;
27/// type Complex = Option<(bool, Option<bool>)>;
28/// 
29/// fn main() -> Result<(), String> {
30///     let my_deep: Deep = Some(Ok(Some(Ok(42))));
31///     let my_simple: Simple = Some(69);
32///     let my_complex: Complex = Some((true, Some(false)));
33/// 
34///     // expression on pass, return static on fail
35///     let my_deep_val = chain_match!(
36///         (default) _, return Err(format!("expected 42"));
37///         my_deep, Some(res_1) =>
38///         res_1, Ok(opt_2) =>
39///         opt_2, Some(res_2) =>
40///         res_2, Ok(val) if val == 42 => val
41///     );
42///     assert_eq!(my_deep_val, 42);
43/// 
44///     // expression on pass, return dynamic on fail
45///     let my_simple_val = chain_match!(
46///         (default) invalid, return Err(format!("expected 69, got {:?}", invalid));
47///         my_simple, Some(val) if val == 69 => val
48///     );
49///     assert_eq!(my_simple_val, 69);
50/// 
51///     // expression on pass, static expression on fail
52///     let my_simple_result = chain_match!(
53///         (default) _, Err(format!("expected 70"));
54///         my_simple, Some(val) if val == 70 => Ok(val)
55///     );
56///     assert_eq!(my_simple_result, Err(format!("expected 70")));
57/// 
58///     // expression on pass, dynamic expression on fail
59///     let my_simple_result = chain_match!(
60///         (default) invalid, Err(invalid);
61///         my_simple, Some(val) if val == 70 => Ok(val)
62///     );
63///     assert_eq!(my_simple_result, Err(Some(69)));
64/// 
65///     // change default path midway
66///     let my_complex_result = chain_match!(
67///         (default) _, Err(format!("First check false"));
68///         my_complex, Some((true, next_option)) =>
69///         (default) _, Err(format!("Second check false"));
70///         next_option, Some(true) => Ok(format!("Both checks pass"))
71///     );
72///     assert_eq!(my_complex_result, Err(format!("Second check false")));
73/// 
74///     // execute statement instead of resolving to expression
75///     let mut it_passed: Option<bool> = None;
76///     chain_match!(
77///         (default) _, it_passed = Some(false);
78///         my_simple, Some(val) if val == 69 => it_passed = Some(true)
79///     );
80///     assert_eq!(it_passed, Some(true));
81/// 
82///     // expression on fail, statement on pass
83///     let main_result = chain_match!(
84///         (default) _, Err(format!("Program failed"));
85///         my_simple, Some(val) if val == 69 => return Ok(())
86///     );
87/// 
88///     return main_result
89/// }
90/// ```
91#[macro_export]
92macro_rules! chain_match {
93    // Final Match: Fail Expression, Pass Expression
94    ((default) $fail_pat:pat, $fail_expr:expr; $match_expr:expr, $pass_pat:pat $( if $guard:expr )? => $pass_expr:expr) => {
95        match $match_expr {
96            $pass_pat $( if $guard )? => $pass_expr,
97            $fail_pat => $fail_expr
98        }
99    };
100    // Final Match: Fail Expression, Pass Statement
101    ((default) $fail_pat:pat, $fail_expr:expr; $match_expr:expr, $pass_pat:pat $( if $guard:expr )? => $pass_stmt:stmt) => {
102        match $match_expr {
103            $pass_pat $( if $guard )? => $pass_stmt,
104            $fail_pat => $fail_expr
105        }
106    };
107    // Final Match: Fail Statement, Pass Expression
108    ((default) $fail_pat:pat, $fail_stmt:stmt; $match_expr:expr, $pass_pat:pat $( if $guard:expr )? => $pass_expr:expr) => {
109        match $match_expr {
110            $pass_pat $( if $guard )? => $pass_expr,
111            $fail_pat => $fail_stmt
112        }
113    };
114    // Final Match: Fail Statement, Pass Statement
115    ((default) $fail_pat:pat, $fail_stmt:stmt; $match_expr:expr, $pass_pat:pat $( if $guard:expr )? => $pass_stmt:stmt) => {
116        match $match_expr {
117            $pass_pat $( if $guard )? => $pass_stmt,
118            $fail_pat => $fail_stmt
119        }
120    };
121    // Recursive Match: Fail Expression, New Fail Expression
122    ((default) $fail_pat:pat, $fail_expr:expr; $match_expr:expr, $pass_pat:pat $( if $guard:expr )? => (default) $new_fail_pat:pat, $new_fail_expr:expr; $($tail:tt)*) => {
123        match $match_expr {
124            $pass_pat $( if $guard )? => chain_match!((default) $new_fail_pat, $new_fail_expr; $($tail)*),
125            $fail_pat => $fail_expr
126        }
127    };
128    // Recursive Match: Fail Expression, New Fail Statement
129    ((default) $fail_pat:pat, $fail_expr:expr; $match_expr:expr, $pass_pat:pat $( if $guard:expr )? => (default) $new_fail_pat:pat, $new_fail_stmt:stmt; $($tail:tt)*) => {
130        match $match_expr {
131            $pass_pat $( if $guard )? => chain_match!((default) $new_fail_pat, $new_fail_stmt; $($tail)*),
132            $fail_pat => $fail_expr
133        }
134    };
135    // Recursive Match: Fail Expression, Same Fail Expression
136    ((default) $fail_pat:pat, $fail_expr:expr; $match_expr:expr, $pass_pat:pat $( if $guard:expr )? => $($tail:tt)*) => {
137        match $match_expr {
138            $pass_pat $( if $guard )? => chain_match!((default) $fail_pat, $fail_expr; $($tail)*),
139            $fail_pat => $fail_expr
140        }
141    };
142    // Recursive Match: Fail Statement, New Fail Expression
143    ((default) $fail_pat:pat, $fail_stmt:stmt; $match_expr:expr, $pass_pat:pat $( if $guard:expr )? => (default) $new_fail_pat:pat, $new_fail_expr:expr; $($tail:tt)*) => {
144        match $match_expr {
145            $pass_pat $( if $guard )? => chain_match!((default) $new_fail_pat, $new_fail_expr; $($tail)*),
146            $fail_pat => $fail_stmt
147        }
148    };
149    // Recursive Match: Fail Statement, New Fail Statement
150    ((default) $fail_pat:pat, $fail_stmt:stmt; $match_expr:expr, $pass_pat:pat $( if $guard:expr )? => (default) $new_fail_pat:pat, $new_fail_stmt:stmt; $($tail:tt)*) => {
151        match $match_expr {
152            $pass_pat $( if $guard )? => chain_match!((default) $new_fail_pat, $new_fail_stmt; $($tail)*),
153            $fail_pat => $fail_stmt
154        }
155    };
156    // Recursive Match: Fail Statement, Same Fail Statement
157    ((default) $fail_pat:pat, $fail_stmt:stmt; $match_expr:expr, $pass_pat:pat $( if $guard:expr )? => $($tail:tt)*) => {
158        match $match_expr {
159            $pass_pat $( if $guard )? => chain_match!((default) $fail_pat, $fail_stmt; $($tail)*),
160            $fail_pat => $fail_stmt
161        }
162    };
163}