use async_trait::async_trait;
use crate::Session;
use crate::session::SessionMeta;
use crate::time_compat::SystemTime;
use crate::types::SessionId;
#[derive(Debug, Clone, Default)]
pub struct SessionFilter {
pub created_after: Option<SystemTime>,
pub updated_after: Option<SystemTime>,
pub limit: Option<usize>,
pub offset: Option<usize>,
}
#[derive(Debug, thiserror::Error)]
pub enum SessionStoreError {
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Serialization error: {0}")]
Serialization(String),
#[error("Session not found: {0}")]
NotFound(SessionId),
#[error("Session corrupted: {0}")]
Corrupted(SessionId),
#[error(
"session {id} save rejected: new message count {new_len} is shorter than previously \
persisted {prev_len} — shrink operations must go through Session::fork_at (F1 \
closure, wave-c C-H1)"
)]
MonotonicityViolation {
id: SessionId,
prev_len: usize,
new_len: usize,
},
#[error("Internal error: {0}")]
Internal(String),
}
pub fn append_only_save_guard(
incoming: &Session,
previous: Option<&Session>,
) -> Result<(), SessionStoreError> {
if let Some(prev) = previous {
let prev_len = prev.messages().len();
let new_len = incoming.messages().len();
if new_len < prev_len {
return Err(SessionStoreError::MonotonicityViolation {
id: incoming.id().clone(),
prev_len,
new_len,
});
}
}
Ok(())
}
impl From<serde_json::Error> for SessionStoreError {
fn from(e: serde_json::Error) -> Self {
Self::Serialization(e.to_string())
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait SessionStore: Send + Sync {
async fn save(&self, session: &Session) -> Result<(), SessionStoreError>;
async fn load(&self, id: &SessionId) -> Result<Option<Session>, SessionStoreError>;
async fn list(&self, filter: SessionFilter) -> Result<Vec<SessionMeta>, SessionStoreError>;
async fn delete(&self, id: &SessionId) -> Result<(), SessionStoreError>;
async fn exists(&self, id: &SessionId) -> Result<bool, SessionStoreError> {
Ok(self.load(id).await?.is_some())
}
}