Skip to content

Commit 39f53c8

Browse files
committed
Add modifications to graphql_query_derive
1 parent 5bdbe0e commit 39f53c8

File tree

7 files changed

+76
-206
lines changed

7 files changed

+76
-206
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@ members = [
2323
"examples/example_module",
2424
"examples/github",
2525
"graphql_query_derive",
26+
"graphql_client_codegen",
2627
"graphql_client_cli",
2728
]

examples/example_module/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ use custom_scalars::*;
1212
#[graphql(
1313
schema_path = "../github/src/schema.graphql",
1414
query_path = "../github/src/query_1.graphql",
15-
response_derives = "Debug",
15+
response_derives = "Debug"
1616
)]
1717
pub struct ExampleModule;

graphql_client_codegen/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ repository = "https://github.com/tomhoule/graphql-client"
88

99
[dependencies]
1010
failure = "0.1"
11+
itertools = "0.7"
12+
lazy_static = "1.0"
1113
quote = "^0.6"
1214
syn = "0.14"
1315
proc-macro2 = { version = "0.4", features = [] }

graphql_client_codegen/src/lib.rs

Lines changed: 56 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
extern crate failure;
55
extern crate graphql_parser;
66
extern crate heck;
7+
extern crate itertools;
8+
#[macro_use]
9+
extern crate lazy_static;
710
extern crate proc_macro;
811
extern crate proc_macro2;
912
extern crate serde;
@@ -43,6 +46,15 @@ use heck::SnakeCase;
4346
mod tests;
4447
use proc_macro2::{Ident, Span};
4548

