|
1 | 1 | // Copyright (c) 2024 Carter Canedy <cartercanedy42@gmail.com>
|
2 | 2 |
|
3 |
| -use { |
4 |
| - proc_macro::TokenStream, |
5 |
| - syn::{parse_macro_input, Expr, Token}, |
6 |
| - syn::parse::{Parse, ParseStream}, |
7 |
| - syn::punctuated::Punctuated, |
8 |
| - quote::quote, |
9 |
| -}; |
| 3 | +#![feature(macro_metavar_expr)] |
10 | 4 |
|
11 |
| -struct ZipArgs { |
12 |
| - args: Punctuated<Expr, Token![,]> |
13 |
| -} |
| 5 | +/// Zips multiple instances of Option<T> into a single instance of Option<(T [, T...])>. Will |
| 6 | +/// either be Some if all arguments are some, or None if one of the instances is None. |
| 7 | +/// |
| 8 | +/// ## Usage: |
| 9 | +/// ``` |
| 10 | +/// let zipped_some = zip!(Some(1), Some(2)); |
| 11 | +/// assert_eq!(zipped_some, Some((1, 2))); |
| 12 | +/// |
| 13 | +/// let zipped_none = zip!(Some(1), None); |
| 14 | +/// assert_eq!(zipped_none, None); |
| 15 | +/// ``` |
| 16 | +#[macro_export] |
| 17 | +macro_rules! zip { |
| 18 | + ($($args:expr),+) => ({ |
| 19 | + let mut ok = true; |
| 20 | + $( |
| 21 | + let arg${index()} = $args; |
| 22 | + if arg${index()}.is_none() { |
| 23 | + ok = false; |
| 24 | + } |
| 25 | + )+ |
14 | 26 |
|
15 |
| -impl Parse for ZipArgs { |
16 |
| - fn parse(input: ParseStream) -> syn::Result<Self> { |
17 |
| - Ok(Self { |
18 |
| - args: Punctuated::<Expr, Token![,]>::parse_terminated(input)? |
19 |
| - }) |
20 |
| - } |
| 27 | + if ok { |
| 28 | + Some(($(arg${index()}.unwrap(),)+)) |
| 29 | + } else { |
| 30 | + None |
| 31 | + } |
| 32 | + }); |
21 | 33 | }
|
22 | 34 |
|
23 |
| -/// Expands into a single `Option<(T [, T...])>::Some((...))` instance if all arguments |
24 |
| -/// are instances of `Option<T>::Some(T)`, else expands to `Option<(T [, T...])>::None` |
| 35 | +/// Zips an arbitrary number of `Result<T>` into a single instance of `Option<(T [, T...])>`. If |
| 36 | +/// any arguments are `Err`, `None` is returned. Otherwise, `Some` is returned. |
25 | 37 | ///
|
26 |
| -/// Usage: |
| 38 | +/// ## Usage: |
27 | 39 | /// ```
|
28 |
| -/// use zips::zip; |
| 40 | +/// let i: Result<i32, String> = Ok(1); |
| 41 | +/// let j: Result<usize, String> = Ok(0usize); |
| 42 | +/// let k: Result<usize, String> = Err("I'm an error"); |
| 43 | +/// |
| 44 | +/// // zipped_ok: Option<(i32, usize)> |
| 45 | +/// let zipped_ok = zip_result!(i, j); |
| 46 | +/// assert_eq!(zipped_ok, Some(1, 0usize)); |
29 | 47 | ///
|
30 |
| -/// fn main() -> () { |
31 |
| -/// let zipped = zip!(Some(0), Some(1)); |
32 |
| -/// assert_eq!(zipped, Some(0, 1)); |
33 |
| -/// } |
| 48 | +/// // zipped_err: Option<(i32, usize, usize)> |
| 49 | +/// let zipped_err = zip_result!(i, j, k); |
| 50 | +/// assert_eq!(zipped_err, None); |
34 | 51 | /// ```
|
35 |
| -#[proc_macro] |
36 |
| -pub fn zip(input: TokenStream) -> TokenStream { |
37 |
| - let ZipArgs { args } = parse_macro_input!(input as ZipArgs); |
38 |
| - let arg_names: Vec<_> = |
39 |
| - (0..args.len()) |
40 |
| - .map(|i| syn::Ident::new(&format!("arg{i}"), proc_macro2::Span::call_site())) |
41 |
| - .collect(); |
42 |
| - |
43 |
| - let args = args.into_iter(); |
44 |
| - |
45 |
| - quote! { |
46 |
| - { |
47 |
| - #(let #arg_names = #args;)* |
48 |
| - if #(#arg_names.is_some() &&)* true { |
49 |
| - Some((#(#arg_names.unwrap()),*)) |
50 |
| - } else { |
51 |
| - None |
| 52 | +#[macro_export] |
| 53 | +macro_rules! zip_result { |
| 54 | + ($($args:expr),+) => ({ |
| 55 | + let mut ok = true; |
| 56 | + $( |
| 57 | + if $args.is_err() { |
| 58 | + ok = false; |
52 | 59 | }
|
| 60 | + )+ |
| 61 | + |
| 62 | + if ok { |
| 63 | + Some(($($args.ok().unwrap(),)+)) |
| 64 | + } else { |
| 65 | + None |
53 | 66 | }
|
54 |
| - }.into() |
| 67 | + }); |
55 | 68 | }
|
0 commit comments