44extern crate failure;
55extern crate graphql_parser;
66extern crate heck;
7+ #[ macro_use]
8+ extern crate lazy_static;
79extern crate proc_macro;
810extern crate proc_macro2;
911extern crate serde;
@@ -42,6 +44,13 @@ mod tests;
4244use heck:: * ;
4345use proc_macro2:: { Ident , Span } ;
4446
47+ type CacheMap < T > = :: std:: sync:: Mutex < :: std:: collections:: hash_map:: HashMap < :: std:: path:: PathBuf , T > > ;
48+
49+ lazy_static ! {
50+ static ref SCHEMA_CACHE : CacheMap <schema:: Schema > = CacheMap :: default ( ) ;
51+ static ref QUERY_CACHE : CacheMap <( String , graphql_parser:: query:: Document ) > = CacheMap :: default ( ) ;
52+ }
53+
4554#[ proc_macro_derive( GraphQLQuery , attributes( graphql) ) ]
4655pub fn graphql_query_derive ( input : proc_macro:: TokenStream ) -> proc_macro:: TokenStream {
4756 let input = TokenStream :: from ( input) ;
@@ -51,19 +60,19 @@ pub fn graphql_query_derive(input: proc_macro::TokenStream) -> proc_macro::Token
5160}
5261
5362fn read_file (
54- path : impl AsRef < :: std:: path:: Path > + :: std :: fmt :: Debug ,
63+ path : & :: std:: path:: Path ,
5564) -> Result < String , failure:: Error > {
5665 use std:: io:: prelude:: * ;
5766
5867 let mut out = String :: new ( ) ;
59- let mut file = :: std:: fs:: File :: open ( & path) . map_err ( |io_err| {
68+ let mut file = :: std:: fs:: File :: open ( path) . map_err ( |io_err| {
6069 let err: failure:: Error = io_err. into ( ) ;
6170 err. context ( format ! (
6271 r#"
63- Could not find file with path: {:? }
72+ Could not find file with path: {}
6473 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".
6574 "# ,
66- path
75+ path. display ( )
6776 ) )
6877 } ) ?;
6978 file. read_to_string ( & mut out) ?;
@@ -88,29 +97,52 @@ fn impl_gql_query(input: &syn::DeriveInput) -> Result<TokenStream, failure::Erro
8897 . unwrap_or ( deprecation:: DeprecationStrategy :: Warn ) ;
8998
9099 // We need to qualify the query with the path to the crate it is part of
91- let query_path = format ! ( "{}/{}" , cargo_manifest_dir, query_path) ;
92- let query_string = read_file ( & query_path) ?;
93- let query = graphql_parser:: parse_query ( & query_string) ?;
100+ let query_path = :: std:: path:: Path :: new ( & cargo_manifest_dir) . join ( query_path) ;
101+ // Check the query cache.
102+ let ( query_string, query) = {
103+ let mut lock = QUERY_CACHE . lock ( ) . expect ( "query cache is poisoned" ) ;
104+ match lock. entry ( query_path) {
105+ :: std:: collections:: hash_map:: Entry :: Occupied ( o) => o. get ( ) . clone ( ) ,
106+ :: std:: collections:: hash_map:: Entry :: Vacant ( v) => {
107+ let query_string = read_file ( v. key ( ) ) ?;
108+ let query = graphql_parser:: parse_query ( & query_string) ?;
109+ v. insert ( ( query_string, query) ) . clone ( )
110+ } ,
111+ }
112+ } ;
94113
95114 // We need to qualify the schema with the path to the crate it is part of
96115 let schema_path = :: std:: path:: Path :: new ( & cargo_manifest_dir) . join ( schema_path) ;
97- let schema_string = read_file ( & schema_path) ?;
98-
99- let extension = schema_path
100- . extension ( )
101- . and_then ( |e| e. to_str ( ) )
102- . unwrap_or ( "INVALID" ) ;
103-
104- let schema = match extension {
105- "graphql" | "gql" => {
106- let s = graphql_parser:: schema:: parse_schema ( & schema_string) ?;
107- schema:: Schema :: from ( s)
108- }
109- "json" => {
110- let parsed: FullResponse < introspection_response:: IntrospectionResponse > = :: serde_json:: from_str ( & schema_string) ?;
111- schema:: Schema :: from ( parsed. data )
116+ // Check the schema cache.
117+ let schema = {
118+ let mut lock = SCHEMA_CACHE . lock ( ) . expect ( "schema cache is poisoned" ) ;
119+ match lock. entry ( schema_path) {
120+ :: std:: collections:: hash_map:: Entry :: Occupied ( o) => o. get ( ) . clone ( ) ,
121+ :: std:: collections:: hash_map:: Entry :: Vacant ( v) => {
122+ let schema_string = read_file ( v. key ( ) ) ?;
123+ let schema = {
124+ let extension = v
125+ . key ( )
126+ . extension ( )
127+ . and_then ( |e| e. to_str ( ) )
128+ . unwrap_or ( "INVALID" ) ;
129+
130+ match extension {
131+ "graphql" | "gql" => {
132+ let s = graphql_parser:: schema:: parse_schema ( & schema_string) ?;
133+ schema:: Schema :: from ( s)
134+ }
135+ "json" => {
136+ let parsed: FullResponse < introspection_response:: IntrospectionResponse > = :: serde_json:: from_str ( & schema_string) ?;
137+ schema:: Schema :: from ( parsed. data )
138+ }
139+ extension => panic ! ( "Unsupported extension for the GraphQL schema: {} (only .json and .graphql are supported)" , extension)
140+ }
141+ } ;
142+
143+ v. insert ( schema) . clone ( )
144+ } ,
112145 }
113- extension => panic ! ( "Unsupported extension for the GraphQL schema: {} (only .json and .graphql are supported)" , extension)
114146 } ;
115147
116148 let module_name = Ident :: new ( & input. ident . to_string ( ) . to_snake_case ( ) , Span :: call_site ( ) ) ;
0 commit comments