use crate::{Batch, BatchId, PostageContext};
pub trait BatchStore {
type Error: std::error::Error;
fn get(&self, id: &BatchId) -> Result<Option<Batch>, Self::Error>;
fn put(&self, batch: Batch) -> Result<(), Self::Error>;
fn remove(&self, id: &BatchId) -> Result<bool, Self::Error>;
fn contains(&self, id: &BatchId) -> Result<bool, Self::Error>;
fn context(&self) -> Result<PostageContext, Self::Error>;
fn set_context(&self, state: PostageContext) -> Result<(), Self::Error>;
fn batch_ids(&self) -> Result<Vec<BatchId>, Self::Error>;
fn count(&self) -> Result<usize, Self::Error>;
}
pub trait BatchStoreExt: BatchStore {
fn get_usable(
&self,
id: &BatchId,
confirmation_threshold: u64,
) -> Result<Batch, BatchStoreError<Self::Error>> {
let batch = self
.get(id)
.map_err(BatchStoreError::Store)?
.ok_or(BatchStoreError::NotFound(*id))?;
let state = self.context().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> BatchStoreExt for T {}
#[non_exhaustive]
#[derive(Debug, thiserror::Error)]
pub enum BatchStoreError<E: std::error::Error> {
#[error("batch not found: {0}")]
NotFound(BatchId),
#[error(
"batch {batch_id} not usable: created at block {created}, current block {current}, need {threshold} confirmations"
)]
NotUsable {
batch_id: BatchId,
created: u64,
current: u64,
threshold: u64,
},
#[error("batch {batch_id} expired: value {value} <= total_amount {total_amount}")]
Expired {
batch_id: BatchId,
value: u128,
total_amount: u128,
},
#[error("store error: {0}")]
Store(#[from] E),
}