use crate::blob::{BlobAddress, BlobDescriptor};
use crate::envelope::EventEnvelope;
use crate::ids::{BlobUploadId, ByteCount, EvidenceId, FenceToken, Seq};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AppendError {
VersionConflict { actual: u32, expected: u32 },
Fenced {
presented: FenceToken,
current: FenceToken,
},
MalformedBatch(String),
Storage(String),
}
impl std::fmt::Display for AppendError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::VersionConflict { actual, expected } => {
write!(f, "version conflict: actual {actual}, expected {expected}")
}
Self::Fenced { presented, current } => {
write!(f, "fenced: presented {presented}, current {current}")
}
Self::MalformedBatch(reason) => write!(f, "malformed batch: {reason}"),
Self::Storage(reason) => write!(f, "storage error: {reason}"),
}
}
}
impl std::error::Error for AppendError {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StorageError(pub String);
impl std::fmt::Display for StorageError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "storage error: {}", self.0)
}
}
impl std::error::Error for StorageError {}
pub const MAX_READ_LIMIT: usize = 65_536;
pub trait EventStore {
fn append(
&self,
expected_version: u32,
fence: Option<FenceToken>,
events: Vec<EventEnvelope>,
) -> impl Future<Output = Result<Vec<EventEnvelope>, AppendError>>;
fn read_from(
&self,
cursor: Option<Seq>,
limit: usize,
) -> impl Future<Output = Result<Vec<EventEnvelope>, StorageError>>;
fn watermark(&self) -> impl Future<Output = Result<Option<Seq>, StorageError>>;
fn grant_fence(&self) -> impl Future<Output = Result<FenceToken, StorageError>>;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BlobError {
NotFound,
Tombstoned,
DigestMismatch {
expected: BlobAddress,
actual: BlobAddress,
},
Integrity(String),
Pinned,
Storage(String),
}
impl std::fmt::Display for BlobError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::NotFound => f.write_str("blob not found"),
Self::Tombstoned => f.write_str("blob tombstoned: the wrapped key is gone"),
Self::DigestMismatch { expected, actual } => {
write!(f, "digest mismatch: expected {expected}, got {actual}")
}
Self::Integrity(reason) => write!(f, "blob integrity failure: {reason}"),
Self::Pinned => f.write_str("blob is pinned as evidence"),
Self::Storage(reason) => write!(f, "storage error: {reason}"),
}
}
}
impl std::error::Error for BlobError {}
pub trait BlobStore {
fn begin(
&self,
media_type: String,
byte_size: ByteCount,
) -> impl Future<Output = Result<BlobUploadId, BlobError>>;
fn write_chunk(
&self,
upload: &BlobUploadId,
sequence: u32,
chunk: &[u8],
) -> impl Future<Output = Result<(), BlobError>>;
fn commit(
&self,
upload: BlobUploadId,
address: BlobAddress,
) -> impl Future<Output = Result<(BlobDescriptor, bool), BlobError>>;
fn abort(&self, upload: BlobUploadId) -> impl Future<Output = Result<(), BlobError>>;
fn read(
&self,
address: &BlobAddress,
offset: ByteCount,
length: ByteCount,
) -> impl Future<Output = Result<Vec<u8>, BlobError>>;
fn stat(
&self,
address: &BlobAddress,
) -> impl Future<Output = Result<Option<BlobDescriptor>, BlobError>>;
fn pin(
&self,
address: &BlobAddress,
evidence: &EvidenceId,
) -> impl Future<Output = Result<(), BlobError>>;
fn unpin(
&self,
address: &BlobAddress,
evidence: &EvidenceId,
) -> impl Future<Output = Result<(), BlobError>>;
fn sweep(&self) -> impl Future<Output = Result<Vec<BlobAddress>, BlobError>>;
fn shred(&self, address: &BlobAddress) -> impl Future<Output = Result<(), BlobError>>;
}