Skip to content

Commit d28f9a3

Browse files
committed
WIP - Start working on recursive, lazy codegen
1 parent dfb795a commit d28f9a3

File tree

7 files changed

+149
-57
lines changed

7 files changed

+149
-57
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1+
use field_type::FieldType;
2+
use graphql_parser::query;
3+
use objects::GqlObjectField;
4+
use proc_macro2::{Ident, Span, TokenStream};
5+
use query::QueryContext;
6+
7+
#[derive(Debug)]
18
pub struct GqlInterface {
29
pub implemented_by: Vec<String>,
10+
pub name: String,
11+
pub fields: Vec<GqlObjectField>,
12+
}
13+
14+
impl GqlInterface {
15+
pub fn response_for_selection(
16+
&self,
17+
query_context: &QueryContext,
18+
selection: &query::SelectionSet,
19+
prefix: &str,
20+
) -> TokenStream {
21+
let name = Ident::new(&format!("{}{}", prefix, self.name), Span::call_site());
22+
quote! {
23+
#[derive(Debug, Deserialize)]
24+
pub struct #name;
25+
}
26+
}
327
}

graphql_query_derive/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#![recursion_limit = "128"]
2+
#![feature(nll)]
23

34
#[macro_use]
45
extern crate failure;
@@ -20,6 +21,7 @@ mod interfaces;
2021
mod objects;
2122
mod query;
2223
mod schema;
24+
mod unions;
2325

2426
use heck::*;
2527
use proc_macro2::{Ident, Span};

graphql_query_derive/src/objects.rs

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use field_type::FieldType;
22
use graphql_parser::query;
3+
use heck::SnakeCase;
34
use proc_macro2::{Ident, Span, TokenStream};
45
use query::QueryContext;
56

@@ -20,17 +21,48 @@ impl GqlObject {
2021
&self,
2122
query_context: &QueryContext,
2223
selection: &query::SelectionSet,
24+
prefix: &str,
2325
) -> TokenStream {
24-
let name = Ident::new(&self.name, Span::call_site());
26+
let name = Ident::new(&format!("{}{}", prefix, self.name), Span::call_site());
2527
let fields = self.response_fields_for_selection(query_context, selection);
28+
let field_impls = self.field_impls_for_selection(query_context, selection, prefix);
2629
quote! {
30+
#(#field_impls)*
31+
2732
#[derive(Debug, Deserialize)]
2833
pub struct #name {
2934
#(#fields,)*
3035
}
3136
}
3237
}
3338

