use thiserror::Error; #[derive(Error, Debug)] pub enum StorageError { #[error("session not found: {0}")] NotFound(String), #[error("session already exists: {0}")] AlreadyExists(String), #[error("database error: {0}")] Database(#[from] sqlx::Error), #[error("serialization error: {0}")] Serialization(String), #[error("schema migration error: {0}")] Migration(String), #[error("storage conflict: {0}")] Conflict(String), } impl StorageError { /// Only retry failures that can plausibly clear without changing the data. pub fn is_transient(&self) -> bool { let Self::Database(error) = self else { return false; }; match error { sqlx::Error::PoolTimedOut => true, sqlx::Error::Database(database) => { matches!(database.code().as_deref(), Some("5" | "6" | "261" | "262")) || { let message = database.message().to_ascii_lowercase(); message.contains("database is locked") || message.contains("database is busy") } } _ => false, } } }