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 a impl block that doesn't have the `delegate_impl` attribute.")
}
InputImplItem::Macro(mac) => {
bail!(mac => "Expected function.\n\
Help: Declare this macro in a 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.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 {
pat_ident: PatIdent { ident, .. },
..
}| ident,
);
let all_args = quote! { #receiver, #(#other_inputs_tt),* };
let inputs = Paren::from((paren_token, all_args));
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),* ).into() }
}
});
}
Ok(quote! {
#attrs
#defaultness #impl_unsafety #impl_token #impl_generics #self_ty #impl_where_clause {
#functions_tt
}
})
}