cynic_codegen/query_variables_derive/
mod.rs1use {
2 proc_macro2::TokenStream,
3 quote::{format_ident, quote, quote_spanned},
4 syn::visit_mut::{self, VisitMut},
5};
6
7mod input;
8
9use crate::{generics_for_serde, variables_fields_ident};
10
11use self::input::QueryVariablesDeriveInput;
12
13pub fn query_variables_derive(ast: &syn::DeriveInput) -> Result<TokenStream, syn::Error> {
14 use darling::FromDeriveInput;
15
16 match QueryVariablesDeriveInput::from_derive_input(ast) {
17 Ok(input) => query_variables_derive_impl(input),
18 Err(e) => Ok(e.write_errors()),
19 }
20}
21
22pub fn query_variables_derive_impl(
23 input: QueryVariablesDeriveInput,
24) -> Result<TokenStream, syn::Error> {
25 let ident = &input.ident;
26
27 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
28 let generics_with_ser = generics_for_serde::with_serialize_bounds(&input.generics);
29 let (impl_generics_with_ser, _, where_clause_with_ser) = generics_with_ser.split_for_impl();
30
31 let vis = &input.vis;
32 let schema_module = &input.schema_module();
33 let fields_struct_ident = variables_fields_ident(ident);
34
35 let input_fields = input.data.take_struct().unwrap().fields;
36
37 let mut field_funcs = Vec::new();
38 let mut variables = Vec::new();
39 let mut field_inserts = Vec::new();
40 let mut coercion_checks = Vec::new();
41 let mut field_output_types = Vec::new();
42
43 for (field_idx, f) in input_fields.into_iter().enumerate() {
44 let name = f.ident.as_ref().unwrap();
45 let ty = &f.ty;
46 let mut ty_for_fields_struct = match f.graphql_type {
47 None => ty.clone(),
48 Some(ref graphql_type) => {
49 let graphql_type_full_path = match graphql_type.get_ident() {
71 Some(ident) => {
72 let span = ident.span();
76 quote_spanned! {span =>
77 #schema_module::#ident
78 }
79 }
80 None => quote! { #graphql_type },
81 };
82
83 let new_type_that_coerces_to_schema_type =
84 format_ident!("CoercionProxyForField{field_idx}");
85 field_output_types.push(quote! {
86 #vis struct #new_type_that_coerces_to_schema_type;
87 cynic::impl_coercions!(#new_type_that_coerces_to_schema_type [] [], #graphql_type_full_path);
88 });
89 coercion_checks.push(quote! {
90 cynic::assert_impl!(#ty [#impl_generics] [#where_clause]: cynic::coercions::CoercesTo<#graphql_type_full_path>);
91 });
92
93 syn::parse_quote! { #new_type_that_coerces_to_schema_type }
95 }
96 };
97 TurnLifetimesToStatic.visit_type_mut(&mut ty_for_fields_struct);
98 let name_str =
99 proc_macro2::Literal::string(&f.graphql_ident(input.rename_all).graphql_name());
100
101 field_funcs.push(quote! {
102 #vis fn #name() -> cynic::variables::VariableDefinition<Self, #ty_for_fields_struct> {
103 cynic::variables::VariableDefinition::new(#name_str)
104 }
105 });
106
107 variables.push(quote! {
108 (#name_str, <#ty as #schema_module::variable::Variable>::TYPE)
109 });
110
111 match f.skip_serializing_if {
112 Some(skip_check_fn) => {
113 let skip_check_fn = &*skip_check_fn;
114 field_inserts.push(quote! {
115 if !#skip_check_fn(&self.#name) {
116 map_serializer.serialize_entry(#name_str, &self.#name)?;
117 }
118 })
119 }
120 None => field_inserts.push(quote! {
121 map_serializer.serialize_entry(#name_str, &self.#name)?;
122 }),
123 }
124 }
125
126 let map_len = field_inserts.len();
127
128 let ident_span = ident.span();
129 let fields_struct = quote_spanned! { ident_span =>
130 #vis struct #fields_struct_ident;
131
132 impl cynic::QueryVariablesFields for #fields_struct_ident {}
133
134 impl cynic::queries::VariableMatch<#fields_struct_ident> for #fields_struct_ident {}
135
136 const _: () = {
137 #(
138 #field_output_types
139 )*
140
141 impl #fields_struct_ident {
142 #(
143 #field_funcs
144 )*
145 }
146 };
147 };
148
149 Ok(quote! {
150
151 #[automatically_derived]
152 impl #impl_generics cynic::QueryVariables for #ident #ty_generics #where_clause {
153 type Fields = #fields_struct_ident;
154 const VARIABLES: &'static [(&'static str, cynic::variables::VariableType)]
155 = &[#(#variables),*];
156 }
157
158 #[automatically_derived]
159 impl #impl_generics_with_ser cynic::serde::Serialize for #ident #ty_generics #where_clause_with_ser {
160 fn serialize<__S>(&self, serializer: __S) -> Result<__S::Ok, __S::Error>
161 where
162 __S: cynic::serde::Serializer,
163 {
164 use cynic::serde::ser::SerializeMap;
165 #(#coercion_checks)*
166
167 let mut map_serializer = serializer.serialize_map(Some(#map_len))?;
168
169 #(#field_inserts)*
170
171 map_serializer.end()
172 }
173 }
174
175 #fields_struct
176 })
177}
178
179struct TurnLifetimesToStatic;
180impl VisitMut for TurnLifetimesToStatic {
181 fn visit_lifetime_mut(&mut self, i: &mut syn::Lifetime) {
182 i.ident = format_ident!("static");
183 visit_mut::visit_lifetime_mut(self, i)
184 }
185}