relay_knowledge/storage/contracts/
boundary.rs1use std::{error::Error, fmt, future::Future, pin::Pin};
2
3pub type StorageFuture<'a, T> = Pin<Box<dyn Future<Output = Result<T, StorageError>> + Send + 'a>>;
4
5#[derive(Debug)]
7pub enum StorageError {
8 Io(std::io::Error),
9 Sqlite(rusqlite::Error),
10 Join(tokio::task::JoinError),
11 LockPoisoned,
12 Busy(String),
13 CapacityExceeded(String),
14 DurableStagingRequired(String),
15 DurableStagingPending {
16 completed_steps: usize,
17 max_steps: usize,
18 },
19 DurableFinalizationRequired {
20 checkpoint_state: String,
21 },
22 InvalidInput(String),
23 Invariant(String),
24}
25
26impl fmt::Display for StorageError {
27 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
28 match self {
29 Self::Io(error) => write!(formatter, "storage I/O failed: {error}"),
30 Self::Sqlite(error) => write!(formatter, "sqlite operation failed: {error}"),
31 Self::Join(error) => write!(formatter, "storage worker failed: {error}"),
32 Self::LockPoisoned => write!(formatter, "sqlite connection lock was poisoned"),
33 Self::Busy(message) => write!(formatter, "storage busy: {message}"),
34 Self::CapacityExceeded(message) => {
35 write!(formatter, "storage capacity exceeded: {message}")
36 }
37 Self::DurableStagingRequired(message) => {
38 write!(formatter, "durable staging required: {message}")
39 }
40 Self::DurableStagingPending {
41 completed_steps,
42 max_steps,
43 } => write!(
44 formatter,
45 "durable staging pending after step {completed_steps} of at most {max_steps}"
46 ),
47 Self::DurableFinalizationRequired { checkpoint_state } => write!(
48 formatter,
49 "durable incremental delta committed; finalization must resume from '{checkpoint_state}'"
50 ),
51 Self::InvalidInput(message) => write!(formatter, "invalid storage input: {message}"),
52 Self::Invariant(message) => write!(formatter, "storage invariant failed: {message}"),
53 }
54 }
55}
56
57impl Error for StorageError {}
58
59impl From<std::io::Error> for StorageError {
60 fn from(error: std::io::Error) -> Self {
61 Self::Io(error)
62 }
63}
64
65impl From<rusqlite::Error> for StorageError {
66 fn from(error: rusqlite::Error) -> Self {
67 Self::Sqlite(error)
68 }
69}
70
71impl From<tokio::task::JoinError> for StorageError {
72 fn from(error: tokio::task::JoinError) -> Self {
73 Self::Join(error)
74 }
75}
76
77#[cfg(test)]
78#[path = "boundary_tests.rs"]
79mod tests;