use core::fmt::Debug;
use core::fmt::Display;
use core::hash::Hash;
use core::ops::ControlFlow;
use core::str::FromStr;
use std::sync::Arc;
use chrono::{DateTime, Utc};
use futures::future::{BoxFuture, LocalBoxFuture};
use crate::context::{Context, GeneratorContext, PreprocessingContext};
use crate::graphql::{GraphqlInputType, GraphqlOutputType, LoaderConfig};
use crate::network::{EventOrder, Network};
use crate::prelude::CidomapError;
use crate::{DynError, stable_hash};
pub trait Identifiable: Debug {
type Id: Debug
+ Ord
+ Hash
+ Clone
+ Sized
+ Send
+ Sync
+ Unpin
+ 'static
+ stable_hash::StableHash
+ sqlx::Type<sqlx::Postgres>
+ for<'a> sqlx::Encode<'a, sqlx::Postgres>
+ for<'a> sqlx::Decode<'a, sqlx::Postgres>
+ crate::filter::ToDbFilter;
fn id(&self) -> &Self::Id;
}
pub trait FromStrErrDisplay: FromStr {
type Error: Display;
fn _from_str(s: &str) -> Result<Self, Self::Error>;
}
impl<T> FromStrErrDisplay for T
where
T: FromStr,
<T as FromStr>::Err: Display,
{
type Error = <T as FromStr>::Err;
fn _from_str(s: &str) -> Result<Self, Self::Error> {
<Self as FromStr>::from_str(s)
}
}
#[derive(
Debug,
Copy,
Clone,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
array_map::Indexable,
sqlx::Type,
serde::Serialize,
serde::Deserialize,
)]
#[repr(i16)]
#[serde(rename_all = "lowercase")]
pub enum ProcessingOrder {
One = 1,
Two = 2,
Three = 3,
}
pub trait ProofOfIndexingImpl: stable_hash::StableHash + Identifiable {}
impl<T> ProofOfIndexingImpl for T
where
T: Transformer + stable_hash::StableHash + Identifiable,
<T as Identifiable>::Id: stable_hash::StableHash,
{
}
pub trait Transformer:
Sized
+ PartialEq
+ Hash
+ Send
+ Sync
+ Unpin
+ CachePolicy<Transformer = Self>
+ Clone
+ Debug
+ Default
+ Identifiable<Id: CacheKey<Self>>
+ ProofOfIndexingImpl
+ LoaderConfig
+ for<'r> sqlx::FromRow<'r, sqlx::postgres::PgRow>
+ crate::filter::ToDbFilter
+ 'static
{
type Cidomap: Cidomap;
type GraphqlInput: GraphqlInputType<Transformer = Self>;
type GraphqlOutput: Default
+ TryFrom<Self, Error = DynError>
+ GraphqlOutputType<Transformer = Self>;
const TRACKING_LEVEL: TrackingLevel;
const TRANSFORM_TYPE: RecordType;
fn bind_array<'q>(
array: impl Iterator<Item = &'q Self> + Clone + Send + 'q,
query: &mut sqlx::query_builder::Separated<'_, 'q, sqlx::Postgres, &'static str>,
);
fn table_descriptor() -> &'static crate::persistence::TableDescriptor<Self>;
fn name() -> &'static str;
}
#[non_exhaustive]
pub enum TrackingLevel {
Block,
Event,
}
#[derive(Clone, Copy, sqlx::Type, PartialEq, Eq, Debug)]
#[repr(i32)]
#[non_exhaustive]
pub enum RecordType {
Entity = 1,
Event = 2,
}
impl RecordType {
pub const fn spans_blocks(self) -> bool {
matches!(self, Self::Entity)
}
}
pub trait Query: Send {
type Cidomap;
type QueryType: Transformer<Cidomap = Self::Cidomap>;
fn visit(&mut self, query_type: &Self::QueryType) -> core::ops::ControlFlow<()>;
}
pub trait ProcessingOrderFilter {
type Network: Network;
type Param: Send;
const PROCESSING_ORDER: ProcessingOrder;
fn new_filter(
p: Self::Param,
) -> Result<<Self::Network as Network>::TriggerFilter, <Self::Network as Network>::Error>;
}
pub trait TryFromTrigger: Sized {
type Network: Network;
fn try_from_trigger(
trigger: &<Self::Network as Network>::Trigger,
filter: &<Self::Network as Network>::TriggerFilter,
) -> Option<Result<Self, <Self::Network as Network>::Error>>;
}
pub trait ParseContext<'a, C: Cidomap> {
fn filter(&self) -> &<C::Network as Network>::TriggerFilter;
fn remove_filter(self) -> impl core::future::Future<Output = Result<(), CidomapError>> + Send;
}
pub trait ParsingContext<H: EventHandler>: Default + Send + Sync {
fn handle_error<'a>(
&'a mut self,
ctx: impl ParseContext<'a, H::Cidomap>,
error: <H::Cidomap as Cidomap>::Error,
) -> BoxFuture<'a, ControlFlow<<H::Cidomap as Cidomap>::Error, <H::Cidomap as Cidomap>::Error>>;
}
pub trait EventHandler: Debug + Send + Sync + Sized + 'static {
type Cidomap: Cidomap;
type Cacheless: Debug + Send + Sync + Sized + 'static;
type ContextHandler: ParsingContext<Self>;
fn parse_trigger(
handler: &mut Self::ContextHandler,
trigger: &<<Self::Cidomap as Cidomap>::Network as Network>::Trigger,
filter: &<<Self::Cidomap as Cidomap>::Network as Network>::TriggerFilter,
) -> Result<Self::Cacheless, <Self::Cidomap as Cidomap>::Error>;
fn event_handler(
self,
ctx: Context<'_, Self::Cidomap>,
meta: MetaEvent<Self::Cidomap>,
) -> impl Future<Output = Result<(), <Self::Cidomap as Cidomap>::Error>>;
fn event_source<'a>(
&'a self,
ctx: GeneratorContext<'a, Self::Cidomap>,
meta: &'a MetaEvent<Self::Cidomap>,
) -> impl Future<Output = Result<(), <Self::Cidomap as Cidomap>::Error>>;
fn event_preprocessor<'a>(
ctx: &'a PreprocessingContext<Self::Cidomap>,
meta: MetaEvent<Self::Cidomap>,
event: Self::Cacheless,
) -> either::Either<
BoxFuture<'a, Result<(MetaEvent<Self::Cidomap>, Self), <Self::Cidomap as Cidomap>::Error>>,
Result<(MetaEvent<Self::Cidomap>, Self), <Self::Cidomap as Cidomap>::Error>,
>;
}
pub trait TypeIter<C: Cidomap> {
fn next_type<E: Transformer<Cidomap = C>>(&mut self) -> Result<(), CidomapError>;
}
pub trait AsyncTypeIter<C: Cidomap>: Send {
fn next_type<E: Transformer<Cidomap = C>>(&mut self) -> BoxFuture<'_, Result<(), CidomapError>>;
}
pub trait Cidomap: 'static + Send + Sync + Sized + core::fmt::Debug + Clone {
type Error: std::error::Error + Send + Sync + 'static;
type Network: crate::network::Network;
type Events: EventHandler<Cidomap = Self>;
type Graphql: 'static + Send + Sync + Default + async_graphql::ObjectType;
const MAX_PROCESSING_ORDER: ProcessingOrder;
const START_BLOCK: <Self::Network as Network>::BlockNumber;
const MAX_BLOCK: Option<<Self::Network as Network>::BlockNumber> = None;
fn type_iter<T: TypeIter<Self>>(t: &mut T) -> Result<(), CidomapError>;
fn async_type_iter<T: AsyncTypeIter<Self>>(t: &mut T) -> BoxFuture<'_, Result<(), CidomapError>>;
fn initial_filters()
-> Result<Vec<<Self::Network as Network>::TriggerFilter>, <Self::Network as Network>::Error>;
fn init<'a>(ctx: Context<'a, Self>) -> LocalBoxFuture<'a, Result<(), Self::Error>> {
let _ = ctx;
Box::pin(futures::future::ok(()))
}
fn create<'a>(ctx: Context<'a, Self>) -> LocalBoxFuture<'a, Result<(), Self::Error>> {
let _ = ctx;
Box::pin(futures::future::ok(()))
}
}
pub trait CachePolicy {
type Transformer: Transformer;
type CacheId: Ord + Hash + Sized + Clone + Send + Sync + Debug + 'static;
type CacheLoader: CacheLoader<Self::Transformer>;
const QUERY_DATABASE: bool;
const QUERY_DATABASE_IF_NOT_FULL: bool;
const LOAD_DATABASE: bool;
const LOAD_MOST_RECENT_FIRST: bool;
const SIZE: usize;
const MAX_SIZE: usize;
}
pub trait CacheKey<T: Transformer> {
fn eq_key(&self, cache_id: &<T as CachePolicy>::CacheId) -> bool {
self.to_cache_id() == *cache_id
}
fn hash_cache<H: std::hash::Hasher>(&self, state: &mut H) {
<<T as CachePolicy>::CacheId as std::hash::Hash>::hash(&self.to_cache_id(), state);
}
fn to_id(&self) -> <T as Identifiable>::Id;
fn eq_id(&self, id: &<T as Identifiable>::Id) -> bool {
self.to_id() == *id
}
fn to_cache_id(&self) -> <T as CachePolicy>::CacheId;
}
impl<'a, T: Transformer, C: CacheKey<T>> CacheKey<T> for &'a C {
fn eq_key(&self, cache_id: &<T as CachePolicy>::CacheId) -> bool {
(*self).eq_key(cache_id)
}
fn hash_cache<H: std::hash::Hasher>(&self, state: &mut H) {
(*self).hash_cache(state)
}
fn to_id(&self) -> <T as Identifiable>::Id {
(*self).to_id()
}
fn eq_id(&self, id: &<T as Identifiable>::Id) -> bool {
(*self).eq_id(id)
}
fn to_cache_id(&self) -> <T as CachePolicy>::CacheId {
(*self).to_cache_id()
}
}
pub trait RecordEvent<EO: EventOrder, T: Identifiable> {
fn event_order(&self) -> EO;
fn record(&self) -> &T;
}
pub trait CacheLoader<T: Transformer>: Sized + Send {
fn new(
first: &dyn RecordEvent<<<T::Cidomap as Cidomap>::Network as Network>::EventOrder, T>,
) -> Self;
fn continue_loading(
&mut self,
t: &dyn RecordEvent<<<T::Cidomap as Cidomap>::Network as Network>::EventOrder, T>,
) -> ::core::ops::ControlFlow<bool>;
}
impl<T: Transformer> CacheLoader<T> for () {
fn new(
_: &dyn RecordEvent<<<T::Cidomap as Cidomap>::Network as Network>::EventOrder, T>,
) -> Self {
}
fn continue_loading(
&mut self,
_: &dyn RecordEvent<<<T::Cidomap as Cidomap>::Network as Network>::EventOrder, T>,
) -> core::ops::ControlFlow<bool> {
::core::ops::ControlFlow::Continue(())
}
}
impl<T: Transformer> CacheLoader<T> for ::core::convert::Infallible {
fn new(
_: &dyn RecordEvent<<<T::Cidomap as Cidomap>::Network as Network>::EventOrder, T>,
) -> Self {
unreachable!()
}
fn continue_loading(
&mut self,
_: &dyn RecordEvent<<<T::Cidomap as Cidomap>::Network as Network>::EventOrder, T>,
) -> core::ops::ControlFlow<bool> {
::core::ops::ControlFlow::Break(false)
}
}
impl<T: Transformer> CacheLoader<T> for usize {
fn new(
_: &dyn RecordEvent<<<T::Cidomap as Cidomap>::Network as Network>::EventOrder, T>,
) -> Self {
0
}
fn continue_loading(
&mut self,
_: &dyn RecordEvent<<<T::Cidomap as Cidomap>::Network as Network>::EventOrder, T>,
) -> core::ops::ControlFlow<bool> {
*self += 1;
if *self >= <T as CachePolicy>::SIZE {
core::ops::ControlFlow::Break(true)
} else {
core::ops::ControlFlow::Continue(())
}
}
}
pub struct MetaEvent<C: Cidomap> {
trigger: <C::Network as Network>::Trigger,
timestamp: DateTime<Utc>,
trigger_processing_order: u8,
order: <C::Network as Network>::EventOrder,
block: Arc<<C::Network as Network>::FullBlock>,
}
impl<C: Cidomap> MetaEvent<C> {
pub fn new(
trigger: <C::Network as Network>::Trigger,
timestamp: DateTime<Utc>,
trigger_processing_order: u8,
order: <C::Network as Network>::EventOrder,
block: Arc<<C::Network as Network>::FullBlock>,
) -> Self {
Self {
trigger,
timestamp,
trigger_processing_order,
order,
block,
}
}
#[inline]
pub fn trigger(&self) -> &<C::Network as Network>::Trigger {
&self.trigger
}
pub(crate) fn trigger_processing_order(&self) -> u8 {
self.trigger_processing_order
}
#[inline]
pub fn block_number(&self) -> <C::Network as Network>::BlockNumber {
self.order.block_number()
}
#[inline]
pub fn timestamp(&self) -> DateTime<Utc> {
self.timestamp
}
#[inline]
pub fn event_order(&self) -> <C::Network as Network>::EventOrder {
self.order
}
#[inline]
pub fn block(&self) -> &Arc<<C::Network as Network>::FullBlock> {
&self.block
}
}