186 lines
6.6 KiB
Rust
186 lines
6.6 KiB
Rust
//! Parse impl blocks.
|
|
#![allow(clippy::or_fun_call)]
|
|
|
|
use crate::util::{self, span_container::SpanContainer};
|
|
use proc_macro2::{Ident, TokenStream};
|
|
use quote::quote;
|
|
use syn::{spanned::Spanned, PatType};
|
|
|
|
pub struct ImplBlock {
|
|
pub attrs: util::ObjectAttributes,
|
|
pub target_trait: Option<(String, syn::Path)>,
|
|
pub target_type: Box<syn::Type>,
|
|
pub type_ident: syn::Ident,
|
|
pub generics: syn::Generics,
|
|
// _impl: syn::ItemImpl,
|
|
pub methods: Vec<syn::ImplItemMethod>,
|
|
pub description: Option<String>,
|
|
}
|
|
|
|
impl ImplBlock {
|
|
/// Parse a `fn <NAME>() -> <TYPE>` method declaration found in
|
|
/// objects.
|
|
pub fn parse_method<
|
|
F: Fn(
|
|
&PatType,
|
|
&Ident,
|
|
bool,
|
|
) -> syn::Result<(TokenStream, util::GraphQLTypeDefinitionFieldArg)>,
|
|
>(
|
|
&self,
|
|
method: &syn::ImplItemMethod,
|
|
is_self_optional: bool,
|
|
f: F,
|
|
) -> syn::Result<(Vec<TokenStream>, Vec<util::GraphQLTypeDefinitionFieldArg>)> {
|
|
let mut arguments = method.sig.inputs.iter().peekable();
|
|
|
|
// Verify `&self` argument.
|
|
match arguments.peek() {
|
|
Some(syn::FnArg::Receiver(rec)) => {
|
|
let _consume = arguments.next();
|
|
if rec.reference.is_none() || rec.mutability.is_some() {
|
|
return Err(syn::Error::new(
|
|
rec.span(),
|
|
"invalid argument: did you mean `&self`?",
|
|
));
|
|
}
|
|
}
|
|
_ => {
|
|
if !is_self_optional {
|
|
return Err(syn::Error::new(
|
|
method.sig.span(),
|
|
"expected a `&self` argument",
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
let mut resolve_parts = Vec::new();
|
|
let mut additional_arguments = Vec::new();
|
|
|
|
for arg in arguments {
|
|
match arg {
|
|
syn::FnArg::Receiver(_) => {
|
|
if !is_self_optional {
|
|
return Err(syn::Error::new(
|
|
method.sig.ident.span(),
|
|
"self receiver must be the first argument",
|
|
));
|
|
}
|
|
}
|
|
syn::FnArg::Typed(captured) => {
|
|
let (arg_ident, is_mut) = match &*captured.pat {
|
|
syn::Pat::Ident(ref pat_ident) => {
|
|
(&pat_ident.ident, pat_ident.mutability.is_some())
|
|
}
|
|
_ => {
|
|
return Err(syn::Error::new(
|
|
captured.pat.span(),
|
|
"expected identifier for function argument",
|
|
));
|
|
}
|
|
};
|
|
let context_type = self.attrs.context.as_ref();
|
|
|
|
// Check for executor arguments.
|
|
if util::type_is_identifier_ref(&captured.ty, "Executor") {
|
|
resolve_parts.push(quote!(let #arg_ident = executor;));
|
|
}
|
|
// Make sure executor is specified as a reference.
|
|
else if util::type_is_identifier(&captured.ty, "Executor") {
|
|
return Err(syn::Error::new(
|
|
captured.ty.span(),
|
|
"to access the Executor, you need to specify the type as a reference.\nDid you mean &Executor?"
|
|
));
|
|
}
|
|
// Check for context arg.
|
|
else if context_type
|
|
.clone()
|
|
.map(|ctx| util::type_is_ref_of(&captured.ty, ctx))
|
|
.unwrap_or(false)
|
|
{
|
|
resolve_parts.push(quote!( let #arg_ident = executor.context(); ));
|
|
}
|
|
// Make sure the user does not specify the Context
|
|
// without a reference. (&Context)
|
|
else if context_type
|
|
.clone()
|
|
.map(|ctx| ctx.inner() == &*captured.ty)
|
|
.unwrap_or(false)
|
|
{
|
|
return Err(syn::Error::new(
|
|
captured.ty.span(),
|
|
format!("to access the context, you need to specify the type as a reference.\nDid you mean &{}?", quote!(captured.ty)),
|
|
));
|
|
} else {
|
|
let (tokens, ty) = f(captured, arg_ident, is_mut)?;
|
|
resolve_parts.push(tokens);
|
|
additional_arguments.push(ty);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok((resolve_parts, additional_arguments))
|
|
}
|
|
|
|
pub fn parse(attr_tokens: TokenStream, body: TokenStream) -> syn::Result<ImplBlock> {
|
|
let attrs = syn::parse2::<util::ObjectAttributes>(attr_tokens)?;
|
|
let mut _impl = syn::parse2::<syn::ItemImpl>(body)?;
|
|
|
|
let target_trait = match _impl.clone().trait_ {
|
|
Some((_, path, _)) => {
|
|
let name = path
|
|
.segments
|
|
.iter()
|
|
.map(|segment| segment.ident.to_string())
|
|
.collect::<Vec<_>>()
|
|
.join(".");
|
|
Some((name, path))
|
|
}
|
|
None => None,
|
|
};
|
|
|
|
let type_ident = if let Some(ident) = util::name_of_type(&*_impl.self_ty) {
|
|
ident
|
|
} else {
|
|
return Err(syn::Error::new(
|
|
_impl.self_ty.span(),
|
|
"could not determine a name for the impl type",
|
|
));
|
|
};
|
|
|
|
let target_type = _impl.self_ty.clone();
|
|
|
|
let description = attrs
|
|
.description
|
|
.clone()
|
|
.or_else(|| util::get_doc_comment(&_impl.attrs.clone()));
|
|
|
|
let mut methods = Vec::new();
|
|
|
|
for item in _impl.items {
|
|
match item {
|
|
syn::ImplItem::Method(method) => {
|
|
methods.push(method);
|
|
}
|
|
_ => {
|
|
return Err(syn::Error::new(
|
|
item.span(),
|
|
"only type declarations and methods are allowed",
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(Self {
|
|
attrs,
|
|
type_ident,
|
|
target_trait,
|
|
target_type,
|
|
generics: _impl.generics,
|
|
description: description.map(SpanContainer::into_inner),
|
|
methods,
|
|
})
|
|
}
|
|
}
|