use crate::{Batch, BatchId, PostageContext};
pub trait BatchStore {
type Error: std::error::Error;
fn get(
&self,
id: &BatchId,
) -> impl std::future::Future<Output = Result<Option<Batch>, Self::Error>> + Send;
fn put(
&self,
batch: Batch,
) -> impl std::future::Future<Output = Result<(), Self::Error>> + Send;
fn remove(
&self,
id: &BatchId,
) -> impl std::future::Future<Output = Result<bool, Self::Error>> + Send;
fn contains(
&self,
id: &BatchId,
) -> impl std::future::Future<Output = Result<bool, Self::Error>> + Send;
fn context(
&self,
) -> impl std::future::Future<Output = Result<PostageContext, Self::Error>> + Send;
fn set_context(
&self,
state: PostageContext,
) -> impl std::future::Future<Output = Result<(), Self::Error>> + Send;
fn batch_ids(
&self,
) -> impl std::future::Future<Output = Result<Vec<BatchId>, Self::Error>> + Send;
fn count(&self) -> impl std::future::Future<Output = Result<usize, Self::Error>> + Send;
}
pub trait BatchStoreExt: BatchStore + Sync {
fn get_usable(
&self,
id: &BatchId,
confirmation_threshold: u64,
) -> impl std::future::Future<Output = Result<Batch, BatchStoreError<Self::Error>>> + Send {
async move {
let batch = self
.get(id)
.await
.map_err(BatchStoreError::Store)?
.ok_or(BatchStoreError::NotFound(*id))?;
let state = self.context().await.map_err(BatchStoreError::Store)?;
if !batch.is_usable(state.block(), confirmation_threshold) {
return Err(BatchStoreError::NotUsable {
batch_id: *id,
created: batch.start(),
current: state.block(),
threshold: confirmation_threshold,
});
}
if batch.is_expired(state.total_amount()) {
return Err(BatchStoreError::Expired {
batch_id: *id,
value: batch.value(),
total_amount: state.total_amount(),
});
}
Ok(batch)
}
}
}
impl<T: BatchStore + Sync> BatchStoreExt for T {}
#[derive(Debug)]
pub enum BatchStoreError<E> {
NotFound(BatchId),
NotUsable {
batch_id: BatchId,
created: u64,
current: u64,
threshold: u64,
},
Expired {
batch_id: BatchId,
value: u128,
total_amount: u128,
},
Store(E),
}
impl<E: std::fmt::Display> std::fmt::Display for BatchStoreError<E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::NotFound(id) => write!(f, "batch not found: {}", id),
Self::NotUsable {
batch_id,
created,
current,
threshold,
} => write!(
f,
"batch {} not usable: created at block {}, current block {}, need {} confirmations",
batch_id, created, current, threshold
),
Self::Expired {
batch_id,
value,
total_amount,
} => write!(
f,
"batch {} expired: value {} <= total_amount {}",
batch_id, value, total_amount
),
Self::Store(e) => write!(f, "store error: {}", e),
}
}
}
impl<E: std::error::Error + 'static> std::error::Error for BatchStoreError<E> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Store(e) => Some(e),
_ => None,
}
}
}