49+
type CacheMap<T> =
50+
::std::sync::Mutex<::std::collections::hash_map::HashMap<::std::path::PathBuf, T>>;
51+
52+
lazy_static! {
53+
static ref SCHEMA_CACHE: CacheMap<schema::Schema> = CacheMap::default();
54+
static ref QUERY_CACHE: CacheMap<(String, graphql_parser::query::Document)> =
55+
CacheMap::default();
56+
}
57+
4658
pub struct GraphQLClientDeriveOptions<'a> {
4759
pub input: &'a syn::DeriveInput,
4860
}
@@ -66,27 +78,48 @@ pub fn generate_module_token_stream(
6678
.unwrap_or(deprecation::DeprecationStrategy::Warn);
6779

6880
// We need to qualify the query with the path to the crate it is part of
69-
let query_string = read_file(&query_path)?;
70-
let query = graphql_parser::parse_query(&query_string)?;
71-
72-
// We need to qualify the schema with the path to the crate it is part of
73-
let schema_string = read_file(&schema_path)?;
74-
75-
let extension = schema_path
76-
.extension()
77-
.and_then(|e| e.to_str())
78-
.unwrap_or("INVALID");
79-
80-
let schema = match extension {
81-
"graphql" | "gql" => {
82-
let s = graphql_parser::schema::parse_schema(&schema_string)?;
83-
schema::Schema::from(s)
81+
let (query_string, query) = {
82+
let mut lock = QUERY_CACHE.lock().expect("query cache is poisoned");
83+
match lock.entry(query_path) {
84+
::std::collections::hash_map::Entry::Occupied(o) => o.get().clone(),
85+
::std::collections::hash_map::Entry::Vacant(v) => {
86+
let query_string = read_file(v.key())?;
87+
let query = graphql_parser::parse_query(&query_string)?;
88+
v.insert((query_string, query)).clone()
89+
}
8490
}
85-
"json" => {
86-
let parsed: FullResponse<introspection_response::IntrospectionResponse> = ::serde_json::from_str(&schema_string)?;
87-
schema::Schema::from(parsed.data)
91+
};
92+
93+
// Check the schema cache.
94+
let schema = {
95+
let mut lock = SCHEMA_CACHE.lock().expect("schema cache is poisoned");
96+
match lock.entry(schema_path) {
97+
::std::collections::hash_map::Entry::Occupied(o) => o.get().clone(),
98+
::std::collections::hash_map::Entry::Vacant(v) => {
99+
let schema_string = read_file(v.key())?;
100+
let schema = {
101+
let extension = v
102+
.key()
103+
.extension()
104+
.and_then(|e| e.to_str())
105+
.unwrap_or("INVALID");
106+
107+
match extension {
108+
"graphql" | "gql" => {
109+
let s = graphql_parser::schema::parse_schema(&schema_string)?;
110+
schema::Schema::from(s)
111+
}
112+
"json" => {
113+
let parsed: FullResponse<introspection_response::IntrospectionResponse> = ::serde_json::from_str(&schema_string)?;
114+
schema::Schema::from(parsed.data)
115+
}
116+
extension => panic!("Unsupported extension for the GraphQL schema: {} (only .json and .graphql are supported)", extension)
117+
}
118+
};
119+
120+
v.insert(schema).clone()
121+
}
88122
}
89-
extension => panic!("Unsupported extension for the GraphQL schema: {} (only .json and .graphql are supported)", extension)
90123
};
91124

92125
let module_name = Ident::new(&input.ident.to_string().to_snake_case(), Span::call_site());
@@ -129,20 +162,18 @@ pub fn generate_module_token_stream(
129162
Ok(result)
130163
}
131164

132-
fn read_file(
133-
path: impl AsRef<::std::path::Path> + ::std::fmt::Debug,
134-
) -> Result<String, failure::Error> {
165+
fn read_file(path: &::std::path::Path) -> Result<String, failure::Error> {
135166
use std::io::prelude::*;
136167

137168
let mut out = String::new();
138-
let mut file = ::std::fs::File::open(&path).map_err(|io_err| {
169+
let mut file = ::std::fs::File::open(path).map_err(|io_err| {
139170
let err: failure::Error = io_err.into();
140171
err.context(format!(
141172
r#"
142-
Could not find file with path: {:?}
173+
Could not find file with path: {}
143174
Hint: file paths in the GraphQLQuery attribute are relative to the project root (location of the Cargo.toml). Example: query_path = "src/my_query.graphql".
144175
"#,
145-
path
176+
path.display()
146177
))
147178
})?;
148179
file.read_to_string(&mut out)?;

graphql_query_derive/Cargo.toml

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,6 @@ repository = "https://github.com/tomhoule/graphql-client"
1010
proc-macro = true
1111

1212
[dependencies]
13-
failure = "0.1"
14-
itertools = "0.7"
15-
lazy_static = "1.0"
16-
quote = "^0.6"
1713
syn = "0.14"
1814
proc-macro2 = { version = "0.4", features = [] }
19-
serde = "1.0"
20-
serde_derive = "1.0"
21-
serde_json = "1.0"
22-
heck = "0.3"
23-
graphql-parser = "0.2"
15+
graphql_client_codegen = { path = "../graphql_client_codegen/" }

graphql_query_derive/src/lib.rs

Lines changed: 13 additions & 169 deletions
Original file line numberDiff line numberDiff line change
@@ -1,187 +1,31 @@
1-
#![recursion_limit = "512"]
2-
3-
#[macro_use]
4-
extern crate failure;
5-
extern crate graphql_parser;
6-
extern crate heck;
7-
extern crate itertools;
8-
#[macro_use]
9-
extern crate lazy_static;
1+
extern crate graphql_client_codegen;
102
extern crate proc_macro;
113
extern crate proc_macro2;
12-
extern crate serde;
13-
#[macro_use]
14-
extern crate serde_derive;
15-
extern crate serde_json;
164
extern crate syn;
17-
#[macro_use]
18-
extern crate quote;
5+
use graphql_client_codegen::*;
196

207
use proc_macro2::TokenStream;
218

22-
mod attributes;
23-
mod codegen;
24-
mod constants;
25-
mod deprecation;
26-
mod enums;
27-
mod field_type;
28-
mod fragments;
29-
mod inputs;
30-
mod interfaces;
31-
mod introspection_response;
32-
mod objects;
33-
mod operations;
34-
mod query;
35-
mod scalars;
36-
mod schema;
37-
mod selection;
38-
mod shared;
39-
mod unions;
40-
mod variables;
41-
42-
#[cfg(test)]
43-
mod tests;
44-
45-
use heck::*;
46-
use proc_macro2::{Ident, Span};
47-
48-
type CacheMap<T> = ::std::sync::Mutex<::std::collections::hash_map::HashMap<::std::path::PathBuf, T>>;
49-
50-
lazy_static! {
51-
static ref SCHEMA_CACHE: CacheMap<schema::Schema> = CacheMap::default();
52-
static ref QUERY_CACHE: CacheMap<(String, graphql_parser::query::Document)> = CacheMap::default();
53-
}
54-
559
#[proc_macro_derive(GraphQLQuery, attributes(graphql))]
5610
pub fn graphql_query_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
5711
let input = TokenStream::from(input);
5812
let ast = syn::parse2(input).expect("Derive input is well formed");
59-
let gen = impl_gql_query(&ast).unwrap();
13+
let (query_path, schema_path) = build_query_and_schema_path(&ast);
14+
let option = GraphQLClientDeriveOptions { input: &ast };
15+
let gen = generate_module_token_stream(query_path, schema_path, Some(option)).unwrap();
6016
gen.into()
6117
}
6218

63-
fn read_file(
64-
path: &::std::path::Path,
65-
) -> Result<String, failure::Error> {
66-
use std::io::prelude::*;
67-
68-
let mut out = String::new();
69-
let mut file = ::std::fs::File::open(path).map_err(|io_err| {
70-
let err: failure::Error = io_err.into();
71-
err.context(format!(
72-
r#"
73-
Could not find file with path: {}
74-
Hint: file paths in the GraphQLQuery attribute are relative to the project root (location of the Cargo.toml). Example: query_path = "src/my_query.graphql".
75-
"#,
76-
path.display()
77-
))
78-
})?;
79-
file.read_to_string(&mut out)?;
80-
Ok(out)
81-
}
82-
83-
#[derive(Serialize, Deserialize, Debug)]
84-
pub(crate) struct FullResponse<T> {
85-
data: T,
86-
}
87-
88-
fn impl_gql_query(input: &syn::DeriveInput) -> Result<TokenStream, failure::Error> {
19+
fn build_query_and_schema_path(
20+
input: &syn::DeriveInput,
21+
) -> (std::path::PathBuf, std::path::PathBuf) {
8922
let cargo_manifest_dir =
9023
::std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR env variable is defined");
9124

92-
let query_path = attributes::extract_attr(input, "query_path")?;
93-
let schema_path = attributes::extract_attr(input, "schema_path")?;
94-
let response_derives = attributes::extract_attr(input, "response_derives").ok();
95-
96-
// The user can determine what to do about deprecations.
97-
let deprecation_strategy = deprecation::extract_deprecation_strategy(input)
98-
.unwrap_or(deprecation::DeprecationStrategy::Warn);
99-
100-
// We need to qualify the query with the path to the crate it is part of
101-
let query_path = ::std::path::Path::new(&cargo_manifest_dir).join(query_path);
102-
// Check the query cache.
103-
let (query_string, query) = {
104-
let mut lock = QUERY_CACHE.lock().expect("query cache is poisoned");
105-
match lock.entry(query_path) {
106-
::std::collections::hash_map::Entry::Occupied(o) => o.get().clone(),
107-
::std::collections::hash_map::Entry::Vacant(v) => {
108-
let query_string = read_file(v.key())?;
109-
let query = graphql_parser::parse_query(&query_string)?;
110-
v.insert((query_string, query)).clone()
111-
},
112-
}
113-
};
114-
115-
// We need to qualify the schema with the path to the crate it is part of
25+
let query_path = attributes::extract_attr(input, "query_path").unwrap();
26+
let query_path = format!("{}/{}", cargo_manifest_dir, query_path);
27+
let query_path = ::std::path::Path::new(&query_path).to_path_buf();
28+
let schema_path = attributes::extract_attr(input, "schema_path").unwrap();
11629
let schema_path = ::std::path::Path::new(&cargo_manifest_dir).join(schema_path);
117-
// Check the schema cache.
118-
let schema = {
119-
let mut lock = SCHEMA_CACHE.lock().expect("schema cache is poisoned");
120-
match lock.entry(schema_path) {
121-
::std::collections::hash_map::Entry::Occupied(o) => o.get().clone(),
122-
::std::collections::hash_map::Entry::Vacant(v) => {
123-
let schema_string = read_file(v.key())?;
124-
let schema = {
125-
let extension = v
126-
.key()
127-
.extension()
128-
.and_then(|e| e.to_str())
129-
.unwrap_or("INVALID");
130-
131-
match extension {
132-
"graphql" | "gql" => {
133-
let s = graphql_parser::schema::parse_schema(&schema_string)?;
134-
schema::Schema::from(s)
135-
}
136-
"json" => {
137-
let parsed: FullResponse<introspection_response::IntrospectionResponse> = ::serde_json::from_str(&schema_string)?;
138-
schema::Schema::from(parsed.data)
139-
}
140-
extension => panic!("Unsupported extension for the GraphQL schema: {} (only .json and .graphql are supported)", extension)
141-
}
142-
};
143-
144-
v.insert(schema).clone()
145-
},
146-
}
147-
};
148-
149-
let module_name = Ident::new(&input.ident.to_string().to_snake_case(), Span::call_site());
150-
let struct_name = &input.ident;
151-
let schema_output = codegen::response_for_query(
152-
schema,
153-
query,
154-
input.ident.to_string(),
155-
response_derives,
156-
deprecation_strategy,
157-
)?;
158-
159-
let result = quote!(
160-
pub mod #module_name {
161-
#![allow(non_camel_case_types)]
162-
#![allow(non_snake_case)]
163-
#![allow(dead_code)]
164-
165-
use serde;
166-
167-
pub const QUERY: &'static str = #query_string;
168-
169-
#schema_output
170-
}
171-
172-
impl<'de> ::graphql_client::GraphQLQuery<'de> for #struct_name {
173-
type Variables = #module_name::Variables;
174-
type ResponseData = #module_name::ResponseData;
175-
176-
fn build_query(variables: Self::Variables) -> ::graphql_client::GraphQLQueryBody<Self::Variables> {
177-
::graphql_client::GraphQLQueryBody {
178-
variables,
179-
query: #module_name::QUERY,
180-
}
181-
182-
}
183-
}
184-
);
185-
186-
Ok(result)
30+
(query_path, schema_path)
18731
}

src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ use std::collections::HashMap;
3636
///
3737
/// #[derive(GraphQLQuery)]
3838
/// #[graphql(
39-
/// query_path = "graphql_query_derive/src/tests/star_wars_query.graphql",
40-
/// schema_path = "graphql_query_derive/src/tests/star_wars_schema.graphql"
39+
/// query_path = "graphql_client_codegen/src/tests/star_wars_query.graphql",
40+
/// schema_path = "graphql_client_codegen/src/tests/star_wars_schema.graphql"
4141
/// )]
4242
/// struct StarWarsQuery;
4343
///

0 commit comments

Comments
 (0)