extern crate proc_macro;
mod attributes;
mod gqlgen;
mod schema;
use std::{
env,
path::{Path, PathBuf},
};
use crate::gqlgen::{
generate_module_token_stream, CodegenMode, GraphQLClientCodegenOptions,
};
use proc_macro2::TokenStream;
#[proc_macro_derive(InputQuery, attributes(shisho_graphql))]
pub fn derive_graphql_query(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
match graphql_query_derive_inner(input) {
Ok(ts) => ts,
Err(err) => err.to_compile_error().into(),
}
}
fn graphql_query_derive_inner(
input: proc_macro::TokenStream,
) -> Result<proc_macro::TokenStream, syn::Error> {
let input = TokenStream::from(input);
let ast = syn::parse2(input)?;
let query_path = build_query_and_schema_path(&ast)?;
let options = build_graphql_client_derive_options(&ast, query_path.clone())?;
let schema_path = std::path::Path::new("schema/schema.graphql");
generate_module_token_stream(query_path, &schema_path, options)
.map(Into::into)
.map_err(|err| {
syn::Error::new_spanned(
ast,
format!("Failed to generate Shisho GraphQLQuery impl: {}", err),
)
})
}
fn build_query_and_schema_path(input: &syn::DeriveInput) -> Result<PathBuf, syn::Error> {
let cargo_manifest_dir = env::var("CARGO_MANIFEST_DIR").map_err(|_err| {
syn::Error::new_spanned(
input,
"Error checking that the CARGO_MANIFEST_DIR env variable is defined.",
)
})?;
let query_path = attributes::extract_attr(input, "query_path")?;
let query_path = format!("{}/{}", cargo_manifest_dir, query_path);
let query_path = Path::new(&query_path).to_path_buf();
Ok(query_path)
}
fn build_graphql_client_derive_options(
input: &syn::DeriveInput,
query_path: PathBuf,
) -> Result<GraphQLClientCodegenOptions, syn::Error> {
let variables_derives = attributes::extract_attr(input, "variables_derives").ok();
let derives = attributes::extract_attr(input, "derives").ok();
let custom_scalars_module = attributes::extract_attr(input, "custom_scalars_module").ok();
let extern_enums = attributes::extract_attr_list(input, "extern_enums").ok();
let fragments_other_variant: bool = attributes::extract_fragments_other_variant(input);
let mut options = GraphQLClientCodegenOptions::new(CodegenMode::Derive);
options.set_query_file(query_path);
options.set_fragments_other_variant(fragments_other_variant);
if let Some(variables_derives) = variables_derives {
options.set_variables_derives(variables_derives);
};
if let Some(derives) = derives {
options.set_derives(derives);
};
if let Ok(deprecation_strategy) = attributes::extract_deprecation_strategy(input) {
options.set_deprecation_strategy(deprecation_strategy);
};
if let Ok(normalization) = attributes::extract_normalization(input) {
options.set_normalization(normalization);
};
if let Some(custom_scalars_module) = custom_scalars_module {
let custom_scalars_module = syn::parse_str(&custom_scalars_module)?;
options.set_custom_scalars_module(custom_scalars_module);
}
if let Some(extern_enums) = extern_enums {
options.set_extern_enums(extern_enums);
}
options.set_struct_ident(input.ident.clone());
options.set_module_visibility(input.vis.clone());
options.set_operation_name(input.ident.to_string());
Ok(options)
}