#[derive(Debug, thiserror::Error)] pub enum AppTransportError { #[error("invalid session")] InvalidSession, #[error("unauthorized")] Unauthorized, #[error("token invalid or expired")] TokenInvalidOrExpired, #[error("renewal limit exceeded")] RenewalLimitExceeded, #[error("rate limit exceeded")] RateLimitExceeded, #[error("room not found")] RoomNotFound, #[error("access denied")] AccessDenied, #[error("internal error")] Internal, } impl From for AppTransportError { fn from(e: room::error::RoomError) -> Self { match e { room::error::RoomError::Unauthorized => AppTransportError::Unauthorized, room::error::RoomError::NoPower => AppTransportError::AccessDenied, room::error::RoomError::NotFound(_) => AppTransportError::RoomNotFound, room::error::RoomError::RateLimited(_) => AppTransportError::RateLimitExceeded, room::error::RoomError::BadRequest(_) => AppTransportError::Internal, room::error::RoomError::Database(_) => AppTransportError::Internal, room::error::RoomError::RoleParseError => AppTransportError::Internal, room::error::RoomError::Internal(_) => AppTransportError::Internal, } } } impl AppTransportError { /// Map error to a (code, error_type) pair for WS error messages. /// Frontend can use these to distinguish auth vs rate-limit vs internal errors. pub fn ws_error_code(&self) -> (u16, &'static str) { match self { AppTransportError::Unauthorized => (401, "unauthorized"), AppTransportError::TokenInvalidOrExpired => (401, "token_invalid"), AppTransportError::AccessDenied => (403, "access_denied"), AppTransportError::RateLimitExceeded => (429, "rate_limit_exceeded"), AppTransportError::RoomNotFound => (404, "not_found"), AppTransportError::InvalidSession => (401, "invalid_session"), AppTransportError::RenewalLimitExceeded => (429, "renewal_limit"), AppTransportError::Internal => (500, "internal_error"), } } }