Skip to content

Commit a4389c8

Browse files
committed
Allow using #[derive(GraphQLQuery)] without depending on serde
This PR makes the serde derives emitted by `#[derive(GraphQLQuery)]` use a private re-export of the serde crate instead of relying on users of this crate having added serde to their `Cargo.toml`. Serde allows you to add an annotation #[serde(crate = "<path to crate>")] for exactly this reason so most of this PR is just plumbing the correct path through the codegen crate to the correct annotation. Details ------- - There is a new `#[doc(hidden)] mod _private` declaration in the `graphql_client` crate. All re-exports (which is just serde) have been placed there. - I have added a new `serde_path` field to `GraphQLCodegenOptions` which defaults to `::serde`. This means that the code generated by the CLI should remain effectively unchanged. - The rest is just applying `#[serde(crate = ...)]` annotations where appropriate. Fixes #427
1 parent 89c4cc2 commit a4389c8

File tree

10 files changed

+46
-13
lines changed

10 files changed

+46
-13
lines changed

examples/github/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ edition = "2018"
77
[dev-dependencies]
88
anyhow = "1.0"
99
graphql_client = { path = "../../graphql_client", features = ["reqwest-blocking"] }
10-
serde = "^1.0"
1110
reqwest = { version = "0.12", features = ["json", "blocking"] }
1211
prettytable-rs = "^0.10.0"
1312
clap = { version = "^3.0", features = ["derive"] }

examples/hasura/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ edition = "2018"
77
[dev-dependencies]
88
anyhow = "1.0"
99
graphql_client = { path = "../../graphql_client", features = ["reqwest-blocking"] }
10-
serde = { version = "1.0", features = ["derive"] }
1110
serde_json = "1.0"
1211
reqwest = { version = "0.12", features = ["json", "blocking"] }
1312
prettytable-rs = "0.10.0"

examples/web/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ crate-type = ["cdylib", "rlib"]
1010
[dependencies]
1111
graphql_client = { path = "../../graphql_client", features = ["reqwest"] }
1212
wasm-bindgen = "^0.2"
13-
serde = { version = "1.0.67", features = ["derive"] }
1413
lazy_static = "1.0.1"
1514
js-sys = "0.3.6"
1615
wasm-bindgen-futures = "0.4.18"

graphql_client/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,12 @@ pub struct Response<Data> {
299299
pub extensions: Option<HashMap<String, serde_json::Value>>,
300300
}
301301

