1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
use proc_macro2::TokenTree;

use crate::{Extractor, FromMacro, Iter, Parenthesisized, StreamExtract, CommaExtractor, EndOfStream};

/// Extracts multiple items sequentiallly.
/// # Example:
/// ```
/// # use macroex::*;
/// # use quote::quote;
/// # fn main() -> Result<(), Error> {
/// let MultiExtractor((a, b, c)) = quote!(1 2.0 '3').into_iter().extract()?;
/// # let _ = 1u8.max(a); let _ = 2.0f32.max(b); let _ = '3'.max(c);
/// assert_eq!(a, 1);
/// assert_eq!(b, 2.0);
/// assert_eq!(c, '3');
/// # Ok(())}
/// ```
#[derive(Debug)]
pub struct MultiExtractor<T>(pub T);

macro_rules! multi_extract_tuple {
    () => {};
    ($this: ident $(,$thing: ident)* $(,)?) => {
        impl<$this, $($thing),*> Extractor for MultiExtractor<($this, $($thing),*)> where $this: Extractor, $($thing: Extractor),*{
            fn extract(iter: &mut impl Iterator<Item=proc_macro2::TokenTree>) -> Result<Self, crate::Error> {
                Ok(Self((
                    $this::extract(iter)?,
                    $($thing::extract(iter)?,)*
                )))
            }
        }
        multi_extract_tuple!($($thing),*);
    };
}

multi_extract_tuple!(A, B, C, D, E, F, G, H, I, J, K, L,);

macro_rules! tuple_impl {
    () => {};
    ($first: ident $(,$thing: ident)* $(,)?) => {
        impl<$first, $($thing),*> FromMacro for ($first, $($thing),*) where $first: FromMacro, $($thing: FromMacro),* {
            fn from_one(tt: TokenTree) -> Result<Self, crate::Error> {
                let Parenthesisized(Iter(mut iter)) = Parenthesisized::from_one(tt)?;
                let result = (
                    {
                        let CommaExtractor(x) = iter.extract::<CommaExtractor<$first>>()?;
                        x
                    },
                    $({
                        let CommaExtractor(x) = iter.extract::<CommaExtractor<$thing>>()?;
                        x
                    },)*
                );
                let EndOfStream = iter.extract()?;
                Ok(result)
            }
        }
        tuple_impl!($($thing),*);
    };
}

tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L,);