Skip to content

Commit c32adbf

Browse files
committed
WIP - derive from json
1 parent bd72a04 commit c32adbf

File tree

12 files changed

+8686
-83
lines changed

12 files changed

+8686
-83
lines changed

graphql_query_derive/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@ syn = "*"
1313
proc-macro2 = { version = "*", features = ["nightly"] }
1414
serde = "1.0"
1515
serde_derive = "1.0"
16+
serde_json = "1.0"
1617
heck = "*"
1718
graphql-parser = "*"

graphql_query_derive/src/interfaces.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use objects::GqlObjectField;
33
use proc_macro2::{Ident, Span, TokenStream};
44
use query::QueryContext;
55

6-
#[derive(Debug)]
6+
#[derive(Debug, PartialEq)]
77
pub struct GqlInterface {
88
pub implemented_by: Vec<String>,
99
pub name: String,
@@ -14,7 +14,7 @@ impl GqlInterface {
1414
pub fn response_for_selection(
1515
&self,
1616
_query_context: &QueryContext,
17-
selection: &query::SelectionSet,
17+
_selection: &query::SelectionSet,
1818
prefix: &str,
1919
) -> TokenStream {
2020
let name = Ident::new(&prefix, Span::call_site());

graphql_query_derive/src/introspection_response.rs

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,8 @@
22

33
use serde;
44

5-
struct IntrospectionQuery;
6-
75
type Boolean = bool;
86

9-
type Float = f64;
10-
11-
type Int = i64;
12-
137
#[derive(Debug)]
148
pub enum __DirectiveLocation {
159
QUERY,
@@ -133,15 +127,16 @@ impl<'de> ::serde::Deserialize<'de> for __TypeKind {
133127
}
134128

135129
#[derive(Debug, Deserialize)]
130+
#[serde(rename_all = "camelCase")]
136131
pub struct FullType {
137-
kind: Option<__TypeKind>,
138-
name: Option<String>,
139-
description: Option<String>,
140-
fields: Option<Vec<Option<FullTypeFields>>>,
141-
inputFields: Option<Vec<Option<FullTypeInputFields>>>,
142-
interfaces: Option<Vec<Option<FullTypeInterfaces>>>,
143-
enumValues: Option<Vec<Option<FullTypeEnumValues>>>,
144-
possible_types: Option<Vec<Option<FullTypePossibleTypes>>>,
132+
pub kind: Option<__TypeKind>,
133+
pub name: Option<String>,
134+
pub description: Option<String>,
135+
pub fields: Option<Vec<Option<FullTypeFields>>>,
136+
pub input_fields: Option<Vec<Option<FullTypeInputFields>>>,
137+
pub interfaces: Option<Vec<Option<FullTypeInterfaces>>>,
138+
pub enum_values: Option<Vec<Option<FullTypeEnumValues>>>,
139+
pub possible_types: Option<Vec<Option<FullTypePossibleTypes>>>,
145140
}
146141

147142
#[derive(Debug, Deserialize)]
@@ -167,7 +162,7 @@ pub struct FullTypeFields {
167162
#[serde(rename = "type")]
168163
type_: Option<FullTypeFieldsType>,
169164
is_deprecated: Option<Boolean>,
170-
deprecationReason: Option<String>,
165+
deprecation_reason: Option<String>,
171166
}
172167

173168
#[derive(Debug, Deserialize)]
@@ -184,13 +179,13 @@ pub struct FullTypeInterfaces {
184179
type_ref: TypeRef,
185180
}
186181

187-
#[derive(Debug, Deserialize)]
182+
#[derive(Clone, Debug, Deserialize)]
188183
#[serde(rename_all = "camelCase")]
189184
pub struct FullTypeEnumValues {
190-
name: Option<String>,
191-
description: Option<String>,
192-
is_deprecated: Option<Boolean>,
193-
deprecation_reason: Option<String>,
185+
pub name: Option<String>,
186+
pub description: Option<String>,
187+
pub is_deprecated: Option<Boolean>,
188+
pub deprecation_reason: Option<String>,
194189
}
195190

196191
#[derive(Debug, Deserialize)]
@@ -302,7 +297,7 @@ pub struct RustIntrospectionQuerySchemaSubscriptionType {
302297
#[serde(rename_all = "camelCase")]
303298
pub struct RustIntrospectionQuerySchemaTypes {
304299
#[serde(flatten)]
305-
full_type: FullType,
300+
pub full_type: FullType,
306301
}
307302

308303
#[derive(Debug, Deserialize)]
@@ -324,14 +319,20 @@ pub struct RustIntrospectionQuerySchemaDirectives {
324319
#[derive(Debug, Deserialize)]
325320
#[serde(rename_all = "camelCase")]
326321
pub struct RustIntrospectionQuerySchema {
327-
query_type: Option<RustIntrospectionQuerySchemaQueryType>,
328-
mutation_type: Option<RustIntrospectionQuerySchemaMutationType>,
329-
subscription_type: Option<RustIntrospectionQuerySchemaSubscriptionType>,
330-
types: Option<Vec<Option<RustIntrospectionQuerySchemaTypes>>>,
322+
pub query_type: Option<RustIntrospectionQuerySchemaQueryType>,
323+
pub mutation_type: Option<RustIntrospectionQuerySchemaMutationType>,
324+
pub subscription_type: Option<RustIntrospectionQuerySchemaSubscriptionType>,
325+
pub types: Option<Vec<Option<RustIntrospectionQuerySchemaTypes>>>,
331326
directives: Option<Vec<Option<RustIntrospectionQuerySchemaDirectives>>>,
332327
}
333328

334329
#[derive(Debug, Deserialize)]
335330
pub struct Schema {
336-
__Schema: Option<RustIntrospectionQuerySchema>,
331+
#[serde(rename = "__schema")]
332+
pub schema: Option<RustIntrospectionQuerySchema>,
333+
}
334+
335+
#[derive(Debug, Deserialize)]
336+
pub struct IntrospectionResponse {
337+
pub data: Schema
337338
}

graphql_query_derive/src/lib.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ extern crate proc_macro2;
1010
extern crate serde;
1111
#[macro_use]
1212
extern crate serde_derive;
13+
extern crate serde_json;
1314
extern crate syn;
1415
#[macro_use]
1516
extern crate quote;
@@ -28,6 +29,9 @@ mod schema;
2829
mod shared;
2930
mod unions;
3031

32+
#[cfg(test)]
33+
mod tests;
34+
3135
use heck::*;
3236
use proc_macro2::{Ident, Span};
3337

@@ -55,11 +59,23 @@ fn impl_gql_query(input: &syn::DeriveInput) -> Result<TokenStream, failure::Erro
5559
let query = graphql_parser::parse_query(&query_string)?;
5660

5761
// We need to qualify the schema with the path to the crate it is part of
58-
let schema_path = format!("{}/{}", cargo_manifest_dir, schema_path);
62+
let schema_path = ::std::path::Path::new(&cargo_manifest_dir).join(schema_path);
5963
let mut schema_string = String::new();
60-
::std::fs::File::open(schema_path)?.read_to_string(&mut schema_string)?;
61-
let schema = graphql_parser::schema::parse_schema(&schema_string)?;
62-
let schema = schema::Schema::from(schema);
64+
::std::fs::File::open(&schema_path)?.read_to_string(&mut schema_string)?;
65+
66+
let extension = schema_path.extension().and_then(|e| e.to_str()).unwrap_or("INVALID");
67+
68+
let schema = match extension {
69+
"graphql" | "gql" => {
70+
let s = graphql_parser::schema::parse_schema(&schema_string)?;
71+
schema::Schema::from(s)
72+
}
73+
"json" => {
74+
let parsed: introspection_response::IntrospectionResponse = ::serde_json::from_str(&schema_string)?;
75+
schema::Schema::from(parsed)
76+
}
77+
extension => panic!("Unsupported extension for the GraphQL schema: {} (only .json and .graphql are supported)", extension)
78+
};
6379

6480
let module_name = Ident::new(&input.ident.to_string().to_snake_case(), Span::call_site());
6581
let struct_name = &input.ident;

graphql_query_derive/src/objects.rs

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -111,50 +111,3 @@ impl GqlObject {
111111
fields
112112
}
113113
}
114-
115-
#[cfg(test)]
116-
mod tests {
117-
use super::*;
118-
use graphql_parser::Pos;
119-
120-
#[test]
121-
fn simple_object() {
122-
let simple = GqlObject {
123-
name: "SimpleObject".to_string(),
124-
fields: vec![
125-
GqlObjectField {
126-
name: "greeting".to_string(),
127-
type_: FieldType::Named(Ident::new("String", Span::call_site())),
128-
},
129-
GqlObjectField {
130-
name: "name".to_string(),
131-
type_: FieldType::Named(Ident::new("String", Span::call_site())),
132-
},
133-
],
134-
};
135-
136-
let pos = Pos { line: 0, column: 0 };
137-
138-
let selection_set = query::SelectionSet {
139-
span: (pos.clone(), pos.clone()),
140-
items: vec![query::Selection::Field(query::Field {
141-
position: pos.clone(),
142-
alias: None,
143-
name: "name".to_string(),
144-
arguments: vec![],
145-
directives: vec![],
146-
selection_set: query::SelectionSet {
147-
span: (pos.clone(), pos.clone()),
148-
items: vec![],
149-
},
150-
})],
151-
};
152-
153-
assert_eq!(
154-
simple
155-
.response_for_selection(&mut QueryContext::new(), &selection_set)
156-
.to_string(),
157-
"# [ derive ( Debug , Deserialize ) ] pub struct SimpleObject { name : String , }"
158-
)
159-
}
160-
}

graphql_query_derive/src/query.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ impl QueryContext {
3333
ty: &str,
3434
prefix: &str,
3535
) -> Result<TokenStream, failure::Error> {
36-
if let Some(enm) = self.schema.enums.get(ty) {
36+
if let Some(_enm) = self.schema.enums.get(ty) {
3737
Ok(quote!()) // we already expand enums separately
3838
} else if let Some(obj) = self.schema.objects.get(ty) {
3939
obj.response_for_selection(self, &field.selection_set, prefix)

graphql_query_derive/src/schema.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use unions::GqlUnion;
1313

1414
pub const DEFAULT_SCALARS: &[&'static str] = &["ID", "String", "Int", "Float", "Boolean"];
1515

16-
#[derive(Debug)]
16+
#[derive(Debug, PartialEq)]
1717
pub struct Schema {
1818
pub enums: BTreeMap<String, GqlEnum>,
1919
pub inputs: BTreeMap<String, GqlInput>,
@@ -257,6 +257,35 @@ impl ::std::convert::From<graphql_parser::schema::Document> for Schema {
257257
}
258258
}
259259

260+
impl ::std::convert::From<::introspection_response::IntrospectionResponse> for Schema {
261+
fn from(src: ::introspection_response::IntrospectionResponse) -> Self {
262+
use introspection_response::{__TypeKind};
263+
264+
let mut schema = Schema::new();
265+
let root = src.data.schema.expect("__Schema is not null");
266+
267+
for ty in root.types.expect("types in schema").iter().filter_map(|t| t.as_ref().map(|t| &t.full_type)) {
268+
match ty.kind {
269+
Some(__TypeKind::ENUM) => {
270+
let name = ty.name.clone().expect("enum name");
271+
let variants: Vec<String> = ty.enum_values.clone().expect("enum variants")
272+
.iter()
273+
.map(|t| t.clone().map(|t| t.name.expect("enum variant name")))
274+
.filter_map(|t| t)
275+
.collect();
276+
schema.enums.insert(name.clone(), GqlEnum {
277+
name,
278+
variants,
279+
});
280+
}
281+
_ => unimplemented!("unimplemented definition")
282+
}
283+
}
284+
285+
schema
286+
}
287+
}
288+
260289
#[cfg(test)]
261290
mod tests {
262291
use super::*;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
use schema::Schema;
2+
use graphql_parser;
3+
use serde_json;
4+
5+
const SCHEMA_JSON: &'static str = include_str!("github_schema.json");
6+
const SCHEMA_GRAPHQL: &'static str = include_str!("github_schema.graphql");
7+
8+
#[test]
9+
fn ast_from_graphql_and_json_produce_the_same_schema() {
10+
let json: ::introspection_response::IntrospectionResponse = serde_json::from_str(SCHEMA_JSON).unwrap();
11+
let graphql_parser_schema = graphql_parser::parse_schema(SCHEMA_GRAPHQL).unwrap();
12+
assert_eq!(
13+
Schema::from(graphql_parser_schema),
14+
Schema::from(json),
15+
);
16+
}

0 commit comments

Comments
 (0)