302+
/// Hidden module for types used by the codegen crate.
303+
#[doc(hidden)]
304+
pub mod _private {
305+
pub use ::serde;
306+
}
307+
302308
#[cfg(test)]
303309
mod tests {
304310
use super::*;

graphql_client_codegen/src/codegen.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::{
1111
};
1212
use heck::ToSnakeCase;
1313
use proc_macro2::{Ident, Span, TokenStream};
14-
use quote::quote;
14+
use quote::{quote, ToTokens};
1515
use selection::*;
1616
use std::collections::BTreeMap;
1717

@@ -21,6 +21,8 @@ pub(crate) fn response_for_query(
2121
options: &GraphQLClientCodegenOptions,
2222
query: BoundQuery<'_>,
2323
) -> Result<TokenStream, GeneralError> {
24+
let serde = options.serde_path();
25+
2426
let all_used_types = all_used_types(operation_id, &query);
2527
let response_derives = render_derives(options.all_response_derives());
2628
let variable_derives = render_derives(options.all_variable_derives());
@@ -43,7 +45,7 @@ pub(crate) fn response_for_query(
4345
render_response_data_fields(operation_id, options, &query).render(&response_derives);
4446

4547
let q = quote! {
46-
use serde::{Serialize, Deserialize};
48+
use #serde::{Serialize, Deserialize};
4749
use super::*;
4850

4951
#[allow(dead_code)]
@@ -77,9 +79,13 @@ fn generate_variables_struct(
7779
options: &GraphQLClientCodegenOptions,
7880
query: &BoundQuery<'_>,
7981
) -> TokenStream {
82+
let serde = options.serde_path();
83+
let serde_path = serde.to_token_stream().to_string();
84+
8085
if operation_has_no_variables(operation_id, query.query) {
8186
return quote!(
8287
#variable_derives
88+
#[serde(crate = #serde_path)]
8389
pub struct Variables;
8490
);
8591
}
@@ -115,6 +121,7 @@ fn generate_variables_struct(
115121

116122
let variables_struct = quote!(
117123
#variable_derives
124+
#[serde(crate = #serde_path)]
118125
pub struct Variables {
119126
#(#variable_fields,)*
120127
}

graphql_client_codegen/src/codegen/enums.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub(super) fn generate_enum_definitions<'a, 'schema: 'a>(
1616
options: &'a GraphQLClientCodegenOptions,
1717
query: BoundQuery<'schema>,
1818
) -> impl Iterator<Item = TokenStream> + 'a {
19+
let serde = options.serde_path();
1920
let traits = options
2021
.all_response_derives()
2122
.chain(options.all_variable_derives())
@@ -66,18 +67,18 @@ pub(super) fn generate_enum_definitions<'a, 'schema: 'a>(
6667
Other(String),
6768
}
6869

69-
impl ::serde::Serialize for #name {
70-
fn serialize<S: serde::Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
70+
impl #serde::Serialize for #name {
71+
fn serialize<S: #serde::Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
7172
ser.serialize_str(match *self {
7273
#(#constructors => #variant_str,)*
7374
#name::Other(ref s) => &s,
7475
})
7576
}
7677
}
7778

78-
impl<'de> ::serde::Deserialize<'de> for #name {
79-
fn deserialize<D: ::serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
80-
let s: String = ::serde::Deserialize::deserialize(deserializer)?;
79+
impl<'de> #serde::Deserialize<'de> for #name {
80+
fn deserialize<D: #serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
81+
let s: String = #serde::Deserialize::deserialize(deserializer)?;
8182

8283
match s.as_str() {
8384
#(#variant_str => Ok(#constructors),)*

graphql_client_codegen/src/codegen/inputs.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::{
77
};
88
use heck::{ToSnakeCase, ToUpperCamelCase};
99
use proc_macro2::{Ident, Span, TokenStream};
10-
use quote::quote;
10+
use quote::{quote, ToTokens};
1111

1212
pub(super) fn generate_input_object_definitions(
1313
all_used_types: &UsedTypes,
@@ -33,6 +33,9 @@ fn generate_struct(
3333
variable_derives: &impl quote::ToTokens,
3434
query: &BoundQuery<'_>,
3535
) -> TokenStream {
36+
let serde = options.serde_path();
37+
let serde_path = serde.to_token_stream().to_string();
38+
3639
let normalized_name = options.normalization().input_name(input.name.as_str());
3740
let safe_name = keyword_replace(normalized_name);
3841
let struct_name = Ident::new(safe_name.as_ref(), Span::call_site());
@@ -71,6 +74,7 @@ fn generate_struct(
7174

7275
quote! {
7376
#variable_derives
77+
#[serde(crate = #serde_path)]
7478
pub struct #struct_name{
7579
#(#fields,)*
7680
}

graphql_client_codegen/src/codegen/selection.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::{
1616
};
1717
use heck::*;
1818
use proc_macro2::{Ident, Span, TokenStream};
19-
use quote::quote;
19+
use quote::{quote, ToTokens};
2020
use std::borrow::Cow;
2121

2222
pub(crate) fn render_response_data_fields<'a>(
@@ -433,7 +433,7 @@ impl ExpandedField<'_> {
433433
(Some(msg), DeprecationStrategy::Warn) => {
434434
let optional_msg = msg.map(|msg| quote!((note = #msg)));
435435

436-
Some(quote!(#[deprecated#optional_msg]))
436+
Some(quote!(#[deprecated #optional_msg]))
437437
}
438438
(Some(_), DeprecationStrategy::Deny) => return None,
439439
};
@@ -532,6 +532,9 @@ impl<'a> ExpandedSelection<'a> {
532532
}
533533

534534
pub fn render(&self, response_derives: &impl quote::ToTokens) -> TokenStream {
535+
let serde = self.options.serde_path();
536+
let serde_path = serde.to_token_stream().to_string();
537+
535538
let mut items = Vec::with_capacity(self.types.len());
536539

537540
for (type_id, ty) in self.types() {
@@ -600,6 +603,7 @@ impl<'a> ExpandedSelection<'a> {
600603

601604
let tokens = quote! {
602605
#response_derives
606+
#[serde(crate = #serde_path)]
603607
pub struct #struct_name {
604608
#(#fields,)*
605609
#on_field

graphql_client_codegen/src/codegen_options.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ pub struct GraphQLClientCodegenOptions {
4747
fragments_other_variant: bool,
4848
/// Skip Serialization of None values.
4949
skip_serializing_none: bool,
50+
/// Path to the serde crate.
51+
serde_path: syn::Path,
5052
}
5153

5254
impl GraphQLClientCodegenOptions {
@@ -68,6 +70,7 @@ impl GraphQLClientCodegenOptions {
6870
extern_enums: Default::default(),
6971
fragments_other_variant: Default::default(),
7072
skip_serializing_none: Default::default(),
73+
serde_path: syn::parse_quote!(::serde),
7174
}
7275
}
7376

@@ -227,4 +230,14 @@ impl GraphQLClientCodegenOptions {
227230
pub fn skip_serializing_none(&self) -> &bool {
228231
&self.skip_serializing_none
229232
}
233+
234+
/// Set the path to used to resolve serde traits.
235+
pub fn set_serde_path(&mut self, path: syn::Path) {
236+
self.serde_path = path;
237+
}
238+
239+
/// Get a reference to the path used to resolve serde traits.
240+
pub fn serde_path(&self) -> &syn::Path {
241+
&self.serde_path
242+
}
230243
}

graphql_query_derive/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ fn build_graphql_client_derive_options(
104104
options.set_struct_ident(input.ident.clone());
105105
options.set_module_visibility(input.vis.clone());
106106
options.set_operation_name(input.ident.to_string());
107+
options.set_serde_path(syn::parse_quote!(graphql_client::_private::serde));
107108

108109
Ok(options)
109110
}

0 commit comments

Comments
 (0)