Skip to content

Commit 4bfd3ac

Browse files
committed
Extract object field codegen logic out of GqlObject
We are going to use it for interfaces.
1 parent e9456d1 commit 4bfd3ac

File tree

6 files changed

+94
-79
lines changed

6 files changed

+94
-79
lines changed

graphql_query_derive/src/constants.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,3 @@ pub const TYPENAME_FIELD: &'static str = "__typename";
55
pub fn string_type() -> Ident {
66
Ident::new("String", Span::call_site())
77
}
8-
9-
pub fn float_type() -> Ident {
10-
Ident::new("Float", Span::call_site())
11-
}

graphql_query_derive/src/fragments.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,19 @@ pub struct GqlFragment {
1010
}
1111

1212
impl GqlFragment {
13-
pub fn to_rust(&self, context: &QueryContext) -> TokenStream {
13+
pub fn to_rust(&self, context: &QueryContext) -> Result<TokenStream, ::failure::Error> {
1414
let name_ident = Ident::new(&self.name, Span::call_site());
1515
let object = context.schema.objects.get(&self.on).expect("oh, noes");
16-
let field_impls = object
17-
.field_impls_for_selection(context, &self.selection, &self.name)
18-
.unwrap();
19-
let fields = object.response_fields_for_selection(context, &self.selection, &self.name);
16+
let field_impls = object.field_impls_for_selection(context, &self.selection, &self.name)?;
17+
let fields = object.response_fields_for_selection(context, &self.selection, &self.name)?;
2018

21-
quote!{
19+
Ok(quote!{
2220
#[derive(Debug, Deserialize, Serialize)]
2321
pub struct #name_ident {
2422
#(#fields,)*
2523
}
2624

2725
#(#field_impls)*
28-
}
26+
})
2927
}
3028
}

graphql_query_derive/src/lib.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,6 @@ fn read_file(
6666
}
6767

6868
fn impl_gql_query(input: &syn::DeriveInput) -> Result<TokenStream, failure::Error> {
69-
use std::io::prelude::*;
70-
7169
let cargo_manifest_dir =
7270
::std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR env variable is defined");
7371

graphql_query_derive/src/objects.rs

Lines changed: 5 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
use constants::*;
22
use failure;
33
use field_type::FieldType;
4-
use heck::{CamelCase, SnakeCase};
54
use proc_macro2::{Ident, Span, TokenStream};
65
use query::QueryContext;
76
use selection::*;
8-
use shared::render_object_field;
7+
use shared::{field_impls_for_selection, response_fields_for_selection};
98
use std::borrow::Cow;
109

1110
#[derive(Debug, PartialEq)]
@@ -64,7 +63,7 @@ impl GqlObject {
6463
prefix: &str,
6564
) -> Result<TokenStream, failure::Error> {
6665
let name = Ident::new(prefix, Span::call_site());
67-
let fields = self.response_fields_for_selection(query_context, selection, prefix);
66+
let fields = self.response_fields_for_selection(query_context, selection, prefix)?;
6867
let field_impls = self.field_impls_for_selection(query_context, selection, &prefix)?;
6968
Ok(quote! {
7069
#(#field_impls)*
@@ -82,69 +81,15 @@ impl GqlObject {
8281
selection: &Selection,
8382
prefix: &str,
8483
) -> Result<Vec<TokenStream>, failure::Error> {
85-
selection
86-
.0
87-
.iter()
88-
.map(|selected| {
89-
if let SelectionItem::Field(selected) = selected {
90-
let ty = self
91-
.fields
92-
.iter()
93-
.find(|f| f.name == selected.name)
94-
.ok_or_else(|| format_err!("could not find field `{}`", selected.name))?
95-
.type_
96-
.inner_name_string();
97-
let prefix = format!(
98-
"{}{}",
99-
prefix.to_camel_case(),
100-
selected.name.to_camel_case()
101-
);
102-
query_context.maybe_expand_field(&ty, &selected.fields, &prefix)
103-
} else {
104-
Ok(quote!())
105-
}
106-
})
107-
.collect()
84+
field_impls_for_selection(&self.fields, query_context, selection, prefix)
10885
}
10986

11087
pub fn response_fields_for_selection(
11188
&self,
11289
query_context: &QueryContext,
11390
selection: &Selection,
11491
prefix: &str,
115-
) -> Vec<TokenStream> {
116-
let mut fields = Vec::new();
117-
118-
for item in selection.0.iter() {
119-
match item {
120-
SelectionItem::Field(f) => {
121-
let name = &f.name;
122-
123-
let ty = &self
124-
.fields
125-
.iter()
126-
.find(|field| field.name.as_str() == name.as_str())
127-
.expect("could not find field")
128-
.type_;
129-
let ty = ty.to_rust(
130-
query_context,
131-
&format!("{}{}", prefix.to_camel_case(), name.to_camel_case()),
132-
);
133-
fields.push(render_object_field(name, ty));
134-
}
135-
SelectionItem::FragmentSpread(fragment) => {
136-
let field_name =
137-
Ident::new(&fragment.fragment_name.to_snake_case(), Span::call_site());
138-
let type_name = Ident::new(&fragment.fragment_name, Span::call_site());
139-
fields.push(quote!{
140-
#[serde(flatten)]
141-
#field_name: #type_name
142-
})
143-
}
144-
SelectionItem::InlineFragment(_) => unreachable!("inline fragment on object field"),
145-
}
146-
}
147-
148-
fields
92+
) -> Result<Vec<TokenStream>, failure::Error> {
93+
response_fields_for_selection(&self.fields, query_context, selection, prefix)
14994
}
15095
}

graphql_query_derive/src/schema.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ impl Schema {
6464
definition.field_impls_for_selection(&context, &selection, &prefix)?,
6565
);
6666
Some(
67-
definition.response_fields_for_selection(&context, &selection, &prefix),
67+
definition
68+
.response_fields_for_selection(&context, &selection, &prefix)?,
6869
)
6970
};
7071

@@ -86,7 +87,8 @@ impl Schema {
8687
definition.field_impls_for_selection(&context, &selection, &prefix)?,
8788
);
8889
Some(
89-
definition.response_fields_for_selection(&context, &selection, &prefix),
90+
definition
91+
.response_fields_for_selection(&context, &selection, &prefix)?,
9092
)
9193
};
9294

@@ -110,7 +112,8 @@ impl Schema {
110112
definition.field_impls_for_selection(&context, &selection, &prefix)?,
111113
);
112114
Some(
113-
definition.response_fields_for_selection(&context, &selection, &prefix),
115+
definition
116+
.response_fields_for_selection(&context, &selection, &prefix)?,
114117
)
115118
};
116119

