Skip to content

Commit ad68836

Browse files
committed
Factor response_for_query out of schema.rs
1 parent 5aed14a commit ad68836

File tree

4 files changed

+186
-219
lines changed

4 files changed

+186
-219
lines changed

graphql_query_derive/src/codegen.rs

Lines changed: 149 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,155 @@
1-
use field_type::FieldType;
2-
use std::collections::BTreeMap;
3-
4-
#[derive(Debug)]
5-
pub struct StructFieldDescriptor {
6-
pub attributes: TokenStream,
7-
pub name: String,
8-
pub ty: FieldType,
9-
}
1+
use constants::*;
2+
use failure;
3+
use fragments::GqlFragment;
4+
use graphql_parser::query;
5+
use proc_macro2::TokenStream;
6+
use query::QueryContext;
7+
use schema;
8+
use selection::Selection;
109

11-
#[derive(Debug)]
12-
pub struct StructDescriptor {
13-
pub attributes: TokenStream,
14-
pub fields: Vec<StructFieldDescriptor>,
15-
pub name: String,
16-
}
10+
pub(crate) fn response_for_query(
11+
schema: schema::Schema,
12+
query: query::Document,
13+
) -> Result<TokenStream, failure::Error> {
14+
let mut context = QueryContext::new(schema);
15+
let mut definitions = Vec::new();
16+
17+
for definition in query.definitions {
18+
match definition {
19+
query::Definition::Operation(query::OperationDefinition::Query(q)) => {
20+
context.root = {
21+
let definition = context
22+
.schema
23+
.query_type
24+
.clone()
25+
.and_then(|query_type| context.schema.objects.get(&query_type))
26+
.expect("query type is defined");
27+
let prefix = &q.name.expect("unnamed operation");
28+
let prefix = format!("RUST_{}", prefix);
29+
let selection = Selection::from(&q.selection_set);
30+
31+
definitions.extend(
32+
definition.field_impls_for_selection(&context, &selection, &prefix)?,
33+
);
34+
Some(definition.response_fields_for_selection(&context, &selection, &prefix)?)
35+
};
36+
37+
context.register_variables(&q.variable_definitions);
38+
}
39+
query::Definition::Operation(query::OperationDefinition::Mutation(q)) => {
40+
context.root = {
41+
let definition = context
42+
.schema
43+
.mutation_type
44+
.clone()
45+
.and_then(|mutation_type| context.schema.objects.get(&mutation_type))
46+
.expect("mutation type is defined");
47+
let prefix = &q.name.expect("unnamed operation");
48+
let prefix = format!("RUST_{}", prefix);
49+
let selection = Selection::from(&q.selection_set);
50+
51+
definitions.extend(
52+
definition.field_impls_for_selection(&context, &selection, &prefix)?,
53+
);
54+
Some(definition.response_fields_for_selection(&context, &selection, &prefix)?)
55+
};
1756

18-
impl StructDescriptor {
19-
pub fn field_by_name(&self, name: &str) -> Option<StructFieldDescriptor> {
20-
unimplemented!()
57+
context.register_variables(&q.variable_definitions);
58+
}
59+
query::Definition::Operation(query::OperationDefinition::Subscription(q)) => {
60+
context.root = {
61+
let definition = context
62+
.schema
63+
.subscription_type
64+
.clone()
65+
.and_then(|subscription_type| {
66+
context.schema.objects.get(&subscription_type)
67+
})
68+
.expect("subscription type is defined");
69+
let prefix = &q.name.expect("unnamed operation");
70+
let prefix = format!("RUST_{}", prefix);
71+
let selection = Selection::from(&q.selection_set);
72+
73+
if selection.0.len() > 1 {
74+
Err(format_err!(
75+
"{}",
76+
::constants::MULTIPLE_SUBSCRIPTION_FIELDS_ERROR
77+
))?
78+
}
79+
80+
definitions.extend(
81+
definition.field_impls_for_selection(&context, &selection, &prefix)?,
82+
);
83+
Some(definition.response_fields_for_selection(&context, &selection, &prefix)?)
84+
};
85+
86+
context.register_variables(&q.variable_definitions);
87+
}
88+
query::Definition::Operation(query::OperationDefinition::SelectionSet(_)) => {
89+
panic!(SELECTION_SET_AT_ROOT)
90+
}
91+
query::Definition::Fragment(fragment) => {
92+
let query::TypeCondition::On(on) = fragment.type_condition;
93+
context.fragments.insert(
94+
fragment.name.clone(),
95+
GqlFragment {
96+
name: fragment.name,
97+
selection: Selection::from(&fragment.selection_set),
98+
on,
99+
},
100+
);
101+
}
102+
}
21103
}
22-
}
23104

24-
pub struct EnumVariantDescriptor {
25-
pub attributes: TokenStream,
26-
pub name: String,
27-
}
105+
let enum_definitions = context.schema.enums.values().map(|enm| enm.to_rust());
106+
let fragment_definitions: Result<Vec<TokenStream>, _> = context
107+
.fragments
108+
.values()
109+
.map(|fragment| fragment.to_rust(&context))
110+
.collect();
111+
let fragment_definitions = fragment_definitions?;
112+
let variables_struct = context.expand_variables();
113+
let response_data_fields = context.root.as_ref().expect("no selection defined");
114+
115+
let input_object_definitions: Result<Vec<TokenStream>, _> = context
116+
.schema
117+
.inputs
118+
.values()
119+
.map(|i| i.to_rust(&context))
120+
.collect();
121+
let input_object_definitions = input_object_definitions?;
122+
123+
let scalar_definitions: Vec<TokenStream> = context
124+
.schema
125+
.scalars
126+
.values()
127+
.map(|s| s.to_rust())
128+
.collect();
129+
130+
Ok(quote! {
131+
type Boolean = bool;
132+
type Float = f64;
133+
type Int = i64;
134+
type ID = String;
135+
136+
#(#scalar_definitions)*
137+
138+
#(#input_object_definitions)*
139+
140+
#(#enum_definitions)*
141+
142+
#(#fragment_definitions)*
143+
144+
#(#definitions)*
145+
146+
#variables_struct
147+
148+
#[derive(Debug, Serialize, Deserialize)]
149+
#[serde(rename_all = "camelCase")]
150+
pub struct ResponseData {
151+
#(#response_data_fields)*,
152+
}
28153

29-
#[derive(Debug)]
30-
pub struct EnumDescriptor {
31-
pub name: String,
32-
pub variants: Vec<String>,
154+
})
33155
}

graphql_query_derive/src/constants.rs

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@ use field_type::FieldType;
22
use objects::GqlObjectField;
33
use proc_macro2::{Ident, Span};
44

5-
pub const TYPENAME_FIELD: &str = "__typename";
5+
pub(crate) const TYPENAME_FIELD: &str = "__typename";
66

7-
pub fn string_type() -> Ident {
7+
pub(crate) fn string_type() -> Ident {
88
Ident::new("String", Span::call_site())
99
}
1010

1111
#[cfg(test)]
12-
pub fn float_type() -> Ident {
12+
pub(crate) fn float_type() -> Ident {
1313
Ident::new("Float", Span::call_site())
1414
}
1515

16-
pub fn typename_field() -> GqlObjectField {
16+
pub(crate) fn typename_field() -> GqlObjectField {
1717
GqlObjectField {
1818
description: None,
1919
name: TYPENAME_FIELD.to_string(),
@@ -23,8 +23,37 @@ pub fn typename_field() -> GqlObjectField {
2323
}
2424
}
2525

26-
pub const MULTIPLE_SUBSCRIPTION_FIELDS_ERROR: &str = r##"
26+
pub(crate) const MULTIPLE_SUBSCRIPTION_FIELDS_ERROR: &str = r##"
2727
Multiple-field queries on the root subscription field are forbidden by the spec.
2828
2929
See: https://github.com/facebook/graphql/blob/master/spec/Section%205%20--%20Validation.md#subscription-operation-definitions
3030
"##;
31+
32+
/// Error message when a selection set is the root of a query.
33+
pub(crate) const SELECTION_SET_AT_ROOT: &str = r#"
34+
Operations in queries must be named.
35+
36+
Instead of this:
37+
38+
{
39+
user {
40+
name
41+
repositories {
42+
name
43+
commits
44+
}
45+
}
46+
}
47+
48+
Write this:
49+
50+
query UserRepositories {
51+
user {
52+
name
53+
repositories {
54+
name
55+
commits
56+
}
57+
}
58+
}
59+
"#;

graphql_query_derive/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ extern crate quote;
1616

1717
use proc_macro2::TokenStream;
1818

19+
mod codegen;
1920
mod constants;
2021
mod enums;
2122
mod field_type;
@@ -106,7 +107,7 @@ fn impl_gql_query(input: &syn::DeriveInput) -> Result<TokenStream, failure::Erro
106107

107108
let module_name = Ident::new(&input.ident.to_string().to_snake_case(), Span::call_site());
108109
let struct_name = &input.ident;
109-
let schema_output = schema.response_for_query(query)?;
110+
let schema_output = codegen::response_for_query(schema, query)?;
110111

111112
let result = quote!(
112113
pub mod #module_name {

0 commit comments

Comments
 (0)