diff options
Diffstat (limited to 'improved_slice_patterns/src/lib.rs')
-rw-r--r-- | improved_slice_patterns/src/lib.rs | 316 |
1 files changed, 0 insertions, 316 deletions
diff --git a/improved_slice_patterns/src/lib.rs b/improved_slice_patterns/src/lib.rs deleted file mode 100644 index 56ea54a..0000000 --- a/improved_slice_patterns/src/lib.rs +++ /dev/null @@ -1,316 +0,0 @@ -#![doc(html_root_url = "https://docs.rs/improved_slice_patterns/2.0.0")] - -//! A tiny crate that provides two macros to help matching -//! on `Vec`s and iterators using the syntax of -//! [`slice_patterns`][slice_patterns] -//! -//! [slice_patterns]: https://doc.rust-lang.org/nightly/unstable-book/language-features/slice-patterns.html - -/// Destructure an iterator using the syntax of slice_patterns. -/// -/// Wraps the match body in `Some` if there was a match; returns -/// `None` otherwise. -/// -/// Contrary to slice_patterns, this allows moving out -/// of the iterator. -/// -/// A variable length pattern (`x @ ..`) is only allowed as the last -/// pattern, unless the iterator is double-ended. -/// -/// Example: -/// ```edition2018 -/// use improved_slice_patterns::destructure_iter; -/// -/// let vec = vec![Some(1), Some(2), Some(3), None]; -/// -/// let res = destructure_iter!(vec.into_iter(); -/// [Some(x), y @ .., z] => { -/// // x: usize -/// // y: impl Iterator<Option<usize>> -/// // z: Option<usize> -/// (x, y.collect::<Vec<_>>(), z) -/// } -/// ); -/// -/// assert_eq!(res, Some((1, vec![Some(2), Some(3)], None))); -/// -/// # Ok::<(), ()>(()) -/// ``` -/// -/// -#[macro_export] -macro_rules! destructure_iter { - // Variable length pattern - (@match_forwards, $iter:expr, ($body:expr), - $x:ident @ .., $($rest:tt)*) => { - $crate::destructure_iter!(@match_backwards, - $iter, - ({ - let $x = $iter; - $body - }), - $($rest)* - ) - }; - // Special variable length pattern with a common unary variant - (@match_forwards, $iter:expr, ($body:expr), - $variant:ident ($x:ident).., $($rest:tt)*) => { - $crate::destructure_iter!(@match_backwards, - $iter, - ({ - let $x = $iter - .map(|x| match x { - $variant(y) => y, - _ => unreachable!(), - }); - $body - }), - $($rest)* - ) - }; - // Variable length pattern without a binder - (@match_forwards, $iter:expr, ($body:expr), .., $($rest:tt)*) => { - $crate::destructure_iter!(@match_backwards, - $iter, - ($body), - $($rest)* - ) - }; - // Single item pattern - (@match_forwards, $iter:expr, ($body:expr), $x:pat, $($rest:tt)*) => { - if let std::option::Option::Some($x) = $iter.next() { - $crate::destructure_iter!(@match_forwards, - $iter, - ($body), - $($rest)* - ) - } else { - std::option::Option::None - } - }; - // Single item pattern after a variable length one: declare reversed and take from the end - (@match_backwards, $iter:expr, ($body:expr), $x:pat, $($rest:tt)*) => { - $crate::destructure_iter!(@match_backwards, $iter, ( - if let std::option::Option::Some($x) = $iter.next_back() { - $body - } else { - std::option::Option::None - } - ), $($rest)*) - }; - - // Check no elements remain - (@match_forwards, $iter:expr, ($body:expr) $(,)*) => { - if $iter.next().is_some() { - std::option::Option::None - } else { - $body - } - }; - // After a variable length pattern, everything has already been consumed - (@match_backwards, $iter:expr, ($body:expr) $(,)*) => { - $body - }; - - ($iter:expr; [$($args:tt)*] => $body:expr) => { - { - #[allow(unused_mut)] - let mut iter = $iter; - $crate::destructure_iter!(@match_forwards, - iter, - (std::option::Option::Some($body)), - $($args)*, - ) - } - }; -} - -/// Pattern-match on a vec using the syntax of slice_patterns. -/// -/// Wraps the match body in `Ok` if there was a match; returns -/// an `Err` containing the ownership of the vec otherwise. -/// -/// Contrary to slice_patterns, this allows moving out -/// of the `Vec`. -/// -/// A variable length pattern (`x @ ..`) returns an iterator. -/// -/// Example: -/// ```edition2018 -/// #![feature(slice_patterns)] -/// use improved_slice_patterns::match_vec; -/// -/// let vec = vec![Some(1), Some(2), Some(3), None]; -/// -/// let res = match_vec!(vec; -/// [Some(_), y @ .., None] => { -/// y.collect::<Vec<_>>() -/// }, -/// [None, None] => { -/// vec![] -/// }, -/// [..] => vec![] -/// ); -/// -/// assert_eq!(res, Ok(vec![Some(2), Some(3)])); -/// -/// -/// let vec = vec![Some(1), Some(2), Some(3), None]; -/// -/// let res = match_vec!(vec; -/// [Some(_), y @ .., Some(_)] => { -/// y.collect::<Vec<_>>() -/// }, -/// [None, None] => { -/// vec![] -/// }, -/// ); -/// -/// assert!(res.is_err()); // there was no match -/// -/// # Ok::<(), ()>(()) -/// ``` -/// -/// -#[macro_export] -macro_rules! match_vec { - // Variable length pattern - (@make_pat; ($($acc:tt)*), $x:ident @ .., $($rest:tt)*) => { - $crate::match_vec!(@make_pat; - ($($acc)*, $x @ ..), - $($rest)* - ) - }; - // Special variable length pattern with a common unary variant - (@make_pat; ($($acc:tt)*), $variant:ident ($x:ident).., $($rest:tt)*) => { - $crate::match_vec!(@make_pat; - ($($acc)*, $x @ ..), - $($rest)* - ) - }; - // Variable length pattern without a binder - (@make_pat; ($($acc:tt)*), .., $($rest:tt)*) => { - $crate::match_vec!(@make_pat; - ($($acc)*, ..), - $($rest)* - ) - }; - // Single item pattern - (@make_pat; ($($acc:tt)*), $x:pat, $($rest:tt)*) => { - $crate::match_vec!(@make_pat; - ($($acc)*, $x), - $($rest)* - ) - }; - (@make_pat; (, $($acc:tt)*), $(,)*) => { - [$($acc)*] - }; - (@make_pat; ($($acc:tt)*), $(,)*) => { - [$($acc)*] - }; - - (@make_filter; $x:ident @ .., $($rest:tt)*) => { - $crate::match_vec!(@make_filter; - $($rest)* - ) - }; - (@make_filter; $variant:ident ($x:ident).., $($rest:tt)*) => { - { - // Circumvent https://github.com/rust-lang/rust/issues/59803 - let is_all_variant = || $x.iter() - .all(|x| match x { - $variant(_) => true, - _ => false, - }); - is_all_variant() - } - && - $crate::match_vec!(@make_filter; - $($rest)* - ) - }; - (@make_filter; .., $($rest:tt)*) => { - $crate::match_vec!(@make_filter; - $($rest)* - ) - }; - (@make_filter; $x:pat, $($rest:tt)*) => { - $crate::match_vec!(@make_filter; - $($rest)* - ) - }; - (@make_filter; $(,)*) => { - true - }; - - ($arg:expr; $( [$($args:tt)*] => $body:expr ),* $(,)*) => { - { - let vec = $arg; - // Match as references to decide which branch to take - // I think `match_default_bindings` should make this always work but - // there may be some patterns this doesn't capture. - #[allow(unused_variables, unreachable_patterns)] - match vec.as_slice() { - $( - $crate::match_vec!(@make_pat; (), $($args)*,) - if - $crate::match_vec!(@make_filter; $($args)*,) - => { - // Actually consume the values - #[allow(unused_mut)] - let mut iter = vec.into_iter(); - let ret = - $crate::destructure_iter!(iter; - [$($args)*] => $body - ); - match ret { - Some(x) => Ok(x), - None => unreachable!(), // Hopefully - } - } - )* - _ => std::result::Result::Err(vec), - } - } - }; -} - -#[test] -fn test() { - let test = |v: Vec<Option<isize>>| { - match_vec!(v.into_iter(); - [Some(_x), None, None] => 4, - [Some(_x), None] => 2, - [None, Some(y)] => 1, - [None, _y @ ..] => 3, - [_x @ .., Some(y), Some(z), None] => y - z, - [Some(ys)..] => ys.sum(), - [] => 0, - [..] => -1, - ) - .unwrap() - }; - - assert_eq!(test(vec![Some(0), None, None]), 4); - assert_eq!(test(vec![Some(0), None]), 2); - assert_eq!(test(vec![None, Some(0)]), 1); - assert_eq!(test(vec![Some(1), Some(2), Some(5), Some(14), None]), -9); - assert_eq!(test(vec![Some(1), Some(2), Some(3), Some(4)]), 10); - assert_eq!(test(vec![None]), 3); - assert_eq!(test(vec![]), 0); - assert_eq!(test(vec![Some(0), None, Some(1)]), -1); - - // Test move out of pattern - #[derive(Debug)] - struct Foo; - let _: (Foo, Foo) = match_vec!(vec![Some(Foo), Some(Foo)]; - [Some(f1), Some(f2)] => (f1, f2), - ) - .unwrap(); - - // Test return ownership if no match - let _: Vec<Foo> = match_vec!(vec![Foo]; - [] => "Error".to_string(), - ) - .unwrap_err(); -} |