spire_enum_macros 0.7.2

Procedural macros distributed by the crate `spire_enum`.
Documentation
use super::*;

#[derive(Parse, ToTokens)]
pub struct InputImplInherent {
    attrs: Any<Attribute<SynMeta>>,
    defaultness: Optional<Token![default]>,
    unsafety: Optional<Token![unsafe]>,
    impl_token: Token![impl],
    generics: Optional<InputGenerics>,
    self_ty: Box<Type>,
    where_clause: Optional<WhereClause>,
    items: Brace<Any<InputImplItem>>,
}

pub fn run(input: InputImplInherent) -> Result<TokenStream> {
    let sane = sanitize_input(input)?;
    generate_output(sane)
}

struct SaneImplInherent {
    attrs: Any<Attribute<SynMeta>>,
    defaultness: Optional<Token![default]>,
    unsafety: Optional<Token![unsafe]>,
    impl_token: Token![impl],
    generics: Optional<SaneGenerics>,
    self_ty: Box<Type>,
    functions: Brace<Any<SaneMethod>>,
}

fn sanitize_input(input: InputImplInherent) -> Result<SaneImplInherent> {
    let InputImplInherent {
        attrs,
        defaultness,
        unsafety,
        impl_token,
        generics,
        self_ty,
        where_clause,
        items,
    } = input;

    let (brace, item_list) = items.into_parts();

    let generics = sanitize_generics(generics, where_clause)?;

    let sane_items = item_list
		.into_iter()
		.map(|item| {
			match item {
				InputImplItem::Fn(func) => sanitize_fn(*func),
				InputImplItem::Type(assoc_type) => {
					bail!(assoc_type => "Expected function.\n\
						Help: Associated types aren't supported in impl blocks that have the `delegate_impl` attribute.")
				}
				InputImplItem::Const(constant) => {
					bail!(constant => "Expected function.\n\
						Help: Declare this constant in an impl block that doesn't have the `delegate_impl` attribute.")
				}
				InputImplItem::Macro(mac) => {
					bail!(mac => "Expected function.\n\
						Help: Declare this macro in an impl block that doesn't have the `delegate_impl` attribute.")
				}
			}
		})
		.try_collect()?;

    Ok(SaneImplInherent {
        attrs,
        defaultness,
        unsafety,
        impl_token,
        generics,
        self_ty,
        functions: Brace::from((brace, sane_items)),
    })
}

struct SaneMethod {
    attrs: Any<Attribute<SynMeta>>,
    vis: Visibility,
    sig: SaneMethodSignature,
    _semi_token: Token![;],
}

fn sanitize_fn(input: InputImplItemFn) -> Result<SaneMethod> {
    let InputImplItemFn {
        attrs,
        vis,
        sig,
        body,
    } = input;

    let semi_token = match body {
        InputImplItemFnBody::Block(block) => {
            bail!(block => "Function implementations aren't allowed. \
					The implementation will be generated by this macro.\n\
					Help: Replace this block with a `;` (semi-colon).")
        }
        InputImplItemFnBody::SemiColon(semi_token) => semi_token,
    };

    Ok(SaneMethod {
        attrs,
        vis,
        sig: sanitize_method_signature(sig)?,
        _semi_token: semi_token,
    })
}

struct SaneMethodSignature {
    constness: Optional<Token![const]>,
    asyncness: Optional<Token![async]>,
    unsafety: Optional<Token![unsafe]>,
    abi: Optional<syn::Abi>,
    fn_token: Token![fn],
    ident: Ident,
    generics: Optional<InputGenerics>,
    paren_token: syn::token::Paren,
    receiver: Receiver,
    other_inputs: Vec<SaneNonReceiverFnArg>,
    output: syn::ReturnType,
    where_clause: Optional<WhereClause>,
}

