test_shisho_datasource/
lib.rs

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