39+
pub fn field_impls_for_selection(
40+
&self,
41+
query_context: &QueryContext,
42+
selection: &query::SelectionSet,
43+
prefix: &str,
44+
) -> Vec<TokenStream> {
45+
let prefix = format!("{}{}", prefix, self.name);
46+
selection
47+
.items
48+
.iter()
49+
.map(|selected| {
50+
if let query::Selection::Field(selected) = selected {
51+
let ty = self
52+
.fields
53+
.iter()
54+
.find(|f| f.name == selected.name)
55+
.expect("field found")
56+
.type_
57+
.inner_name_string();
58+
query_context.maybe_expand_field(&selected, &ty, &prefix)
59+
} else {
60+
quote!()
61+
}
62+
})
63+
.collect()
64+
}
65+
3466
pub fn response_fields_for_selection(
3567
&self,
3668
query_context: &QueryContext,
@@ -52,8 +84,18 @@ impl GqlObject {
5284
let ty = ty.to_rust();
5385
fields.push(quote!(#name: #ty));
5486
}
55-
query::Selection::FragmentSpread(_) => (),
56-
query::Selection::InlineFragment(_) => (),
87+
query::Selection::FragmentSpread(fragment) => {
88+
let field_name =
89+
Ident::new(&fragment.fragment_name.to_snake_case(), Span::call_site());
90+
let type_name = Ident::new(&fragment.fragment_name, Span::call_site());
91+
fields.push(quote!{
92+
#[serde(flatten)]
93+
#field_name: #type_name
94+
})
95+
}
96+
query::Selection::InlineFragment(_) => {
97+
unreachable!("inline fragment on object field")
98+
}
5799
}
58100
}
59101

graphql_query_derive/src/query.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,41 @@
11
use field_type::FieldType;
2+
use graphql_parser::query;
23
use proc_macro2::TokenStream;
4+
use schema::Schema;
35
use std::collections::BTreeMap;
46

57
pub struct QueryContext {
68
pub _subscription_root: Option<Vec<TokenStream>>,
79
pub fragments: BTreeMap<String, BTreeMap<String, FieldType>>,
810
pub mutation_root: Option<Vec<TokenStream>>,
911
pub query_root: Option<Vec<TokenStream>>,
12+
pub schema: Schema,
1013
pub variables: BTreeMap<String, FieldType>,
1114
}
1215

1316
impl QueryContext {
14-
pub fn new() -> QueryContext {
17+
pub fn new(schema: Schema) -> QueryContext {
1518
QueryContext {
1619
_subscription_root: None,
1720
fragments: BTreeMap::new(),
1821
mutation_root: None,
1922
query_root: None,
23+
schema,
2024
variables: BTreeMap::new(),
2125
}
2226
}
27+
28+
pub fn maybe_expand_field(&self, field: &query::Field, ty: &str, prefix: &str) -> TokenStream {
29+
if let Some(enm) = self.schema.enums.get(ty) {
30+
enm.to_rust()
31+
} else if let Some(obj) = self.schema.objects.get(ty) {
32+
obj.response_for_selection(self, &field.selection_set, prefix)
33+
} else if let Some(iface) = self.schema.interfaces.get(ty) {
34+
iface.response_for_selection(self, &field.selection_set, prefix)
35+
} else if let Some(unn) = self.schema.unions.get(ty) {
36+
unn.response_for_selection(self, &field.selection_set, prefix)
37+
} else {
38+
quote!()
39+
}
40+
}
2341
}

graphql_query_derive/src/schema.rs

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,21 @@ use failure;
33
use field_type::FieldType;
44
use graphql_parser::{self, query, schema};
55
use inputs::GqlInput;
6+
use interfaces::GqlInterface;
67
use objects::{GqlObject, GqlObjectField};
78
use proc_macro2::TokenStream;
89
use query::QueryContext;
910
use std::collections::{BTreeMap, BTreeSet};
11+
use unions::GqlUnion;
1012

1113
#[derive(Debug)]
1214
pub struct Schema {
1315
pub enums: BTreeMap<String, GqlEnum>,
1416
pub inputs: BTreeMap<String, GqlInput>,
15-
pub interfaces: BTreeMap<String, GqlObject>,
17+
pub interfaces: BTreeMap<String, GqlInterface>,
1618
pub objects: BTreeMap<String, GqlObject>,
1719
pub scalars: BTreeSet<String>,
18-
pub unions: BTreeMap<String, Vec<String>>,
20+
pub unions: BTreeMap<String, GqlUnion>,
1921
pub query_type: Option<String>,
2022
pub mutation_type: Option<String>,
2123
pub subscription_type: Option<String>,
@@ -36,38 +38,56 @@ impl Schema {
3638
}
3739
}
3840

39-
pub fn response_for_query(
40-
&self,
41-
query: query::Document,
42-
) -> Result<TokenStream, failure::Error> {
43-
let mut context = QueryContext::new();
41+
pub fn response_for_query(self, query: query::Document) -> Result<TokenStream, failure::Error> {
42+
let mut context = QueryContext::new(self);
43+
let mut definitions = Vec::new();
4444

4545
for definition in query.definitions {
4646
match definition {
4747
query::Definition::Operation(query::OperationDefinition::Query(q)) => {
48-
let definition = self
48+
let definition = context
49+
.schema
4950
.query_type
5051
.clone()
51-
.and_then(|query_type| self.objects.get(&query_type))
52+
.and_then(|query_type| context.schema.objects.get(&query_type))
5253
.expect("query type is defined");
54+
definitions.extend(definition.field_impls_for_selection(
55+
&context,
56+
&q.selection_set,
57+
"",
58+
));
5359
context.query_root =
5460
Some(definition.response_fields_for_selection(&context, &q.selection_set));
5561
}
5662
query::Definition::Operation(query::OperationDefinition::Mutation(q)) => {
57-
let definition = self
63+
let definition = context
64+
.schema
5865
.mutation_type
5966
.clone()
60-
.and_then(|mutation_type| self.objects.get(&mutation_type))
67+
.and_then(|mutation_type| context.schema.objects.get(&mutation_type))
6168
.expect("mutation type is defined");
69+
definitions.extend(definition.field_impls_for_selection(
70+
&context,
71+
&q.selection_set,
72+
"",
73+
));
6274
context.mutation_root =
6375
Some(definition.response_fields_for_selection(&context, &q.selection_set));
6476
}
6577
query::Definition::Operation(query::OperationDefinition::Subscription(q)) => {
66-
let definition = self
78+
let definition = context
79+
.schema
6780
.subscription_type
6881
.clone()
69-
.and_then(|subscription_type| self.objects.get(&subscription_type))
82+
.and_then(|subscription_type| {
83+
context.schema.objects.get(&subscription_type)
84+
})
7085
.expect("subscription type is defined");
86+
definitions.extend(definition.field_impls_for_selection(
87+
&context,
88+
&q.selection_set,
89+
"",
90+
));
7191
context._subscription_root =
7292
Some(definition.response_fields_for_selection(&context, &q.selection_set));
7393
}
@@ -80,7 +100,7 @@ impl Schema {
80100
}
81101
}
82102

83-
let enum_definitions = self.enums.values().map(|enm| enm.to_rust());
103+
let enum_definitions = context.schema.enums.values().map(|enm| enm.to_rust());
84104
let variables_struct = quote!(#[derive(Serialize)]
85105
pub struct Variables;);
86106
let response_data_fields = context
@@ -90,17 +110,11 @@ impl Schema {
90110
.expect("no selection defined");
91111

92112
use proc_macro2::{Ident, Span};
93-
let object_definitions = self.objects.values().map(|obj| {
94-
let name = Ident::new(&obj.name, Span::call_site());
95-
quote! {
96-
pub struct #name;
97-
}
98-
});
99113

100114
Ok(quote! {
101115
#(#enum_definitions)*
102116

103-
#(#object_definitions)*
117+
#(#definitions)*
104118

105119
#variables_struct
106120

@@ -161,13 +175,14 @@ impl ::std::convert::From<graphql_parser::schema::Document> for Schema {
161175
schema.scalars.insert(scalar.name);
162176
}
163177
schema::TypeDefinition::Union(union) => {
164-
schema.unions.insert(union.name, union.types);
178+
schema.unions.insert(union.name, GqlUnion(union.types));
165179
}
166180
schema::TypeDefinition::Interface(interface) => {
167181
schema.interfaces.insert(
168182
interface.name.clone(),
169-
GqlObject {
183+
GqlInterface {
170184
name: interface.name,
185+
implemented_by: Vec::new(),
171186
fields: interface
172187
.fields
173188
.iter()

graphql_query_derive/src/unions.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
use graphql_parser::query;
2+
use proc_macro2::{Ident, Span, TokenStream};
3+
use query::QueryContext;
4+
5+
#[derive(Debug)]
6+
pub struct GqlUnion(pub Vec<String>);
7+
8+
impl GqlUnion {
9+
pub fn response_for_selection(
10+
&self,
11+
query_context: &QueryContext,
12+
selection: &query::SelectionSet,
13+
prefix: &str,
14+
) -> TokenStream {
15+
unimplemented!("union generation")
16+
}
17+
}

src/lib.rs

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
extern crate failure;
2-
extern crate graphql_parser;
3-
extern crate quote;
41
extern crate serde;
52
#[macro_use]
63
extern crate serde_derive;
74
#[allow(unused_imports)]
85
#[macro_use]
96
extern crate graphql_query_derive;
7+
108
#[doc(hidden)]
119
pub use graphql_query_derive::*;
1210

@@ -18,7 +16,7 @@ pub trait GraphQLQuery<'de> {
1816
fn build_query(variables: Self::Variables) -> GraphQLQueryBody<Self::Variables>;
1917
}
2018

21-
#[derive(Serialize)]
19+
#[derive(Debug, Serialize, Deserialize)]
2220
pub struct GraphQLQueryBody<Variables>
2321
where
2422
Variables: serde::Serialize,
@@ -27,37 +25,13 @@ where
2725
query: &'static str,
2826
}
2927

28+
#[derive(Debug, Serialize, Deserialize)]
3029
pub struct GraphQLError {
3130
pub path: String,
3231
}
3332

33+
#[derive(Debug, Serialize, Deserialize)]
3434
pub struct GraphQLResponse<Data> {
3535
pub data: Option<Data>,
3636
pub errors: Option<Vec<GraphQLError>>,
3737
}
38-
39-
#[cfg(test)]
40-
mod tests {
41-
42-
macro_rules! assert_parses_to {
43-
($query:expr, $schema:expr => $expected:tt) => {
44-
unimplemented!()
45-
};
46-
}
47-
48-
macro_rules! assert_mismatch {
49-
($query:expr, $schema:expr) => {
50-
unimplemented!()
51-
};
52-
}
53-
54-
#[test]
55-
fn queries_parse_properly() {
56-
assert_eq!(2 + 2, 4);
57-
}
58-
59-
#[test]
60-
fn invalid_queries_are_rejected() {
61-
unimplemented!();
62-
}
63-
}

0 commit comments

Comments
 (0)