match_opt/
lib.rs

1/// Returns `Some(output)` in case the given expression matches one of the given patterns
2/// of the form `pattern => output`, and None otherwise.
3///
4/// Like in a `match` expression, the pattern can be optionally followed by `if`
5/// and a guard expression that has access to names bound by the pattern. There can be several patterns.
6///
7/// # Examples
8///
9/// ```
10/// use match_opt::match_opt;
11/// let foo = 'f';
12/// let some_true = match_opt!(foo, 'A'..='Z' | 'a'..='z' => true);
13///
14/// let bar = Some(4);
15/// let some_42 = match_opt!(bar, Some(x) if x > 2 => 42);
16/// ```
17#[macro_export]
18macro_rules! match_opt {
19    ($expression:expr, $( $( $pattern:pat_param )|+ $( if $guard: expr )? $(,)? => $output:expr ),* ) => {
20        match $expression {
21            $(  $( $pattern )|+ $( if $guard )? => Some($output), )*
22            _ => None,
23        }
24    }
25}
26
27#[cfg(test)]
28mod tests {
29    #[test]
30    fn cond_opt_positive_one() {
31        let result = |v| match_opt!(v,  x if x > 0 => 42);
32        assert_eq!(result(5), Some(42));
33        assert_eq!(result(0), None);
34    }
35
36    #[test]
37    fn cond_opt_positive_several() {
38        let result = |v| match_opt!(v, y if y > 5 => y + 1, x if x > 0 => 42);
39        assert_eq!(result(5), Some(42));
40        assert_eq!(result(7), Some(8));
41        assert_eq!(result(0), None);
42    }
43
44    #[test]
45    fn cond_opt_positive_none() {
46        let result = |v| match_opt!(v,);
47        // type inference requires typing the output
48        let foo: Option<usize> = result(5);
49        assert_eq!(foo, None);
50    }
51
52    #[test]
53    fn cond_opt_positive_many() {
54        let result = |v| match_opt!(v,  z if z > 10 => z -1, y if y > 5 => y + 1, x if x > 0 => 42);
55        assert_eq!(result(5), Some(42));
56        assert_eq!(result(7), Some(8));
57        assert_eq!(result(11), Some(10));
58        assert_eq!(result(0), None);
59    }
60}