99 lines
3.3 KiB
Rust
99 lines
3.3 KiB
Rust
//! Typed error enum for the WebSocket service layer.
|
|
//!
|
|
//! Every distinct failure mode the WS layer can produce is represented here.
|
|
//! The HTTP/WS adapter maps these to close codes or error frames.
|
|
|
|
/// Errors produced by the WebSocket service layer.
|
|
///
|
|
/// Each variant maps to a distinct failure mode; the transport adapter
|
|
/// translates these into WS close codes or error-frame messages.
|
|
#[derive(Debug)]
|
|
#[allow(dead_code)]
|
|
pub enum Error {
|
|
/// Client sent a message that could not be parsed as a valid `WsRequest`.
|
|
/// Maps to an `error` frame with the parse failure detail.
|
|
InvalidMessage(String),
|
|
/// The chat subsystem returned an error (e.g. LLM provider failure).
|
|
/// Maps to an `error` frame with the provider message.
|
|
Chat(String),
|
|
/// A permission response referenced an unknown `request_id`.
|
|
/// Silently ignored in practice (no frame sent), but tracked for observability.
|
|
UnknownPermissionRequest(String),
|
|
/// Failed to load initial state (pipeline, onboarding, wizard).
|
|
/// Maps to an `error` frame.
|
|
Init(String),
|
|
/// The send channel to the client is closed (client disconnected).
|
|
/// Triggers connection teardown — no frame is sent.
|
|
ClientGone,
|
|
}
|
|
|
|
impl std::fmt::Display for Error {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Self::InvalidMessage(msg) => write!(f, "Invalid request: {msg}"),
|
|
Self::Chat(msg) => write!(f, "Chat error: {msg}"),
|
|
Self::UnknownPermissionRequest(id) => {
|
|
write!(f, "Unknown permission request: {id}")
|
|
}
|
|
Self::Init(msg) => write!(f, "Initialisation error: {msg}"),
|
|
Self::ClientGone => write!(f, "Client disconnected"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn display_invalid_message() {
|
|
let err = Error::InvalidMessage("bad json".to_string());
|
|
assert_eq!(err.to_string(), "Invalid request: bad json");
|
|
}
|
|
|
|
#[test]
|
|
fn display_chat_error() {
|
|
let err = Error::Chat("timeout".to_string());
|
|
assert_eq!(err.to_string(), "Chat error: timeout");
|
|
}
|
|
|
|
#[test]
|
|
fn display_unknown_permission_request() {
|
|
let err = Error::UnknownPermissionRequest("req-99".to_string());
|
|
assert_eq!(err.to_string(), "Unknown permission request: req-99");
|
|
}
|
|
|
|
#[test]
|
|
fn display_init_error() {
|
|
let err = Error::Init("CRDT not ready".to_string());
|
|
assert_eq!(err.to_string(), "Initialisation error: CRDT not ready");
|
|
}
|
|
|
|
#[test]
|
|
fn display_client_gone() {
|
|
let err = Error::ClientGone;
|
|
assert_eq!(err.to_string(), "Client disconnected");
|
|
}
|
|
|
|
#[test]
|
|
fn error_is_debug() {
|
|
let err = Error::InvalidMessage("test".to_string());
|
|
let debug = format!("{err:?}");
|
|
assert!(debug.contains("InvalidMessage"));
|
|
}
|
|
|
|
#[test]
|
|
fn all_variants_display_without_panic() {
|
|
let variants: Vec<Error> = vec![
|
|
Error::InvalidMessage("a".to_string()),
|
|
Error::Chat("b".to_string()),
|
|
Error::UnknownPermissionRequest("c".to_string()),
|
|
Error::Init("d".to_string()),
|
|
Error::ClientGone,
|
|
];
|
|
for v in &variants {
|
|
let _ = v.to_string();
|
|
}
|
|
}
|
|
}
|