rocket_codegen/bang/
typed_stream.rs

1use proc_macro2::TokenStream;
2use syn::parse::{Parse, ParseStream, discouraged::Speculative};
3
4#[derive(Debug)]
5pub enum Input {
6    Type(syn::Type, Option<(syn::Token![+], syn::Lifetime)>),
7    Tokens(TokenStream)
8}
9
10#[derive(Debug)]
11struct Invocation {
12    ty_stream_ty: syn::Path,
13    stream_mac: syn::Path,
14    stream_trait: syn::Path,
15    input: Input,
16}
17
18/// Reinterpret a `T + '_` (without the `dyn`) for `impl Stream<T> + '_`.
19fn trait_obj_recast(ty: &syn::Type) -> Option<(syn::Type, syn::Token![+], syn::Lifetime)> {
20    let bounds = match ty {
21        syn::Type::TraitObject(t) if t.dyn_token.is_none() => &t.bounds,
22        _ => return None
23    };
24
25    let mut bounds = bounds.pairs();
26    let (first, second) = (bounds.next()?, bounds.next()?);
27    let plus = **first.punct().expect("have two so have punct");
28
29    let first = first.value();
30    let real_ty = syn::parse2(quote!(#first)).ok()?;
31    let lifetime = match second.value() {
32        syn::TypeParamBound::Lifetime(lt) => lt.clone(),
33        _ => return None,
34    };
35
36    Some((real_ty, plus, lifetime))
37}
38
39impl Parse for Input {
40    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
41        let fork = input.fork();
42        if let Ok(mut ty) = fork.parse() {
43            // If there's an extra + '_, use it in the reinterpretation.
44            let mut bound = match fork.parse() {
45                Ok(plus) => Some((plus, fork.parse()?)),
46                _ => None,
47            };
48
49            // We might miss `A + '_`. Check if we did.
50            if let Some((real_ty, plus, lt)) = trait_obj_recast(&ty) {
51                ty = real_ty;
52                bound = Some((plus, lt));
53            }
54
55            // If we have nothing else to parse, this must have been a type.
56            if fork.is_empty() {
57                input.advance_to(&fork);
58                Ok(Input::Type(ty, bound))
59            } else {
60                Ok(Input::Tokens(input.parse()?))
61            }
62        } else {
63            Ok(Input::Tokens(input.parse()?))
64        }
65    }
66}
67
68impl Parse for Invocation {
69    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
70        Ok(Invocation {
71            ty_stream_ty: (input.parse()?, input.parse::<syn::Token![,]>()?).0,
72            stream_mac: (input.parse()?, input.parse::<syn::Token![,]>()?).0,
73            stream_trait: (input.parse()?, input.parse::<syn::Token![,]>()?).0,
74            input: input.parse()?,
75        })
76    }
77}
78
79/// This macro exists because we want to disambiguate between input of a type
80/// and input of an expression that looks like a type. `macro_rules` matches
81/// eagerly on a single token, so something like `foo!(for x in 0..10 {})` will
82/// match a `($ty)` branch as will anything that starts with a path.
83pub fn _macro(input: proc_macro::TokenStream) -> devise::Result<TokenStream> {
84    let tokens = proc_macro2::TokenStream::from(input);
85    let i: Invocation = syn::parse2(tokens)?;
86    let (s_ty, mac, s_trait) = (i.ty_stream_ty, i.stream_mac, i.stream_trait);
87    let tokens = match i.input {
88        Input::Tokens(tt) => quote!(#s_ty::from(#mac!(#tt))),
89        Input::Type(ty, Some((p, l))) => quote!(#s_ty<impl #s_trait<Item = #ty> #p #l>),
90        Input::Type(ty, None) => quote!(#s_ty<impl #s_trait<Item = #ty>>),
91    };
92
93    Ok(tokens)
94}