Skip to main content

test_shisho_gql_derive/
lib.rs

1extern crate proc_macro;
2
3use std::{
4    env,
5    path::{Path, PathBuf},
6};
7use test_shisho_policy_sdk::gqlgen::{
8    generate_module_token_stream, CodegenMode, GraphQLClientCodegenOptions,
9};
10
11mod attributes;
12
13use proc_macro2::TokenStream;
14
15/// Construct Rust structs by the referred GraphQL schema of Shisho Cloud.
16///
17/// # Examples
18/// Construct Rust structs by the referred GraphQL schema of Shisho Cloud.
19///
20/// ```
21/// #[derive(ShishoGraphQLQuery)]
22/// #[shisho_graphql(
23///    query_path = "src/policies/organization/org_owners/query.graphql",
24///    response_derives = "Debug, PartialEq"
25/// )]
26/// ```
27#[proc_macro_derive(ShishoGraphQLQuery, attributes(shisho_graphql))]
28pub fn derive_graphql_query(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
29    match graphql_query_derive_inner(input) {
30        Ok(ts) => ts,
31        Err(err) => err.to_compile_error().into(),
32    }
33}
34
35fn graphql_query_derive_inner(
36    input: proc_macro::TokenStream,
37) -> Result<proc_macro::TokenStream, syn::Error> {
38    let input = TokenStream::from(input);
39    let ast = syn::parse2(input)?;
40    let query_path = build_query_and_schema_path(&ast)?;
41    let options = build_graphql_client_derive_options(&ast, query_path.clone())?;
42
43    let schema_path = std::path::Path::new("schema/schema.graphql");
44    generate_module_token_stream(query_path, &schema_path, options)
45        .map(Into::into)
46        .map_err(|err| {
47            syn::Error::new_spanned(
48                ast,
49                format!("Failed to generate Shisho GraphQLQuery impl: {}", err),
50            )
51        })
52}
53
54fn build_query_and_schema_path(input: &syn::DeriveInput) -> Result<PathBuf, syn::Error> {
55    let cargo_manifest_dir = env::var("CARGO_MANIFEST_DIR").map_err(|_err| {
56        syn::Error::new_spanned(
57            input,
58            "Error checking that the CARGO_MANIFEST_DIR env variable is defined.",
59        )
60    })?;
61
62    let query_path = attributes::extract_attr(input, "query_path")?;
63    let query_path = format!("{}/{}", cargo_manifest_dir, query_path);
64    let query_path = Path::new(&query_path).to_path_buf();
65    Ok(query_path)
66}
67
68fn build_graphql_client_derive_options(
69    input: &syn::DeriveInput,
70    query_path: PathBuf,
71) -> Result<GraphQLClientCodegenOptions, syn::Error> {
72    let variables_derives = attributes::extract_attr(input, "variables_derives").ok();
73    let response_derives = attributes::extract_attr(input, "response_derives").ok();
74    let custom_scalars_module = attributes::extract_attr(input, "custom_scalars_module").ok();
75    let extern_enums = attributes::extract_attr_list(input, "extern_enums").ok();
76    let fragments_other_variant: bool = attributes::extract_fragments_other_variant(input);
77
78    let mut options = GraphQLClientCodegenOptions::new(CodegenMode::Derive);
79    options.set_query_file(query_path);
80    options.set_fragments_other_variant(fragments_other_variant);
81    // options.set_skip_serializing_none(skip_serializing_none);
82
83    if let Some(variables_derives) = variables_derives {
84        options.set_variables_derives(variables_derives);
85    };
86
87    if let Some(response_derives) = response_derives {
88        options.set_response_derives(response_derives);
89    };
90
91    // The user can determine what to do about deprecations.
92    if let Ok(deprecation_strategy) = attributes::extract_deprecation_strategy(input) {
93        options.set_deprecation_strategy(deprecation_strategy);
94    };
95
96    // The user can specify the normalization strategy.
97    if let Ok(normalization) = attributes::extract_normalization(input) {
98        options.set_normalization(normalization);
99    };
100
101    // The user can give a path to a module that provides definitions for the custom scalars.
102    if let Some(custom_scalars_module) = custom_scalars_module {
103        let custom_scalars_module = syn::parse_str(&custom_scalars_module)?;
104
105        options.set_custom_scalars_module(custom_scalars_module);
106    }
107
108    // The user can specify a list of enums types that are defined externally, rather than generated by this library
109    if let Some(extern_enums) = extern_enums {
110        options.set_extern_enums(extern_enums);
111    }
112
113    options.set_struct_ident(input.ident.clone());
114    options.set_module_visibility(input.vis.clone());
115    options.set_operation_name(input.ident.to_string());
116
117    Ok(options)
118}