use crate::parse::deps::*;
pub use crate::parse::{
CachePolicy, Conversion, DerivedField, StructType, TransformerField, TransformerStruct,
};
use heck::ToPascalCase;
use proc_macro2::TokenStream;
use quote::{ToTokens, format_ident, quote};
use std::collections::HashSet;
use syn::{Ident, Type};
use crate::codegen::orderby::OrderByVariant;
type Result = syn::Result<TokenStream>;
mod db_impl;
mod from_impl;
mod gen_structs;
mod graphql_impl;
fn dyn_error() -> TokenStream {
quote! {
::std::boxed::Box<dyn ::std::error::Error + ::core::marker::Send + ::core::marker::Sync + 'static>
}
}
impl crate::codegen::CodegenWrapper<TransformerField> {
pub fn self_type(&self) -> Type {
if self.entity_or_event.is_some() {
let field_ty = &self.field.ty;
syn::parse_quote! { <#field_ty as ::cido::__internal::ToIdentifiable>::Identifiable }
} else {
self.field.ty.clone()
}
}
pub fn db_type(&self) -> Type {
if self.entity_or_event.is_some() {
let ty = &self.field.ty;
syn::parse_quote! { <#ty as ::cido::__internal::ToIdentifiable>::Identifiable }
} else {
self.db_config.ty.as_ref().clone()
}
}
pub fn graphql_input_type(&self) -> Type {
self
.graphql_config
.input_type
.as_ref()
.cloned()
.unwrap_or_else(|| self.db_type())
}
pub fn graphql_input_name(&self) -> &Ident {
&self.graphql_config.name
}
pub fn graphql_output_name(&self) -> &Ident {
&self.graphql_config.name
}
pub fn graphql_output_type(&self) -> Type {
self.self_type()
}
pub fn graphql_comparison_sdl_name_string(&self) -> String {
self.graphql_config.name.to_string()
}
pub fn graphql_sdl_output_name(&self) -> String {
self.graphql_config.name.to_string()
}
pub fn graphql_sdl_output_type(&self) -> Type {
self
.graphql_config
.output_type
.as_ref()
.cloned()
.unwrap_or_else(|| self.graphql_output_type())
}
pub fn graphql_order_by_name(&self) -> String {
self.graphql_sdl_output_name()
}
}
impl crate::codegen::CodegenWrapper<TransformerStruct> {
pub fn is_entity(&self) -> bool {
self.struct_type.is_entity()
}
pub fn is_event(&self) -> bool {
self.struct_type.is_event()
}
pub fn id_field(&self) -> &TransformerField {
&self.fields[self.id_offset]
}
pub fn struct_name(&self) -> &Ident {
&self.name
}
pub fn sql_table_name(&self) -> &str {
&self.struct_opts.sql_name
}
pub fn db_name(&self) -> &Ident {
&self.struct_opts.db_opts.name
}
pub fn graphql_output_name(&self) -> &Ident {
&self.struct_opts.graphql_opts.name
}
pub fn graphql_input_name(&self) -> Ident {
format_ident!("{}Input", self.struct_opts.graphql_opts.name)
}
pub fn default_graphql_name_from_ident(name: &Ident) -> Ident {
format_ident!("{}Graphql", name)
}
pub fn id_type(&self) -> &Type {
&self.id_field().field.ty
}
pub fn codegen(&self) -> Result {
let mut token_stream = TokenStream::new();
self.gen_structs(&mut token_stream);
self.gen_orderby(&mut token_stream)?;
self.gen_from_impls(&mut token_stream);
self.gen_trait_impls(&mut token_stream)?;
let struct_type = match self.struct_type {
StructType::Entity => "entity",
StructType::Event => "event",
};
crate::parse::embed_generated_code(
self.struct_opts.embed_generated_code,
self.struct_name(),
token_stream,
struct_type,
)
}
fn gen_orderby(&self, token_stream: &mut TokenStream) -> syn::Result<()> {
let mut variants = vec![];
for f in &self.fields {
let graphql_orderby_field_name = f.graphql_order_by_name();
let variant = OrderByVariant {
name: syn::parse_str(&f.name.to_string().to_pascal_case())
.expect("Valid ident is still valid"),
sql_name: f.sql_config.name.clone(),
graphql_name: format_ident!("{}", graphql_orderby_field_name),
graphql_type: f.graphql_config.comparison.clone(),
attrs: Default::default(),
aliases: f.alias.iter().cloned().collect(),
default: f.id,
id: f.id,
hidden: f.graphql_config.hidden,
entity_event: f.entity_or_event.is_some(),
is_block_number: false,
};
variants.push(variant);
}
variants.push(OrderByVariant {
name: format_ident!("__BlockNumber"),
sql_name: crate::parse::SqlSafeName::new(if self.is_entity() {
db_impl::BLOCK_RANGE_COL
} else {
db_impl::BLOCK_NUMBER_COL
}),
graphql_name: format_ident!("__BlockNumber"),
graphql_type: None,
attrs: Default::default(),
aliases: HashSet::new(),
default: false,
id: false,
hidden: false,
entity_event: false,
is_block_number: true,
});
crate::codegen::CodegenWrapper(crate::codegen::orderby::OrderBy {
name: format_ident!("{}OrderBy", self.struct_name()),
variants,
attrs: Default::default(),
vis: self.vis.clone(),
embed_generated_code: self.struct_opts.embed_generated_code,
})
.generate_order_by()?
.to_tokens(token_stream);
Ok(())
}
fn gen_trait_impls(&self, token_stream: &mut TokenStream) -> syn::Result<()> {
self.gen_transformer_trait()?.to_tokens(token_stream);
self.gen_cache_policy_trait().to_tokens(token_stream);
self.impl_persistent()?.to_tokens(token_stream);
self.impl_graphql()?.to_tokens(token_stream);
Ok(())
}
fn gen_transformer_trait(&self) -> Result {
let id = self.id_field();
let self_name = self.struct_name();
let id_type = self.id_type();
let graphql_output_type = self.graphql_output_name();
let graphql_input_type = self.graphql_input_name();
let cidomap = &self.struct_opts.cidomap;
let id_name = &id.name;
let transform_type = match self.struct_type {
StructType::Entity => quote! {::cido::__internal::RecordType::Entity},
StructType::Event => quote! {::cido::__internal::RecordType::Event},
};
let error_ty = dyn_error();
let self_name_str = self_name.to_string();
Ok(quote! {
impl ::cido::__internal::Identifiable for #self_name {
type Id = #id_type;
fn id(&self) -> &Self::Id {
&self.#id_name
}
}
impl ::cido::__internal::Transformer for #self_name {
type Cidomap = #cidomap;
type GraphqlInput = #graphql_input_type;
type GraphqlOutput = #graphql_output_type;
const TRACKING_LEVEL: ::cido::__internal::TrackingLevel = ::cido::__internal::TrackingLevel::Block;
const TRANSFORM_TYPE: ::cido::__internal::RecordType = #transform_type;
fn table_descriptor() -> &'static ::cido::__internal::TableDescriptor::<Self> {
Self::__table_descriptor_inner()
}
fn bind_array<'q>(
iter: impl Iterator<Item = &'q Self> + Clone + Send + 'q,
query: &mut ::cido::__internal::sqlx::query_builder::Separated<'_, 'q, ::cido::__internal::sqlx::Postgres, &'static str>,
) {
Self::__bind_array_inner(iter, query)
}
fn name() -> &'static str {
#self_name_str
}
}
})
}
fn gen_cache_policy_trait(&self) -> TokenStream {
let struct_name = self.struct_name();
let policy_impl = match self.struct_opts.cache_policy {
CachePolicy::Always => quote! {
impl ::cido::__internal::CachePolicy for #struct_name {
type Transformer = #struct_name;
type CacheId = <Self::Transformer as ::cido::__internal::Identifiable>::Id;
type CacheLoader = ();
const QUERY_DATABASE: bool = false;
const QUERY_DATABASE_IF_NOT_FULL: bool = false;
const LOAD_DATABASE: bool = true;
const LOAD_MOST_RECENT_FIRST: bool = false;
const SIZE: usize = usize::MAX;
const MAX_SIZE: usize = usize::MAX;
}
},
CachePolicy::Lru { size, max_size } => {
quote! {
impl ::cido::__internal::CachePolicy for #struct_name {
type Transformer = #struct_name;
type CacheId = <Self::Transformer as ::cido::__internal::Identifiable>::Id;
type CacheLoader = usize;
const QUERY_DATABASE: bool = true;
const QUERY_DATABASE_IF_NOT_FULL: bool = false;
const LOAD_DATABASE: bool = true;
const LOAD_MOST_RECENT_FIRST: bool = true;
const SIZE: usize = #size;
const MAX_SIZE: usize = #max_size;
}
}
}
CachePolicy::Block => {
quote! {
impl ::cido::__internal::CachePolicy for #struct_name {
type Transformer = #struct_name;
type CacheId = <Self::Transformer as ::cido::__internal::Identifiable>::Id;
type CacheLoader = ::core::convert::Infallible;
const QUERY_DATABASE: bool = false;
const QUERY_DATABASE_IF_NOT_FULL: bool = false;
const LOAD_DATABASE: bool = false;
const LOAD_MOST_RECENT_FIRST: bool = false;
const SIZE: usize = 0;
const MAX_SIZE: usize = 1;
}
}
}
CachePolicy::Never => {
quote! {
impl ::cido::__internal::CachePolicy for #struct_name {
type Transformer = #struct_name;
type CacheId = <Self::Transformer as ::cido::__internal::Identifiable>::Id;
type CacheLoader = ::core::convert::Infallible;
const QUERY_DATABASE: bool = false;
const QUERY_DATABASE_IF_NOT_FULL: bool = false;
const LOAD_DATABASE: bool = false;
const LOAD_MOST_RECENT_FIRST: bool = false;
const SIZE: usize = 0;
const MAX_SIZE: usize = 0;
}
}
}
CachePolicy::Custom => return quote! {},
};
quote! {
#policy_impl
impl ::cido::__internal::CacheKey<#struct_name> for <#struct_name as ::cido::__internal::Identifiable>::Id {
fn eq_key(&self, cache_id: &<#struct_name as CachePolicy>::CacheId) -> bool {
self == cache_id
}
fn hash_cache<H: ::std::hash::Hasher>(&self, state: &mut H) {
<Self as ::std::hash::Hash>::hash(self, state);
}
fn to_id(&self) -> Self {
self.clone()
}
fn eq_id(&self, id: &Self) -> bool {
self == id
}
fn to_cache_id(&self) -> <#struct_name as CachePolicy>::CacheId {
self.clone()
}
}
}
}
}