aboutsummaryrefslogtreecommitdiff
path: root/src/invoke.rs
blob: f7da20abb43f087995283acc6eeaf3bf6435f579 (plain) (blame)
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
use crate::SynResult;
use crate::TokenStream2;
use crate::config::default_feature_name;
use proc_macro::TokenStream;
use quote::quote;
use syn::{
    LitStr, Token,
    parse::{Parse, ParseStream},
    parse_macro_input,
};

#[doc = include_str!("../doc/args/invoke.md")]
pub enum InvokeArgs {
    /// invoke!(expr) — feature name defaults to "async"
    Default(TokenStream2),

    /// invoke!("feat" => expr) — explicit feature name
    Explicit(LitStr, TokenStream2),
}

impl Parse for InvokeArgs {
    fn parse(input: ParseStream) -> SynResult<Self> {
        if input.peek(LitStr) {
            let feat: LitStr = input.parse()?;
            input.parse::<Token![=>]>()?;
            let expr: TokenStream2 = input.parse()?;
            Ok(InvokeArgs::Explicit(feat, expr))
        } else {
            let expr: TokenStream2 = input.parse()?;
            Ok(InvokeArgs::Default(expr))
        }
    }
}

pub(crate) fn invoke(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as InvokeArgs);

    let expanded = match input {
        InvokeArgs::Default(expr) => {
            let feat = default_feature_name();
            quote! {{
                #[cfg(feature = #feat)]
                { #expr.await }
                #[cfg(not(feature = #feat))]
                { #expr }
            }}
        }
        InvokeArgs::Explicit(feat, expr) => {
            let feat_name = &feat;
            quote! {{
                #[cfg(feature = #feat_name)]
                { #expr.await }
                #[cfg(not(feature = #feat_name))]
                { #expr }
            }}
        }
    };

    TokenStream::from(expanded)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn default_variant() {
        // Input: an expression without feature name → Default variant
        let input: proc_macro2::TokenStream = "compute(5)".parse().unwrap();
        let args: InvokeArgs = syn::parse2(input).unwrap();
        match args {
            InvokeArgs::Default(_) => {} // expected
            _ => panic!("expected Default variant"),
        }
    }

    #[test]
    fn explicit_variant() {
        // Input: "my_ft" => expr → Explicit variant with feature "my_ft"
        let input: proc_macro2::TokenStream = "\"my_ft\" => compute(5)".parse().unwrap();
        let args: InvokeArgs = syn::parse2(input).unwrap();
        match args {
            InvokeArgs::Explicit(feat, _) => {
                assert_eq!(feat.value(), "my_ft");
            }
            _ => panic!("expected Explicit variant"),
        }
    }
}