@@ -4,7 +4,10 @@ use std::path::Path;
44
55use proc_macro2:: { Ident , Span , TokenStream } ;
66use quote:: { quote, ToTokens } ;
7- use syn:: { self , parse:: Parse , parse:: ParseStream , parse_macro_input, Expr , FnArg , LitStr , Token } ;
7+ use syn:: {
8+ self , parse:: Parse , parse:: ParseStream , parse_macro_input, Expr , ExprArray , FnArg , LitStr ,
9+ Token ,
10+ } ;
811
912#[ derive( Clone , Default ) ]
1013struct ShopifyFunctionArgs {
@@ -123,6 +126,7 @@ struct ShopifyFunctionTargetArgs {
123126 schema_path : Option < LitStr > ,
124127 input_stream : Option < Expr > ,
125128 output_stream : Option < Expr > ,
129+ extern_enums : Option < ExprArray > ,
126130}
127131
128132impl ShopifyFunctionTargetArgs {
@@ -156,6 +160,8 @@ impl Parse for ShopifyFunctionTargetArgs {
156160 args. input_stream = Some ( Self :: parse :: < kw:: input_stream , Expr > ( & input) ?) ;
157161 } else if lookahead. peek ( kw:: output_stream) {
158162 args. output_stream = Some ( Self :: parse :: < kw:: output_stream , Expr > ( & input) ?) ;
163+ } else if lookahead. peek ( kw:: extern_enums) {
164+ args. extern_enums = Some ( Self :: parse :: < kw:: extern_enums , ExprArray > ( & input) ?) ;
159165 } else {
160166 return Err ( lookahead. error ( ) ) ;
161167 }
@@ -170,6 +176,7 @@ struct GenerateTypeArgs {
170176 schema_path : Option < LitStr > ,
171177 input_stream : Option < Expr > ,
172178 output_stream : Option < Expr > ,
179+ extern_enums : Option < ExprArray > ,
173180}
174181
175182impl GenerateTypeArgs {
@@ -199,6 +206,8 @@ impl Parse for GenerateTypeArgs {
199206 args. input_stream = Some ( Self :: parse :: < kw:: input_stream , Expr > ( & input) ?) ;
200207 } else if lookahead. peek ( kw:: output_stream) {
201208 args. output_stream = Some ( Self :: parse :: < kw:: output_stream , Expr > ( & input) ?) ;
209+ } else if lookahead. peek ( kw:: extern_enums) {
210+ args. extern_enums = Some ( Self :: parse :: < kw:: extern_enums , ExprArray > ( & input) ?) ;
202211 } else {
203212 return Err ( lookahead. error ( ) ) ;
204213 }
@@ -256,6 +265,26 @@ fn extract_shopify_function_return_type(ast: &syn::ItemFn) -> Result<&syn::Ident
256265 Ok ( & path. path . segments . last ( ) . as_ref ( ) . unwrap ( ) . ident )
257266}
258267
268+ /// Generates code for a Function using an explicitly-named target. This will:
269+ /// - Generate a module to host the generated types.
270+ /// - Generate types based on the GraphQL schema for the Function input and output.
271+ /// - Define a wrapper function that's exported to Wasm. The wrapper handles
272+ /// decoding the input from STDIN, and encoding the output to STDOUT.
273+ ///
274+ ///
275+ /// The macro takes the following parameters:
276+ /// - `query_path`: A path to a GraphQL query, whose result will be used
277+ /// as the input for the function invocation. The query MUST be named "Input".
278+ /// - `schema_path`: A path to Shopify's GraphQL schema definition. Use the CLI
279+ /// to download a fresh copy.
280+ /// - `target` (optional): The API-specific handle for the target if the function name does not match the target handle as `snake_case`
281+ /// - `module_name` (optional): The name of the generated module.
282+ /// - default: The target handle as `snake_case`
283+ /// - `extern_enums` (optional): A list of Enums for which an external type should be used.
284+ /// For those, code generation will be skipped. This is useful for large enums
285+ /// which can increase binary size, or for enums shared between multiple targets.
286+ /// Example: `extern_enums = ["LanguageCode"]`
287+ /// - default: `["LanguageCode", "CountryCode", "CurrencyCode"]`
259288#[ proc_macro_attribute]
260289pub fn shopify_function_target (
261290 attr : proc_macro:: TokenStream ,
@@ -282,17 +311,21 @@ pub fn shopify_function_target(
282311
283312 let query_path = args. query_path . expect ( "No value given for query_path" ) ;
284313 let schema_path = args. schema_path . expect ( "No value given for schema_path" ) ;
314+ let extern_enums = args. extern_enums . as_ref ( ) . map ( extract_extern_enums) ;
285315 let output_query_file_name = format ! ( ".{}{}" , & target_handle_string, OUTPUT_QUERY_FILE_NAME ) ;
286316
287317 let input_struct = generate_struct (
288318 "Input" ,
289319 query_path. value ( ) . as_str ( ) ,
290320 schema_path. value ( ) . as_str ( ) ,
321+ extern_enums. as_deref ( ) ,
291322 ) ;
323+
292324 let output_struct = generate_struct (
293325 "Output" ,
294326 & output_query_file_name,
295327 schema_path. value ( ) . as_str ( ) ,
328+ extern_enums. as_deref ( ) ,
296329 ) ;
297330 if let Err ( error) = extract_shopify_function_return_type ( & ast) {
298331 return error. to_compile_error ( ) . into ( ) ;
@@ -353,12 +386,16 @@ const OUTPUT_QUERY_FILE_NAME: &str = ".output.graphql";
353386/// modules generate Rust types from the GraphQL schema file for the Function input
354387/// and output respectively.
355388///
356- /// The macro takes two parameters:
389+ /// The macro takes the following parameters:
357390/// - `query_path`: A path to a GraphQL query, whose result will be used
358391/// as the input for the function invocation. The query MUST be named "Input".
359- /// - `schema_path`: A path to Shopify's GraphQL schema definition. You
360- /// can find it in the `example` folder of the repo, or use the CLI
361- /// to download a fresh copy (not implemented yet).
392+ /// - `schema_path`: A path to Shopify's GraphQL schema definition. Use the CLI
393+ /// to download a fresh copy.
394+ /// - `extern_enums` (optional): A list of Enums for which an external type should be used.
395+ /// For those, code generation will be skipped. This is useful for large enums
396+ /// which can increase binary size, or for enums shared between multiple targets.
397+ /// Example: `extern_enums = ["LanguageCode"]`
398+ /// - default: `["LanguageCode", "CountryCode", "CurrencyCode"]`
362399///
363400/// Note: This macro creates a file called `.output.graphql` in the root
364401/// directory of the project. It can be safely added to your `.gitignore`. We
@@ -375,9 +412,19 @@ pub fn generate_types(attr: proc_macro::TokenStream) -> proc_macro::TokenStream
375412 . schema_path
376413 . expect ( "No value given for schema_path" )
377414 . value ( ) ;
378-
379- let input_struct = generate_struct ( "Input" , query_path. as_str ( ) , schema_path. as_str ( ) ) ;
380- let output_struct = generate_struct ( "Output" , OUTPUT_QUERY_FILE_NAME , schema_path. as_str ( ) ) ;
415+ let extern_enums = args. extern_enums . as_ref ( ) . map ( extract_extern_enums) ;
416+ let input_struct = generate_struct (
417+ "Input" ,
418+ query_path. as_str ( ) ,
419+ schema_path. as_str ( ) ,
420+ extern_enums. as_deref ( ) ,
421+ ) ;
422+ let output_struct = generate_struct (
423+ "Output" ,
424+ OUTPUT_QUERY_FILE_NAME ,
425+ schema_path. as_str ( ) ,
426+ extern_enums. as_deref ( ) ,
427+ ) ;
381428 let output_query =
382429 "mutation Output($result: FunctionResult!) {\n handleResult(result: $result)\n }\n " ;
383430
@@ -390,16 +437,28 @@ pub fn generate_types(attr: proc_macro::TokenStream) -> proc_macro::TokenStream
390437 . into ( )
391438}
392439
393- fn generate_struct ( name : & str , query_path : & str , schema_path : & str ) -> TokenStream {
440+ const DEFAULT_EXTERN_ENUMS : & [ & str ] = & [ "LanguageCode" , "CountryCode" , "CurrencyCode" ] ;
441+
442+ fn generate_struct (
443+ name : & str ,
444+ query_path : & str ,
445+ schema_path : & str ,
446+ extern_enums : Option < & [ String ] > ,
447+ ) -> TokenStream {
394448 let name_ident = Ident :: new ( name, Span :: mixed_site ( ) ) ;
395449
450+ let extern_enums = extern_enums
451+ . map ( |e| e. to_owned ( ) )
452+ . unwrap_or_else ( || DEFAULT_EXTERN_ENUMS . iter ( ) . map ( |e| e. to_string ( ) ) . collect ( ) ) ;
453+
396454 quote ! {
397455 #[ derive( graphql_client:: GraphQLQuery , Clone , Debug , serde:: Deserialize , PartialEq ) ]
398456 #[ graphql(
399457 query_path = #query_path,
400458 schema_path = #schema_path,
401459 response_derives = "Clone,Debug,PartialEq,Deserialize,Serialize" ,
402460 variables_derives = "Clone,Debug,PartialEq,Deserialize" ,
461+ extern_enums( #( #extern_enums) , * ) ,
403462 skip_serializing_none
404463 ) ]
405464 pub struct #name_ident;
@@ -415,6 +474,24 @@ fn write_output_query_file(output_query_file_name: &str, contents: &str) {
415474 . unwrap_or_else ( |_| panic ! ( "Could not write to {}" , output_query_file_name) ) ;
416475}
417476
477+ fn extract_extern_enums ( extern_enums : & ExprArray ) -> Vec < String > {
478+ let extern_enum_error_msg = r#"The `extern_enums` attribute expects comma separated string literals\n\n= help: use `extern_enums = ["Enum1", "Enum2"]`"# ;
479+ extern_enums
480+ . elems
481+ . iter ( )
482+ . map ( |expr| {
483+ let value = match expr {
484+ Expr :: Lit ( lit) => lit. lit . clone ( ) ,
485+ _ => panic ! ( "{}" , extern_enum_error_msg) ,
486+ } ;
487+ match value {
488+ syn:: Lit :: Str ( lit) => lit. value ( ) ,
489+ _ => panic ! ( "{}" , extern_enum_error_msg) ,
490+ }
491+ } )
492+ . collect ( )
493+ }
494+
418495#[ cfg( test) ]
419496mod tests { }
420497
@@ -425,4 +502,5 @@ mod kw {
425502 syn:: custom_keyword!( schema_path) ;
426503 syn:: custom_keyword!( input_stream) ;
427504 syn:: custom_keyword!( output_stream) ;
505+ syn:: custom_keyword!( extern_enums) ;
428506}
0 commit comments