async_graphql_derive/
merged_object.rs

1use 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}