zephyrus-macros 0.3.1

Procedural macros used by Zephyrus
Documentation
use proc_macro2::{Span, TokenStream as TokenStream2};
use syn::{
    parse2, spanned::Spanned, Error, GenericArgument, ItemFn, Lifetime, PathArguments, Result,
    Signature, Type,
};

pub fn autocomplete(input: TokenStream2) -> Result<TokenStream2> {
    let mut fun = parse2::<ItemFn>(input)?;

    if fun.sig.inputs.len() != 1 {
        return Err(Error::new(
            fun.sig.inputs.span(),
            "Autocomplete hook must have as parameters an AutocompleteContext<D>",
        ));
    }

    let data_type = get_data_type_and_set_lifetime(&fun.sig)?;
    set_lifetime(&mut fun.sig)?;
    let futurize = crate::util::get_futurize_macro();
    let path = quote::quote!(::zephyrus::hook::AutocompleteHook);
    let ident = fun.sig.ident.clone();
    let fn_ident = quote::format_ident!("_{}", ident);
    fun.sig.ident = fn_ident.clone();

    Ok(quote::quote! {
        pub fn #ident() -> #path<#data_type> {
            #path(#fn_ident)
        }

        #[#futurize]
        #fun
    })
}

fn get_data_type_and_set_lifetime(sig: &Signature) -> Result<Type> {
    let ctx = match sig.inputs.iter().next() {
        None => {
            return Err(Error::new(
                sig.inputs.span(),
                "Expected AutocompleteContext as only paramenter",
            ))
        }
        Some(c) => crate::util::get_pat(c)?,
    };
    let mut generics = crate::util::get_generic_arguments(crate::util::get_path(&ctx.ty)?)?;

    let ty = loop {
        match generics.next() {
            Some(GenericArgument::Lifetime(_)) => (),
            Some(a) => {
                break match a {
                    GenericArgument::Type(t) => {
                        if let Type::Infer(_) = t {
                            return Err(Error::new(
                                sig.inputs.span(),
                                "AutocompleteContext must have a known type",
                            ));
                        } else {
                            t.clone()
                        }
                    }
                    _ => {
                        return Err(Error::new(
                            sig.inputs.span(),
                            "AutocompleteContext type must be a type",
                        ))
                    }
                }
            }
            None => {
                return Err(Error::new(
                    sig.inputs.span(),
                    "AutocompleteContext type must be set",
                ))
            }
        }
    };

    Ok(ty)
}

fn set_lifetime(sig: &mut Signature) -> Result<()> {
    let lifetime = Lifetime::new("'future", Span::call_site());
    let ctx = crate::util::get_pat_mut(sig.inputs.iter_mut().next().unwrap())?;
    let path = crate::util::get_path_mut(&mut ctx.ty)?;
    let mut insert_lifetime = true;

    {
        let mut generics = crate::util::get_generic_arguments(&path)?;
        while let Some(generic) = generics.next() {
            if let GenericArgument::Lifetime(inner) = generic {
                if *inner == lifetime {
                    insert_lifetime = false;
                }
            }
        }
    }

    if insert_lifetime {
        if let PathArguments::AngleBracketed(inner) =
            &mut path.segments.last_mut().unwrap().arguments
        {
            inner.args.insert(0, GenericArgument::Lifetime(lifetime));
        }
    }

    Ok(())
}