Skip to main content

ws_kit/
error.rs

1//! Error types for `ws-kit`.
2//!
3//! All WebSocket failures are represented by [`WsError`]. For token-extraction
4//! specific errors see [`crate::extractor::WsAuthError`].
5
6use thiserror::Error;
7
8/// WebSocket error variants.
9///
10/// Each variant maps to a failure mode encountered during authentication,
11/// connection management, or message handling.
12#[derive(Debug, Error, Clone, PartialEq, Eq)]
13pub enum WsError {
14    /// No authentication token was provided.
15    #[error("authentication token missing")]
16    AuthMissing,
17
18    /// Provided authentication token is invalid or expired.
19    #[error("authentication token invalid")]
20    AuthInvalid,
21
22    /// Connection limit reached.
23    #[error("too many connections")]
24    TooManyConnections,
25
26    /// Broadcast channel is full or lagged.
27    #[error("broadcast channel full")]
28    BroadcastFull,
29
30    /// Incoming message could not be parsed or is semantically invalid.
31    #[error("invalid message")]
32    InvalidMessage,
33
34    /// A compressed payload was malformed: bad envelope flags, corrupt
35    /// deflate stream, or invalid UTF-8 for a text frame.
36    #[error("malformed compressed payload")]
37    Compression,
38
39    /// A payload exceeded the configured size limit — for decompression this
40    /// is the decompression-bomb guard (`CompressionConfig::max_size`).
41    #[error("payload exceeds size limit")]
42    PayloadTooLarge,
43
44    /// Redis (multi-node room fan-out) transport failure. The underlying
45    /// `redis::RedisError` is flattened to its display form to keep
46    /// `WsError` `Clone + Eq` (0.4.0 `redis` feature).
47    #[error("redis error: {0}")]
48    Redis(String),
49
50    /// Connection has been closed.
51    #[error("connection closed")]
52    Closed,
53}
54
55// Tests exercise failure paths and invariants directly; unwrap/expect,
56// slicing, and panicking asserts are acceptable here — violations
57// surface as test failures, not production panics.
58#[allow(
59    clippy::unwrap_used,
60    clippy::expect_used,
61    clippy::indexing_slicing,
62    clippy::panic
63)]
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    #[test]
69    fn display_messages() {
70        assert_eq!(
71            WsError::AuthMissing.to_string(),
72            "authentication token missing"
73        );
74        assert_eq!(
75            WsError::AuthInvalid.to_string(),
76            "authentication token invalid"
77        );
78        assert_eq!(
79            WsError::TooManyConnections.to_string(),
80            "too many connections"
81        );
82        assert_eq!(WsError::BroadcastFull.to_string(), "broadcast channel full");
83        assert_eq!(WsError::InvalidMessage.to_string(), "invalid message");
84        assert_eq!(
85            WsError::Compression.to_string(),
86            "malformed compressed payload"
87        );
88        assert_eq!(
89            WsError::PayloadTooLarge.to_string(),
90            "payload exceeds size limit"
91        );
92        assert_eq!(
93            WsError::Redis("down".to_string()).to_string(),
94            "redis error: down"
95        );
96        assert_eq!(WsError::Closed.to_string(), "connection closed");
97    }
98
99    #[test]
100    fn clone_eq() {
101        let e = WsError::AuthMissing;
102        assert_eq!(e.clone(), WsError::AuthMissing);
103        assert_ne!(WsError::AuthMissing, WsError::Closed);
104    }
105}