improved_slice_patterns/lib.rs
1#![doc(html_root_url = "https://docs.rs/improved_slice_patterns/2.0.1")]
2
3//! A tiny crate that provides two macros to help matching
4//! on `Vec`s and iterators using the syntax of
5//! [`slice_patterns`][slice_patterns]
6//!
7//! [slice_patterns]: https://doc.rust-lang.org/nightly/unstable-book/language-features/slice-patterns.html
8
9/// Destructure an iterator using the syntax of slice_patterns.
10///
11/// Wraps the match body in `Some` if there was a match; returns
12/// `None` otherwise.
13///
14/// Contrary to slice_patterns, this allows moving out
15/// of the iterator.
16///
17/// A variable length pattern (`x @ ..`) is only allowed as the last
18/// pattern, unless the iterator is double-ended.
19///
20/// Example:
21/// ```edition2018
22/// use improved_slice_patterns::destructure_iter;
23///
24/// let vec = vec![Some(1), Some(2), Some(3), None];
25///
26/// let res = destructure_iter!(vec.into_iter();
27/// [Some(x), y @ .., z] => {
28/// // x: usize
29/// // y: impl Iterator<Option<usize>>
30/// // z: Option<usize>
31/// (x, y.collect::<Vec<_>>(), z)
32/// }
33/// );
34///
35/// assert_eq!(res, Some((1, vec![Some(2), Some(3)], None)));
36///
37/// # Ok::<(), ()>(())
38/// ```
39///
40///
41#[macro_export]
42macro_rules! destructure_iter {
43 // Variable length pattern
44 (@match_forwards, $iter:expr, ($body:expr),
45 $x:ident @ .., $($rest:tt)*) => {
46 $crate::destructure_iter!(@match_backwards,
47 $iter,
48 ({
49 let $x = $iter;
50 $body
51 }),
52 $($rest)*
53 )
54 };
55 // Special variable length pattern with a common unary variant
56 (@match_forwards, $iter:expr, ($body:expr),
57 $variant:ident ($x:ident).., $($rest:tt)*) => {
58 $crate::destructure_iter!(@match_backwards,
59 $iter,
60 ({
61 let $x = $iter
62 .map(|x| match x {
63 $variant(y) => y,
64 _ => unreachable!(),
65 });
66 $body
67 }),
68 $($rest)*
69 )
70 };
71 // Variable length pattern without a binder
72 (@match_forwards, $iter:expr, ($body:expr), .., $($rest:tt)*) => {
73 $crate::destructure_iter!(@match_backwards,
74 $iter,
75 ($body),
76 $($rest)*
77 )
78 };
79 // Single item pattern
80 (@match_forwards, $iter:expr, ($body:expr), $x:pat, $($rest:tt)*) => {
81 if let std::option::Option::Some($x) = $iter.next() {
82 $crate::destructure_iter!(@match_forwards,
83 $iter,
84 ($body),
85 $($rest)*
86 )
87 } else {
88 std::option::Option::None
89 }
90 };
91 // Single item pattern after a variable length one: declare reversed and take from the end
92 (@match_backwards, $iter:expr, ($body:expr), $x:pat, $($rest:tt)*) => {
93 $crate::destructure_iter!(@match_backwards, $iter, (
94 if let std::option::Option::Some($x) = $iter.next_back() {
95 $body
96 } else {
97 std::option::Option::None
98 }
99 ), $($rest)*)
100 };
101
102 // Check no elements remain
103 (@match_forwards, $iter:expr, ($body:expr) $(,)*) => {
104 if $iter.next().is_some() {
105 std::option::Option::None
106 } else {
107 $body
108 }
109 };
110 // After a variable length pattern, everything has already been consumed
111 (@match_backwards, $iter:expr, ($body:expr) $(,)*) => {
112 $body
113 };
114
115 ($iter:expr; [$($args:tt)*] => $body:expr) => {
116 {
117 #[allow(unused_mut)]
118 let mut iter = $iter;
119 $crate::destructure_iter!(@match_forwards,
120 iter,
121 (std::option::Option::Some($body)),
122 $($args)*,
123 )
124 }
125 };
126}
127
128/// Pattern-match on a vec using the syntax of slice_patterns.
129///
130/// Wraps the match body in `Ok` if there was a match; returns
131/// an `Err` containing the ownership of the vec otherwise.
132///
133/// Contrary to slice_patterns, this allows moving out
134/// of the `Vec`.
135///
136/// A variable length pattern (`x @ ..`) returns an iterator.
137///
138/// Example:
139/// ```edition2018
140/// #![feature(slice_patterns)]
141/// use improved_slice_patterns::match_vec;
142///
143/// let vec = vec![Some(1), Some(2), Some(3), None];
144///
145/// let res = match_vec!(vec;
146/// [Some(_), y @ .., None] => {
147/// y.collect::<Vec<_>>()
148/// },
149/// [None, None] => {
150/// vec![]
151/// },
152/// [..] => vec![]
153/// );
154///
155/// assert_eq!(res, Ok(vec![Some(2), Some(3)]));
156///
157///
158/// let vec = vec![Some(1), Some(2), Some(3), None];
159///
160/// let res = match_vec!(vec;
161/// [Some(_), y @ .., Some(_)] => {
162/// y.collect::<Vec<_>>()
163/// },
164/// [None, None] => {
165/// vec![]
166/// },
167/// );
168///
169/// assert!(res.is_err()); // there was no match
170///
171/// # Ok::<(), ()>(())
172/// ```
173///
174///
175#[macro_export]
176macro_rules! match_vec {
177 // Variable length pattern
178 (@make_pat; ($($acc:tt)*), $x:ident @ .., $($rest:tt)*) => {
179 $crate::match_vec!(@make_pat;
180 ($($acc)*, $x @ ..),
181 $($rest)*
182 )
183 };
184 // Special variable length pattern with a common unary variant
185 (@make_pat; ($($acc:tt)*), $variant:ident ($x:ident).., $($rest:tt)*) => {
186 $crate::match_vec!(@make_pat;
187 ($($acc)*, $x @ ..),
188 $($rest)*
189 )
190 };
191 // Variable length pattern without a binder
192 (@make_pat; ($($acc:tt)*), .., $($rest:tt)*) => {
193 $crate::match_vec!(@make_pat;
194 ($($acc)*, ..),
195 $($rest)*
196 )
197 };
198 // Single item pattern
199 (@make_pat; ($($acc:tt)*), $x:pat, $($rest:tt)*) => {
200 $crate::match_vec!(@make_pat;
201 ($($acc)*, $x),
202 $($rest)*
203 )
204 };
205 (@make_pat; (, $($acc:tt)*), $(,)*) => {
206 [$($acc)*]
207 };
208 (@make_pat; ($($acc:tt)*), $(,)*) => {
209 [$($acc)*]
210 };
211
212 (@make_filter; $x:ident @ .., $($rest:tt)*) => {
213 $crate::match_vec!(@make_filter;
214 $($rest)*
215 )
216 };
217 (@make_filter; $variant:ident ($x:ident).., $($rest:tt)*) => {
218 {
219 // Circumvent https://github.com/rust-lang/rust/issues/59803
220 let is_all_variant = || $x.iter()
221 .all(|x| match x {
222 $variant(_) => true,
223 _ => false,
224 });
225 is_all_variant()
226 }
227 &&
228 $crate::match_vec!(@make_filter;
229 $($rest)*
230 )
231 };
232 (@make_filter; .., $($rest:tt)*) => {
233 $crate::match_vec!(@make_filter;
234 $($rest)*
235 )
236 };
237 (@make_filter; $x:pat, $($rest:tt)*) => {
238 $crate::match_vec!(@make_filter;
239 $($rest)*
240 )
241 };
242 (@make_filter; $(,)*) => {
243 true
244 };
245
246 ($arg:expr; $( [$($args:tt)*] => $body:expr ),* $(,)*) => {
247 {
248 let vec = $arg;
249 // Match as references to decide which branch to take
250 // I think `match_default_bindings` should make this always work but
251 // there may be some patterns this doesn't capture.
252 #[allow(unused_variables, unreachable_patterns)]
253 match vec.as_slice() {
254 $(
255 $crate::match_vec!(@make_pat; (), $($args)*,)
256 if
257 $crate::match_vec!(@make_filter; $($args)*,)
258 => {
259 // Actually consume the values
260 #[allow(unused_mut)]
261 let mut iter = vec.into_iter();
262 let ret =
263 $crate::destructure_iter!(iter;
264 [$($args)*] => $body
265 );
266 match ret {
267 Some(x) => Ok(x),
268 None => unreachable!(), // Hopefully
269 }
270 }
271 )*
272 _ => std::result::Result::Err(vec),
273 }
274 }
275 };
276}
277
278#[test]
279fn test() {
280 let test = |v: Vec<Option<isize>>| {
281 match_vec!(v.into_iter();
282 [Some(_x), None, None] => 4,
283 [Some(_x), None] => 2,
284 [None, Some(y)] => 1,
285 [None, _y @ ..] => 3,
286 [_x @ .., Some(y), Some(z), None] => y - z,
287 [Some(ys)..] => ys.sum(),
288 [] => 0,
289 [..] => -1,
290 )
291 .unwrap()
292 };
293
294 assert_eq!(test(vec![Some(0), None, None]), 4);
295 assert_eq!(test(vec![Some(0), None]), 2);
296 assert_eq!(test(vec![None, Some(0)]), 1);
297 assert_eq!(test(vec![Some(1), Some(2), Some(5), Some(14), None]), -9);
298 assert_eq!(test(vec![Some(1), Some(2), Some(3), Some(4)]), 10);
299 assert_eq!(test(vec![None]), 3);
300 assert_eq!(test(vec![]), 0);
301 assert_eq!(test(vec![Some(0), None, Some(1)]), -1);
302
303 // Test move out of pattern
304 #[derive(Debug)]
305 struct Foo;
306 let _: (Foo, Foo) = match_vec!(vec![Some(Foo), Some(Foo)];
307 [Some(f1), Some(f2)] => (f1, f2),
308 )
309 .unwrap();
310
311 // Test return ownership if no match
312 let _: Vec<Foo> = match_vec!(vec![Foo];
313 [] => "Error".to_string(),
314 )
315 .unwrap_err();
316}