Skip to content

Commit 5bdbe0e

Browse files
committed
Make new crate, graphql_client_codegen
1 parent 095a501 commit 5bdbe0e

File tree

3 files changed

+169
-0
lines changed

3 files changed

+169
-0
lines changed

graphql_client_codegen/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/target

graphql_client_codegen/Cargo.toml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[package]
2+
name = "graphql_client_codegen"
3+
version = "0.4.0"
4+
authors = ["Tom Houlé <tom@tomhoule.com>"]
5+
description = "Utility crate for graphql_client"
6+
license = "Apache-2.0 OR MIT"
7+
repository = "https://github.com/tomhoule/graphql-client"
8+
9+
[dependencies]
10+
failure = "0.1"
11+
quote = "^0.6"
12+
syn = "0.14"
13+
proc-macro2 = { version = "0.4", features = [] }
14+
serde = "1.0"
15+
serde_derive = "1.0"
16+
serde_json = "1.0"
17+
heck = "0.3"
18+
graphql-parser = "0.2"

graphql_client_codegen/src/lib.rs

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
#![recursion_limit = "512"]
2+
3+
#[macro_use]
4+
extern crate failure;
5+
extern crate graphql_parser;
6+
extern crate heck;
7+
extern crate proc_macro;
8+
extern crate proc_macro2;
9+
extern crate serde;
10+
#[macro_use]
11+
extern crate serde_derive;
12+
extern crate serde_json;
13+
extern crate syn;
14+
#[macro_use]
15+
extern crate quote;
16+
17+
use proc_macro2::TokenStream;
18+
19+
pub mod attributes;
20+
pub mod codegen;
21+
pub mod deprecation;
22+
pub mod introspection_response;
23+
pub mod query;
24+
pub mod schema;
25+
26+
mod constants;
27+
mod enums;
28+
mod field_type;
29+
mod fragments;
30+
mod inputs;
31+
mod interfaces;
32+
mod objects;
33+
mod operations;
34+
mod scalars;
35+
mod selection;
36+
mod shared;
37+
mod unions;
38+
mod variables;
39+
40+
use heck::SnakeCase;
41+
42+
#[cfg(test)]
43+
mod tests;
44+
use proc_macro2::{Ident, Span};
45+
46+
pub struct GraphQLClientDeriveOptions<'a> {
47+
pub input: &'a syn::DeriveInput,
48+
}
49+
50+
#[derive(Serialize, Deserialize, Debug)]
51+
pub(crate) struct FullResponse<T> {
52+
data: T,
53+
}
54+
55+
pub fn generate_module_token_stream(
56+
query_path: std::path::PathBuf,
57+
schema_path: std::path::PathBuf,
58+
options: Option<GraphQLClientDeriveOptions>,
59+
) -> Result<TokenStream, failure::Error> {
60+
let input = options.unwrap().input;
61+
62+
let response_derives = attributes::extract_attr(input, "response_derives").ok();
63+
64+
// The user can determine what to do about deprecations.
65+
let deprecation_strategy = deprecation::extract_deprecation_strategy(input)
66+
.unwrap_or(deprecation::DeprecationStrategy::Warn);
67+
68+
// 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)
84+
}
85+
"json" => {
86+
let parsed: FullResponse<introspection_response::IntrospectionResponse> = ::serde_json::from_str(&schema_string)?;
87+
schema::Schema::from(parsed.data)
88+
}
89+
extension => panic!("Unsupported extension for the GraphQL schema: {} (only .json and .graphql are supported)", extension)
90+
};
91+
92+
let module_name = Ident::new(&input.ident.to_string().to_snake_case(), Span::call_site());
93+
let struct_name = &input.ident;
94+
let schema_output = codegen::response_for_query(
95+
schema,
96+
query,
97+
input.ident.to_string(),
98+
response_derives,
99+
deprecation_strategy,
100+
)?;
101+
102+
let result = quote!(
103+
pub mod #module_name {
104+
#![allow(non_camel_case_types)]
105+
#![allow(non_snake_case)]
106+
#![allow(dead_code)]
107+
108+
use serde;
109+
110+
pub const QUERY: &'static str = #query_string;
111+
112+
#schema_output
113+
}
114+
115+
impl<'de> ::graphql_client::GraphQLQuery<'de> for #struct_name {
116+
type Variables = #module_name::Variables;
117+
type ResponseData = #module_name::ResponseData;
118+
119+
fn build_query(variables: Self::Variables) -> ::graphql_client::GraphQLQueryBody<Self::Variables> {
120+
::graphql_client::GraphQLQueryBody {
121+
variables,
122+
query: #module_name::QUERY,
123+
}
124+
125+
}
126+
}
127+
);
128+
129+
Ok(result)
130+
}
131+
132+
fn read_file(
133+
path: impl AsRef<::std::path::Path> + ::std::fmt::Debug,
134+
) -> Result<String, failure::Error> {
135+
use std::io::prelude::*;
136+
137+
let mut out = String::new();
138+
let mut file = ::std::fs::File::open(&path).map_err(|io_err| {
139+
let err: failure::Error = io_err.into();
140+
err.context(format!(
141+
r#"
142+
Could not find file with path: {:?}
143+
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".
144+
"#,
145+
path
146+
))
147+
})?;
148+
file.read_to_string(&mut out)?;
149+
Ok(out)
150+
}

0 commit comments

Comments
 (0)