//! Crate error type. //! //! [`ChatError::Host`] is the seam for the host app: a trait impl that fails in //! the database or in its own authz layer boxes that error rather than the crate //! trying to name every failure either consumer might have. use std::time::Duration; /// Which budget a connection ran out of. /// /// Worth distinguishing: [`LimitScope::User`] means this client should close a /// tab, while [`LimitScope::Global`] means the process is saturated and is a /// signal an operator needs, not an end user. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum LimitScope { /// This user holds too many concurrent connections. User, /// The process is at its total connection budget. Global, } impl std::fmt::Display for LimitScope { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::User => f.write_str("per-user"), Self::Global => f.write_str("global"), } } } /// Anything that can go wrong in the crate or in a host's trait impl. #[derive(Debug, thiserror::Error)] pub enum ChatError { #[error( "retention exceeds the crate ceiling (asked for {max_age:?} / {max_messages} messages)" )] RetentionTooLong { max_age: Duration, max_messages: usize, }, #[error("retention must be non-zero in both bounds")] RetentionTooShort, #[error("message is {len} characters, over the {max} character limit")] MessageTooLong { len: usize, max: usize }, #[error("message is empty")] MessageEmpty, #[error("invalid send nonce")] InvalidNonce, #[error("room is closed")] RoomClosed, #[error("room is read-only")] RoomReadOnly, /// At capacity. The caller has already passed authz by this point, so this is /// a resource refusal and belongs in the 503 family, not the 403 one. #[error("connection limit reached ({0})")] ConnectionLimit(LimitScope), /// The sender did something the room refused. Distinct from a host error: /// this is expected traffic, not a fault, and should not be logged as one. #[error("send rejected: {0:?}")] Rejected(crate::send::SendRejection), /// An error from the host app's trait impl: a query failure, a session /// lookup, anything the crate has no vocabulary for. #[error("host error: {0}")] Host(#[source] Box), } impl ChatError { /// Wrap a host-side error. pub fn host(err: E) -> Self { Self::Host(Box::new(err)) } }