use darling::FromAttributes;
use proc_macro2::TokenStream;
use quote::ToTokens;
use quote::quote;
use syn::Generics;
use syn::Path;
use crate::args::common;
use crate::args::common::get_add_implement_code;
use crate::args::common::get_interface_mark_code;
use crate::args::common::get_register_interface_code;
use crate::utils::common::CommonInterfaceAttrs;
use crate::utils::common::CommonObject;
use crate::utils::crate_name::get_crate_name;
use crate::utils::derive_types::BaseStruct;
use crate::utils::error::IntoTokenStream;
use crate::utils::interface_attr::InterfaceImplAttr;
use crate::utils::interface_attr::InterfaceMarkAttr;
use crate::utils::macros::*;
use crate::utils::register_attr::RegisterAttr;
use crate::utils::with_attributes::WithAttributes;
use crate::utils::with_doc::WithDoc;
#[derive(FromAttributes, Debug, Clone)]
#[darling(attributes(graphql))]
pub struct ResolvedObjectAttrs {
#[darling(default)]
pub root: bool,
#[darling(default)]
pub name: Option<String>,
#[darling(default)]
#[darling(rename = "get_type_name")]
pub type_name: bool,
#[darling(default, multiple)]
#[darling(rename = "register")]
pub registers: Vec<RegisterAttr>,
#[darling(default, multiple)]
#[darling(rename = "mark")]
pub marks: Vec<InterfaceMarkAttr>,
#[darling(default, multiple)]
#[darling(rename = "implements")]
pub impls: Vec<InterfaceImplAttr>,
}
from_derive_input!(
ResolvedObject,
WithAttributes<WithDoc<ResolvedObjectAttrs>, BaseStruct<(), Generics>>,
);
impl CommonObject for ResolvedObject {
fn get_name(&self) -> Option<&str> {
self.attrs.name.as_deref()
}
fn should_impl_type_name(&self) -> bool {
!self.attrs.type_name
}
fn get_ident(&self) -> &syn::Ident {
&self.ident
}
fn get_type(&self) -> darling::Result<Path> {
Ok(self.ident.clone().into())
}
fn get_generics(&self) -> darling::Result<&Generics> {
Ok(&self.generics)
}
fn get_doc(&self) -> darling::Result<Option<String>> {
Ok(self.attrs.doc.clone())
}
}
impl CommonInterfaceAttrs for ResolvedObject {
fn get_marks(&self) -> &Vec<InterfaceMarkAttr> {
&self.attrs.marks
}
fn get_impls(&self) -> &Vec<InterfaceImplAttr> {
&self.attrs.impls
}
}
fn impl_register_interface(object: &impl CommonInterfaceAttrs) -> darling::Result<TokenStream> {
let crate_name = get_crate_name();
let object_ident = object.get_ident();
let register_interface_code = get_register_interface_code(object)?;
let add_interfaces = get_interface_mark_code(object)?;
let implement = get_add_implement_code(object, object.get_impls())?;
let (impl_generics, ty_generics, where_clause) = object.get_generics()?.split_for_impl();
Ok(quote! {
impl #impl_generics #object_ident #ty_generics #where_clause {
fn __register_interface(registry: #crate_name::internal::Registry) -> #crate_name::internal::Registry {
#register_interface_code
#implement
let registry = registry.update_object(
<Self as #crate_name::internal::Object>::get_object_type_name().as_ref(),
<Self as #crate_name::internal::Object>::get_object_type_name().as_ref(),
|object| {
#add_interfaces
object
},
);
registry
}
}
})
}
fn impl_register_root(object: &ResolvedObject) -> darling::Result<TokenStream> {
let crate_name = get_crate_name();
let object_ident = object.get_ident();
let (impl_generics, ty_generics, where_clause) = object.get_generics()?.split_for_impl();
let root = if object.attrs.root {
let crate_name = get_crate_name();
Some(quote! {
let registry = registry.set_root(<Self as #crate_name::internal::Object>::get_object_type_name().as_ref());
})
} else {
None
};
Ok(quote! {
impl #impl_generics #object_ident #ty_generics #where_clause {
fn __register_root(registry: #crate_name::internal::Registry) -> #crate_name::internal::Registry {
#root
registry
}
}
})
}
fn impl_graphql_doc_fn(object: &impl CommonObject) -> darling::Result<TokenStream> {
let crate_name = get_crate_name();
let object_ident = object.get_ident();
let (impl_generics, ty_generics, where_clause) = object.get_generics()?.split_for_impl();
let doc = object
.get_doc()
.map(|doc| {
if let Some(doc) = doc {
let crate_name = get_crate_name();
Some(quote! {
let registry = registry.update_object(
<Self as #crate_name::internal::Object>::get_object_type_name().as_ref(),
<Self as #crate_name::internal::Object>::get_object_type_name().as_ref(),
|object| {
object.description(#doc)
},
);
})
} else {
None
}
})
.into_token_stream();
Ok(quote! {
impl #impl_generics #object_ident #ty_generics #where_clause {
fn __register_doc(registry: #crate_name::internal::Registry) -> #crate_name::internal::Registry {
#doc
registry
}
}
})
}
fn impl_registers_fn(object: &ResolvedObject) -> darling::Result<TokenStream> {
let crate_name = get_crate_name();
let object_ident = object.get_ident();
let (impl_generics, ty_generics, where_clause) = object.get_generics()?.split_for_impl();
let register_attr = &object.attrs.registers;
Ok(quote! {
impl #impl_generics #object_ident #ty_generics #where_clause {
fn __registers(registry: #crate_name::internal::Registry) -> #crate_name::internal::Registry {
#( #register_attr )*
registry
}
}
})
}
fn impl_register_fns_trait(obj: &impl CommonInterfaceAttrs) -> darling::Result<TokenStream> {
let crate_name = get_crate_name();
let object_ident = obj.get_ident();
let generics = obj.get_generics()?;
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let turbofish_generics = ty_generics.as_turbofish();
Ok(quote! {
impl #impl_generics #crate_name::internal::RegisterFns for #object_ident #ty_generics #where_clause {
const REGISTER_FNS: &'static [fn (registry: #crate_name::internal::Registry) -> #crate_name::internal::Registry] = &[
#object_ident #turbofish_generics ::__register_interface,
#object_ident #turbofish_generics ::__register_root,
#object_ident #turbofish_generics ::__register_doc,
#object_ident #turbofish_generics ::__registers,
];
}
})
}
impl ToTokens for ResolvedObject {
fn to_tokens(&self, tokens: &mut TokenStream) {
let impl_object = common::impl_object(self).into_token_stream();
let impl_resolve_owned = common::impl_resolve_owned(self).into_token_stream();
let impl_resolve_ref = common::impl_resolve_ref(self).into_token_stream();
let impl_graphql_doc = impl_graphql_doc_fn(self).into_token_stream();
let register_interface = impl_register_interface(self).into_token_stream();
let register_root = impl_register_root(self).into_token_stream();
let impl_register_extras = impl_register_fns_trait(self).into_token_stream();
let impl_interface_mark = common::impl_interface_mark(self).into_token_stream();
let impl_registers_fn = impl_registers_fn(self).into_token_stream();
tokens.extend(quote! {
#impl_registers_fn
#impl_object
#impl_interface_mark
#impl_resolve_owned
#impl_resolve_ref
#impl_graphql_doc
#register_interface
#register_root
#impl_register_extras
});
}
}