@@ -134,10 +137,12 @@ impl Schema {
134137
}
135138

136139
let enum_definitions = context.schema.enums.values().map(|enm| enm.to_rust());
137-
let fragment_definitions = context
140+
let fragment_definitions: Result<Vec<TokenStream>, _> = context
138141
.fragments
139142
.values()
140-
.map(|fragment| fragment.to_rust(&context));
143+
.map(|fragment| fragment.to_rust(&context))
144+
.collect();
145+
let fragment_definitions = fragment_definitions?;
141146
let variables_struct = context.expand_variables();
142147
let response_data_fields = context
143148
.query_root

graphql_query_derive/src/shared.rs

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
use failure;
2+
use heck::{CamelCase, SnakeCase};
3+
use objects::GqlObjectField;
14
use proc_macro2::{Ident, Span, TokenStream};
5+
use query::QueryContext;
6+
use selection::*;
27

3-
pub fn render_object_field(field_name: &str, field_type: TokenStream) -> TokenStream {
8+
pub(crate) fn render_object_field(field_name: &str, field_type: TokenStream) -> TokenStream {
49
if field_name == "type" {
510
let name_ident = Ident::new(&format!("{}_", field_name), Span::call_site());
611
return quote! {
@@ -13,3 +18,71 @@ pub fn render_object_field(field_name: &str, field_type: TokenStream) -> TokenSt
1318

1419
quote!(pub #name_ident: #field_type)
1520
}
21+
22+
pub(crate) fn field_impls_for_selection(
23+
fields: &[GqlObjectField],
24+
context: &QueryContext,
25+
selection: &Selection,
26+
prefix: &str,
27+
) -> Result<Vec<TokenStream>, failure::Error> {
28+
selection
29+
.0
30+
.iter()
31+
.map(|selected| {
32+
if let SelectionItem::Field(selected) = selected {
33+
let ty = fields
34+
.iter()
35+
.find(|f| f.name == selected.name)
36+
.ok_or_else(|| format_err!("could not find field `{}`", selected.name))?
37+
.type_
38+
.inner_name_string();
39+
let prefix = format!(
40+
"{}{}",
41+
prefix.to_camel_case(),
42+
selected.name.to_camel_case()
43+
);
44+
context.maybe_expand_field(&ty, &selected.fields, &prefix)
45+
} else {
46+
Ok(quote!())
47+
}
48+
})
49+
.collect()
50+
}
51+
52+
pub fn response_fields_for_selection(
53+
schema_fields: &[GqlObjectField],
54+
context: &QueryContext,
55+
selection: &Selection,
56+
prefix: &str,
57+
) -> Result<Vec<TokenStream>, failure::Error> {
58+
selection
59+
.0
60+
.iter()
61+
.map(|item| match item {
62+
SelectionItem::Field(f) => {
63+
let name = &f.name;
64+
65+
let ty = &schema_fields
66+
.iter()
67+
.find(|field| field.name.as_str() == name.as_str())
68+
.ok_or(format_err!("Could not find field: {}", name.as_str()))?
69+
.type_;
70+
let ty = ty.to_rust(
71+
context,
72+
&format!("{}{}", prefix.to_camel_case(), name.to_camel_case()),
73+
);
74+
Ok(render_object_field(name, ty))
75+
}
76+
SelectionItem::FragmentSpread(fragment) => {
77+
let field_name =
78+
Ident::new(&fragment.fragment_name.to_snake_case(), Span::call_site());
79+
let type_name = Ident::new(&fragment.fragment_name, Span::call_site());
80+
Ok(quote!{
81+
#[serde(flatten)]
82+
#field_name: #type_name
83+
})
84+
}
85+
SelectionItem::InlineFragment(_) => unreachable!("inline fragment on object field"),
86+
})
87+
.collect()
88+
}

0 commit comments

Comments
 (0)