use async_trait::async_trait;
use kuatia_types::{
Account, AccountId, AssetId, Book, BookId, Envelope, EnvelopeId, Posting, PostingId,
PostingStatus, Receipt, ReservationId,
};
use crate::error::StoreError;
use crate::events::EventStore;
#[derive(Debug, Clone)]
pub struct EnvelopeRecord {
pub envelope: Envelope,
pub receipt: Receipt,
pub created_at: i64,
}
#[derive(Debug, Clone)]
pub struct PostingQuery {
pub account: AccountId,
pub asset: Option<AssetId>,
pub status: Option<PostingStatus>,
pub limit: Option<u32>,
pub offset: Option<u32>,
}
#[derive(Debug, Clone, Default)]
pub struct TransferQuery {
pub account: Option<AccountId>,
pub from_ts: Option<i64>,
pub to_ts: Option<i64>,
pub book: Option<BookId>,
pub limit: Option<u32>,
pub offset: Option<u32>,
}
#[derive(Debug, Clone)]
pub struct Page<T> {
pub items: Vec<T>,
pub total: u64,
}
#[async_trait]
pub trait AccountStore: Send + Sync {
async fn get_account(&self, id: &AccountId) -> Result<Account, StoreError>;
async fn get_accounts(&self, ids: &[AccountId]) -> Result<Vec<Account>, StoreError>;
async fn create_account(&self, account: Account) -> Result<(), StoreError>;
async fn append_account_version(&self, account: Account) -> Result<(), StoreError>;
async fn get_account_history(&self, id: &AccountId) -> Result<Vec<Account>, StoreError>;
async fn list_accounts(&self) -> Result<Vec<Account>, StoreError>;
}
#[async_trait]
pub trait PostingStore: Send + Sync {
async fn get_postings(&self, ids: &[PostingId]) -> Result<Vec<Posting>, StoreError>;
async fn get_postings_by_account(
&self,
account: &AccountId,
asset: Option<&AssetId>,
status: Option<PostingStatus>,
) -> Result<Vec<Posting>, StoreError>;
async fn reserve_postings(
&self,
ids: &[PostingId],
reservation: ReservationId,
) -> Result<u64, StoreError>;
async fn release_postings(
&self,
ids: &[PostingId],
reservation: ReservationId,
) -> Result<u64, StoreError>;
async fn deactivate_postings(
&self,
ids: &[PostingId],
reservation: Option<ReservationId>,
) -> Result<u64, StoreError>;
async fn insert_postings(&self, postings: &[Posting]) -> Result<u64, StoreError>;
async fn query_postings(&self, query: &PostingQuery) -> Result<Page<Posting>, StoreError> {
let all = self
.get_postings_by_account(&query.account, query.asset.as_ref(), query.status)
.await?;
let total = all.len() as u64;
let offset = query.offset.unwrap_or(0) as usize;
let limit = query.limit.unwrap_or(u32::MAX) as usize;
let items = all.into_iter().skip(offset).take(limit).collect();
Ok(Page { items, total })
}
}
#[async_trait]
pub trait TransferStore: Send + Sync {
async fn get_transfer(&self, id: &EnvelopeId) -> Result<Option<EnvelopeRecord>, StoreError>;
async fn store_transfer(
&self,
record: EnvelopeRecord,
involved: &[AccountId],
) -> Result<u64, StoreError>;
async fn get_transfers_for_account(
&self,
account: &AccountId,
) -> Result<Vec<EnvelopeRecord>, StoreError>;
async fn query_transfers(
&self,
query: &TransferQuery,
) -> Result<Page<EnvelopeRecord>, StoreError> {
let all = if let Some(ref account) = query.account {
self.get_transfers_for_account(account).await?
} else {
return Err(StoreError::Internal(
"query_transfers requires account filter in default implementation".into(),
));
};
let filtered: Vec<EnvelopeRecord> = all
.into_iter()
.filter(|r| {
if let Some(from) = query.from_ts
&& r.created_at < from
{
return false;
}
if let Some(to) = query.to_ts
&& r.created_at >= to
{
return false;
}
if let Some(book) = query.book
&& r.envelope.book() != book
{
return false;
}
true
})
.collect();
let total = filtered.len() as u64;
let offset = query.offset.unwrap_or(0) as usize;
let limit = query.limit.unwrap_or(u32::MAX) as usize;
let items = filtered.into_iter().skip(offset).take(limit).collect();
Ok(Page { items, total })
}
}
#[async_trait]
pub trait SagaStore: Send + Sync {
async fn save_saga(&self, id: &i64, data: Vec<u8>) -> Result<(), StoreError>;
async fn list_pending_sagas(&self) -> Result<Vec<(i64, Vec<u8>)>, StoreError>;
async fn delete_saga(&self, id: &i64) -> Result<(), StoreError>;
}
#[async_trait]
pub trait BookStore: Send + Sync {
async fn create_book(&self, book: Book) -> Result<(), StoreError>;
async fn get_book(&self, id: &BookId) -> Result<Book, StoreError>;
async fn list_books(&self) -> Result<Vec<Book>, StoreError>;
}
pub trait Store:
AccountStore + PostingStore + TransferStore + SagaStore + EventStore + BookStore
{
}
impl<T: AccountStore + PostingStore + TransferStore + SagaStore + EventStore + BookStore> Store
for T
{
}