async_graphql_derive/
merged_object.rs1use darling::ast::Data;
2use proc_macro::TokenStream;
3use proc_macro2::Span;
4use quote::quote;
5use syn::{Error, LitInt};
6
7use crate::{
8 args::{self, RenameTarget, TypeDirectiveLocation},
9 utils::{
10 GeneratorResult, gen_boxed_trait, gen_directive_calls, get_crate_name, get_rustdoc,
11 visible_fn,
12 },
13};
14
15pub fn generate(object_args: &args::MergedObject) -> GeneratorResult<TokenStream> {
16 let crate_name = get_crate_name(object_args.internal);
17 let boxed_trait = gen_boxed_trait(&crate_name);
18 let ident = &object_args.ident;
19 let (impl_generics, ty_generics, where_clause) = object_args.generics.split_for_impl();
20 let extends = object_args.extends;
21 let shareable = object_args.shareable;
22 let inaccessible = object_args.inaccessible;
23 let interface_object = object_args.interface_object;
24 let tags = object_args
25 .tags
26 .iter()
27 .map(|tag| quote!(::std::string::ToString::to_string(#tag)))
28 .collect::<Vec<_>>();
29 let gql_typename = if !object_args.name_type {
30 let name = object_args
31 .name
32 .clone()
33 .unwrap_or_else(|| RenameTarget::Type.rename(ident.to_string()));
34 quote!(::std::borrow::Cow::Borrowed(#name))
35 } else {
36 quote!(<Self as #crate_name::TypeName>::type_name())
37 };
38
39 let directives = gen_directive_calls(&object_args.directives, TypeDirectiveLocation::Object);
40
41 let desc = get_rustdoc(&object_args.attrs)?
42 .map(|s| quote! { ::std::option::Option::Some(::std::string::ToString::to_string(#s)) })
43 .unwrap_or_else(|| quote! {::std::option::Option::None});
44
45 let s = match &object_args.data {
46 Data::Struct(e) => e,
47 _ => {
48 return Err(Error::new_spanned(
49 ident,
50 "MergedObject can only be applied to an struct.",
51 )
52 .into());
53 }
54 };
55
56 let mut types = Vec::new();
57 for field in &s.fields {
58 types.push(&field.ty);
59 }
60
61 let create_merged_obj = {
62 let mut obj = quote! { #crate_name::MergedObjectTail };
63 for i in 0..types.len() {
64 let n = LitInt::new(&format!("{}", i), Span::call_site());
65 obj = quote! { #crate_name::MergedObject(&self.#n, #obj) };
66 }
67 quote! {
68 #obj
69 }
70 };
71
72 let merged_type = {
73 let mut obj = quote! { #crate_name::MergedObjectTail };
74 for ty in &types {
75 obj = quote! { #crate_name::MergedObject::<#ty, #obj> };
76 }
77 obj
78 };
79
80 let visible = visible_fn(&object_args.visible);
81 let resolve_container = if object_args.serial {
82 quote! { #crate_name::resolver_utils::resolve_container_serial(ctx, self).await }
83 } else {
84 quote! { #crate_name::resolver_utils::resolve_container(ctx, self).await }
85 };
86
87 let expanded = quote! {
88 #[allow(clippy::all, clippy::pedantic)]
89 #boxed_trait
90 impl #impl_generics #crate_name::resolver_utils::ContainerType for #ident #ty_generics #where_clause {
91 async fn resolve_field(&self, ctx: &#crate_name::Context<'_>) -> #crate_name::ServerResult<::std::option::Option<#crate_name::Value>> {
92 #create_merged_obj.resolve_field(ctx).await
93 }
94
95 async fn find_entity(&self, ctx: &#crate_name::Context<'_>, params: &#crate_name::Value) -> #crate_name::ServerResult<::std::option::Option<#crate_name::Value>> {
96 #create_merged_obj.find_entity(ctx, params).await
97 }
98 }
99
100 #[allow(clippy::all, clippy::pedantic)]
101 #boxed_trait
102 impl #impl_generics #crate_name::OutputType for #ident #ty_generics #where_clause {
103 fn type_name() -> ::std::borrow::Cow<'static, ::std::primitive::str> {
104 #gql_typename
105 }
106
107 fn create_type_info(registry: &mut #crate_name::registry::Registry) -> ::std::string::String {
108 registry.create_output_type::<Self, _>(#crate_name::registry::MetaTypeId::Object, |registry| {
109 let mut fields = ::std::default::Default::default();
110 let mut cache_control = ::std::default::Default::default();
111
112 if let #crate_name::registry::MetaType::Object {
113 fields: obj_fields,
114 cache_control: obj_cache_control,
115 ..
116 } = registry.create_fake_output_type::<#merged_type>() {
117 fields = obj_fields;
118 cache_control = obj_cache_control;
119 }
120
121 #crate_name::registry::MetaType::Object {
122 name: ::std::borrow::Cow::into_owned(#gql_typename),
123 description: #desc,
124 fields,
125 cache_control,
126 extends: #extends,
127 shareable: #shareable,
128 resolvable: true,
129 inaccessible: #inaccessible,
130 interface_object: #interface_object,
131 tags: ::std::vec![ #(#tags),* ],
132 keys: ::std::option::Option::None,
133 visible: #visible,
134 is_subscription: false,
135 rust_typename: ::std::option::Option::Some(::std::any::type_name::<Self>()),
136 directive_invocations: ::std::vec![ #(#directives),* ],
137 requires_scopes: ::std::vec![],
138 }
139 })
140 }
141
142 async fn resolve(&self, ctx: &#crate_name::ContextSelectionSet<'_>, _field: &#crate_name::Positioned<#crate_name::parser::types::Field>) -> #crate_name::ServerResult<#crate_name::Value> {
143 #resolve_container
144 }
145 }
146
147 impl #impl_generics #crate_name::ObjectType for #ident #ty_generics #where_clause {}
148 };
149 Ok(expanded.into())
150}