use crate::parse::deps::*;
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
pub(super) fn graphql_impl(
cidomap: &super::CidomapStruct,
graphql_name: &Ident,
) -> syn::Result<TokenStream> {
let graphql_inner_name = format_ident!("{graphql_name}Inner");
let mut graphql_variants = Vec::new();
let mut graphql_field_impls = Vec::new();
let cidomap_name = &cidomap.name;
let opts = &cidomap.opts;
let fields = &cidomap.fields;
let vis = &cidomap.vis;
let block_number_err = format!("Error when querying MetaInfo<{}>: {{}}", cidomap_name);
for f in fields {
let ty = &f.field.ty;
let graphql_input_ty = quote!(<<#ty as ::cido::__internal::Transformer>::GraphqlInput as ::cido::__internal::GraphqlInputType>);
let filter_struct_ty = quote! ( #graphql_input_ty::Filter );
let order_by_struct_ty =
quote!( ::cido::__internal::GraphqlOrderBy<#graphql_input_ty::OrderByVariant> );
let name = f
.field
.ident
.as_ref()
.ok_or_else(|| crate::err(&f.field, "Fields without names are not supported"))?;
let v_name = name;
let custom_graphql_field = &f.graphql;
let single_fn = custom_graphql_field
.single
.name
.clone()
.or_else(|| opts.gql.single.apply_default_name(name))
.unwrap_or_else(|| name.clone());
let complexity = custom_graphql_field
.single
.complexity
.as_ref()
.map(|expr| quote!(#[graphql(complexity = #expr)]))
.unwrap_or_default();
let multiple_fn = custom_graphql_field
.multiple
.name
.clone()
.or_else(|| opts.gql.multiple.apply_default_name(name))
.unwrap_or_else(|| format_ident!("{name}_multiple"));
let multiple_complexity = custom_graphql_field
.multiple
.complexity
.as_ref()
.map(|expr| quote!(#[graphql(complexity = #expr)]))
.unwrap_or_default();
let history_fn = custom_graphql_field
.history
.name
.clone()
.or_else(|| opts.gql.history.apply_default_name(name))
.unwrap_or_else(|| format_ident!("{name}_history"));
let history_complexity = custom_graphql_field
.history
.complexity
.as_ref()
.map(|expr| quote!(#[graphql(complexity = #expr)]))
.unwrap_or_default();
let graphql_type = quote! {<#ty as ::cido::__internal::Transformer>::Graphql};
graphql_variants.push(quote! {#v_name(#graphql_type)});
let id_ty = custom_graphql_field
.id
.clone()
.unwrap_or_else(|| syn::parse_quote!(<#ty as ::cido::__internal::Identifiable>::Id));
let block_id_ty = quote!( <<<#ty as ::cido::__internal::Transformer>::Cidomap as ::cido::__internal::Cidomap>::Network as ::cido::__internal::Network>::BlockId );
let block_number_ty = quote!( <<<#ty as ::cido::__internal::Transformer>::Cidomap as ::cido::__internal::Cidomap>::Network as ::cido::__internal::Network>::BlockNumber );
let id_borrow = custom_graphql_field
.to_id_borrow
.clone()
.unwrap_or_else(|| syn::parse_quote!({ id }));
let history_id_borrow = custom_graphql_field
.to_id_borrow
.as_ref()
.map(|expr| {
quote! {
if let ::core::option::Option::Some(id) = id {
::core::option::Option::Some(#expr)
} else {
::core::option::Option::None
}
}
})
.unwrap_or(quote! { id });
let internal_error_converter = quote! {
map_err(|e| {
::cido::__internal::tracing::error!("{:?}", e);
::cido::__internal::async_graphql::Error::new("internal error")
})
};
let field_impl = quote! {
#complexity
#vis async fn #single_fn<'ctx>(
&self,
ctx: &::cido::__internal::async_graphql::Context<'ctx>,
id: #id_ty,
#[graphql(desc = "The block at which the query should be executed. Can either be a `{ hash: Bytes }` value containing a block hash, a `{ number: Int }` containing the block number, or a `{ number_gte: Int }` containing the minimum block number. In the case of `number_gte`, the query will be executed on the latest block only if the cidomap has progressed to or past the minimum block number. Defaults to the latest block when omitted.")]
block: ::core::option::Option<#block_id_ty>,
) -> ::cido::__internal::async_graphql::Result<::core::option::Option<<#ty as ::cido::__internal::Transformer>::GraphqlOutput>>
{
use ::cido::__internal::BlockId;
let network = ctx.data::<::std::sync::Arc<<<#ty as ::cido::__internal::Transformer>::Cidomap as ::cido::__internal::Cidomap>::Network>>().#internal_error_converter?;
let block_number = if let Some(block) = block {
block.to_block_number(&network).await.#internal_error_converter?
} else {
<#block_number_ty as ::cido::__internal::BlockNumber>::MAX
};
::cido::__internal::GraphqlQueryBuilder::<#ty>::fetch_optional_by_blocknumber(
::cido::__internal::GraphqlQueryBuilder::<_>::new(
::std::option::Option::None,
::std::option::Option::None,
::std::option::Option::None,
::std::default::Default::default(),
::std::default::Default::default(),
),
ctx,
&#id_borrow,
block_number,
).await
}
#[allow(clippy::too_many_arguments)]
#multiple_complexity
#vis async fn #multiple_fn<'ctx>(&self,
ctx: &::cido::__internal::async_graphql::Context<'ctx>,
#[graphql(default_with = "::core::option::Option::Some(0_u64)")]
skip: ::std::option::Option<u64>,
#[graphql(default_with = "::core::option::Option::Some(100_u64)")]
first: ::std::option::Option<u64>,
order_by: ::std::option::Option<#order_by_struct_ty>,
order_direction: ::std::option::Option<::cido::__internal::GraphqlOrderDirection>,
#[graphql(name="where")]
filter: ::std::option::Option<#filter_struct_ty>,
#[graphql(desc = "The block at which the query should be executed. Can either be a `{ hash: Bytes }` value containing a block hash, a `{ number: Int }` containing the block number, or a `{ number_gte: Int }` containing the minimum block number. In the case of `number_gte`, the query will be executed on the latest block only if the cidomap has progressed to or past the minimum block number. Defaults to the latest block when omitted.")]
block: ::core::option::Option<#block_id_ty>,
) -> ::cido::__internal::async_graphql::Result<::std::vec::Vec<<#ty as ::cido::__internal::Transformer>::GraphqlOutput>>
{
use ::cido::__internal::BlockId;
let network = ctx.data::<::std::sync::Arc<<<#ty as ::cido::__internal::Transformer>::Cidomap as ::cido::__internal::Cidomap>::Network>>().#internal_error_converter?;
let block_number = if let Some(block) = block {
block.to_block_number(&network).await.#internal_error_converter?
} else {
<#block_number_ty as ::cido::__internal::BlockNumber>::MAX
};
let filter = match filter {
::core::option::Option::None => ::core::option::Option::None,
::core::option::Option::Some(filter) => ::core::option::Option::Some(::cido::__internal::TryMapFilter::try_map(filter)?),
};
::cido::__internal::GraphqlQueryBuilder::<#ty>::new(
filter.map(::std::sync::Arc::new),
::std::option::Option::None,
order_by.map(::std::sync::Arc::new),
order_direction.map(::std::sync::Arc::new).unwrap_or_default(),
::cido::__internal::GraphqlPagination::new(first, skip)?,
)
.fetch_all(ctx, block_number)
.await
}
};
let history = quote! {
#[allow(clippy::too_many_arguments)]
#history_complexity
#vis async fn #history_fn<'ctx>(&self,
ctx: &::cido::__internal::async_graphql::Context<'ctx>,
id: ::core::option::Option<#id_ty>,
start_block: #block_id_ty,
end_block: ::std::option::Option<#block_id_ty>,
order_by: ::std::option::Option<#order_by_struct_ty>,
order_direction: ::std::option::Option<::cido::__internal::GraphqlOrderDirection>,
#[graphql(default_with = "::core::option::Option::Some(10_000_u64)")]
first: ::std::option::Option<u64>,
skip: ::std::option::Option<u64>,
#[graphql(name = "where")]
filter: ::std::option::Option<#filter_struct_ty>,
) -> ::cido::__internal::async_graphql::Result<::std::vec::Vec<<#ty as ::cido::__internal::Transformer>::GraphqlOutput>>
{
use ::cido::__internal::BlockId;
let network = ctx.data::<::std::sync::Arc<<<#ty as ::cido::__internal::Transformer>::Cidomap as ::cido::__internal::Cidomap>::Network>>().#internal_error_converter?;
let start_block_number = start_block.to_block_number(&network).await?;
let end_block_number = match end_block {
::core::option::Option::Some(end_block) => end_block.to_block_number(&network).await?,
::core::option::Option::None => {
let conn = ctx.data::<::cido::__internal::sqlx::PgPool>()?;
::cido::__internal::MetaInfo::<<#cidomap_name as ::cido::__internal::Cidomap>::Network>::get(conn).await.inspect_err(|e| {
::cido::__internal::tracing::error!(#block_number_err, e);
})?
.get_sync_block()
}
};
let filter = match filter {
::core::option::Option::None => ::core::option::Option::None,
::core::option::Option::Some(filter) => ::core::option::Option::Some(::cido::__internal::TryMapFilter::try_map(filter)?),
};
let id_borrow = #history_id_borrow;
::cido::__internal::GraphqlQueryBuilder::<#ty>::new(
filter.map(::std::sync::Arc::new),
::std::option::Option::None,
order_by.map(::std::sync::Arc::new),
order_direction.map(::std::sync::Arc::new).unwrap_or_default(),
::cido::__internal::GraphqlPagination::new(first, skip)?,
)
.fetch_history(ctx, id_borrow.as_ref(), start_block_number, end_block_number, false)
.await
.map(|(results, _)| results)
}
};
graphql_field_impls.push(quote! { #field_impl #history });
}
let extensions = &cidomap.opts.gql.extensions;
let graphql_attrs = Vec::<syn::Attribute>::new();
let tokens = quote! {
#(#graphql_attrs)*
#[derive(::core::default::Default, ::cido::__internal::async_graphql::MergedObject)]
#[graphql(name = "Query")]
#vis struct #graphql_name(#graphql_inner_name, #(#extensions),*);
#(#graphql_attrs)*
#[derive(::core::default::Default)]
#vis struct #graphql_inner_name;
#[::cido::__internal::async_graphql::Object]
impl #graphql_inner_name {
#(#graphql_field_impls)*
#[graphql(name = "_meta")]
#vis async fn meta<'ctx>(
&self,
ctx: &::cido::__internal::async_graphql::Context<'ctx>,
block: ::core::option::Option<<<#cidomap_name as ::cido::__internal::Cidomap>::Network as ::cido::__internal::Network>::BlockId>,
)
-> ::cido::__internal::async_graphql::Result<::core::option::Option<::cido::__internal::GraphqlCidomapMetaInfo<<#cidomap_name as ::cido::__internal::Cidomap>::Network>>> {
use ::cido::__internal::Network;
use ::core::convert::From;
let conn = ctx.data::<::cido::__internal::sqlx::PgPool>()?;
let meta = ::cido::__internal::MetaInfo::<<#cidomap_name as ::cido::__internal::Cidomap>::Network>::get(conn).await.map_err(|e| {
::cido::__internal::tracing::error!(#block_number_err, e);
::cido::__internal::async_graphql::Error::from(::cido::__internal::CidomapError::from(e))
})?;
let meta = ::cido::__internal::GraphqlCidomapMetaInfo::<<#cidomap_name as ::cido::__internal::Cidomap>::Network>::new(meta);
::std::result::Result::Ok(::core::option::Option::Some(meta))
}
}
};
Ok(tokens)
}