fn sanitize_method_signature(input: InputFnSignature) -> Result<SaneMethodSignature> {
    let InputFnSignature {
        constness,
        asyncness,
        unsafety,
        abi,
        fn_token,
        ident,
        generics,
        inputs,
        output,
        where_clause,
    } = input;

    let (paren_token, inputs) = inputs.into_parts();
    let mut inputs_iter = inputs.inner.into_iter();

    let Some(FnArg::Receiver(receiver)) = inputs_iter.next() else {
        bail!(ident => "Expected function to have a receiver.\n\
			Help: To delegate the implementation to the variants, we need `Self`(the enum) as an argument.")
    };

    let other_inputs = inputs_iter
        .map(|arg| {
            match arg {
                FnArg::Receiver(other_receiver) => {
                    bail!(other_receiver => "Expected exactly one receiver.",
                        receiver => "First receiver declared here"
                    );
                }
                FnArg::Typed(pat_type) => sanitize_fn_arg(pat_type),
            }
        })
        .try_collect()?;

    Ok(SaneMethodSignature {
        constness,
        asyncness,
        unsafety,
        abi,
        fn_token,
        ident,
        generics,
        paren_token,
        receiver,
        other_inputs,
        output,
        where_clause,
    })
}

struct SaneNonReceiverFnArg {
    attrs: Vec<SynAttribute>,
    pat_ident: PatIdent,
    colon_token: Token![:],
    ty: Box<Type>,
}

fn sanitize_fn_arg(arg: PatType) -> Result<SaneNonReceiverFnArg> {
    let PatType {
        attrs,
        pat,
        colon_token,
        ty,
    } = arg;

    match *pat {
        Pat::Ident(pat_ident) => {
            Ok(SaneNonReceiverFnArg {
                attrs,
                pat_ident,
                colon_token,
                ty,
            })
        }
        other => {
            bail!(other => "Patterns in parameters aren't allowed, \
				please use a plain identifier (e.g: `foo: Ty`).")
        }
    }
}

fn generate_output(sane: SaneImplInherent) -> Result<TokenStream> {
    let SaneImplInherent {
        attrs,
        defaultness,
        unsafety: impl_unsafety,
        impl_token,
        generics: impl_generics,
        self_ty,
        functions,
    } = sane;

    let (impl_generics, impl_where_clause) = impl_generics.into_pair();

    let macro_ident = {
        let enum_ident = find_enum_ident(&self_ty)
            .ok_or_else(|| Error::new(self_ty.span(), "Could not find main ident in this type."))?;

        delegate_macro_ident(enum_ident)
    };

    let mut functions_tt = TokenStream::new();

    for SaneMethod {
        attrs,
        vis,
        sig,
        _semi_token: _,
    } in functions.into_inner()
    {
        let SaneMethodSignature {
            constness,
            asyncness,
            unsafety: fn_unsafety,
            abi,
            fn_token,
            ident: fn_ident,
            generics: fn_generics,
            paren_token,
            receiver,
            other_inputs,
            output,
            where_clause: fn_where_clause,
        } = sig;

        let attrs = attrs.iter();

        let other_inputs_tt = other_inputs.iter().map(
            |SaneNonReceiverFnArg {
                 attrs,
                 pat_ident,
                 colon_token,
                 ty,
             }| quote! { #(#attrs)* #pat_ident #colon_token #ty },
        );

        let invocation_args = other_inputs.iter().map(
            |SaneNonReceiverFnArg {
                 attrs: _,
                 pat_ident: PatIdent { ident, .. },
                 colon_token: _,
                 ty: _,
             }| ident,
        );

        let all_args = quote! { #receiver, #(#other_inputs_tt),* };

        let inputs = Paren::from((paren_token, all_args));

        let maybe_await = asyncness.as_ref().map(|_| quote! { . await });

        functions_tt.extend(quote! {
            #( #attrs )*
            #vis #constness #asyncness #fn_unsafety #abi #fn_token
            #fn_ident #fn_generics #inputs #output #fn_where_clause {
                #macro_ident ! { self.#fn_ident( #(#invocation_args),* ) #maybe_await .into() }
            }
        });
    }

    Ok(quote! {
        #attrs
        #defaultness #impl_unsafety #impl_token #impl_generics #self_ty #impl_where_clause {
            #functions_tt
        }
    })
}