821537186e
- Reorganized import statements in adapter tests for better readability - Replaced or_insert_with(Vec::new) with or_default() in test closures - Updated Cargo.lock with new dependency versions and checksums - Added TLS features to tonic dependency configuration - Included sqlx, chrono, and uuid dependencies with specific features - Added jsonwebtoken and arc-swap as project dependencies - Reformatted assertion statements to comply with line length limits - Adjusted base64 import order in engine codec module - Updated protobuf include statement formatting
256 lines
8.4 KiB
Rust
256 lines
8.4 KiB
Rust
//! Unified error type for imks.
|
|
//!
|
|
//! Consolidates all submodule-specific error enums into a single `ImksError`.
|
|
//! Public APIs return `ImksResult<T>`, private code may still use local enums
|
|
//! for internal dispatch and convert via `From` / `.map_err()` when crossing
|
|
//! module boundaries.
|
|
|
|
use std::string::FromUtf8Error;
|
|
|
|
use thiserror::Error;
|
|
|
|
/// Unified error enum for the entire imks crate.
|
|
#[derive(Debug, Error)]
|
|
pub enum ImksError {
|
|
// Protocol layer (engine)
|
|
#[error("invalid engine packet type: {0}")]
|
|
InvalidEnginePacketType(u8),
|
|
#[error("invalid engine packet type char: {0}")]
|
|
InvalidEnginePacketTypeChar(char),
|
|
#[error("empty engine packet")]
|
|
EmptyEnginePacket,
|
|
#[error("invalid base64: {0}")]
|
|
InvalidBase64(#[from] base64::DecodeError),
|
|
#[error("invalid utf8 in packet: {0}")]
|
|
InvalidPacketUtf8(#[from] FromUtf8Error),
|
|
#[error("engine serialization error: {0}")]
|
|
EngineSerialization(String),
|
|
|
|
// Transport upgrade
|
|
#[error("session not found for upgrade")]
|
|
UpgradeSessionNotFound,
|
|
#[error("session already closed, cannot upgrade")]
|
|
UpgradeSessionClosed,
|
|
#[error("invalid session state for upgrade")]
|
|
UpgradeInvalidState,
|
|
|
|
// Socket.IO layer
|
|
#[error("invalid socket packet type: {0}")]
|
|
InvalidSocketPacketType(u8),
|
|
#[error("invalid socket packet type char: {0}")]
|
|
InvalidSocketPacketTypeChar(char),
|
|
#[error("empty socket packet")]
|
|
EmptySocketPacket,
|
|
#[error("invalid socket packet format: {0}")]
|
|
InvalidSocketPacketFormat(String),
|
|
#[error("missing namespace in socket packet")]
|
|
MissingNamespace,
|
|
#[error("invalid attachment count in binary event")]
|
|
InvalidAttachmentCount,
|
|
|
|
// Socket namespace
|
|
#[error("namespace error: {0}")]
|
|
Namespace(String),
|
|
#[error("socket not found: {0}")]
|
|
SocketNotFound(String),
|
|
#[error("failed to send packet to socket: channel full")]
|
|
SocketSendFull,
|
|
|
|
// Adapter layer
|
|
#[error("adapter redis error: {0}")]
|
|
AdapterRedis(String),
|
|
#[error("adapter nats error: {0}")]
|
|
AdapterNats(String),
|
|
#[error("adapter message bus error: {0}")]
|
|
AdapterMessageBus(String),
|
|
#[error("adapter serialization error: {0}")]
|
|
AdapterSerialization(String),
|
|
#[error("adapter room error: {0}")]
|
|
AdapterRoom(String),
|
|
|
|
// Message bus
|
|
#[error("message bus connection closed")]
|
|
MessageBusConnectionClosed,
|
|
#[error("message bus channel not found: {0}")]
|
|
MessageBusChannelNotFound(String),
|
|
|
|
// Session store
|
|
#[error("session not found: {0}")]
|
|
SessionNotFound(String),
|
|
#[error("session expired: {0}")]
|
|
SessionExpired(String),
|
|
#[error("session store redis error: {0}")]
|
|
SessionRedis(String),
|
|
#[error("session serialization error: {0}")]
|
|
SessionSerialization(String),
|
|
|
|
// Database
|
|
#[error("database error: {0}")]
|
|
Database(#[from] sqlx::Error),
|
|
|
|
// gRPC
|
|
#[error("gRPC error: {0}")]
|
|
GrpcStatus(#[from] tonic::Status),
|
|
#[error("gRPC transport error: {0}")]
|
|
GrpcTransport(#[from] tonic::transport::Error),
|
|
|
|
// Serialization
|
|
#[error("JSON error: {0}")]
|
|
Json(#[from] serde_json::Error),
|
|
|
|
// Transport
|
|
#[error("webtransport error: {0}")]
|
|
WebTransport(String),
|
|
#[error("IO error: {0}")]
|
|
Io(#[from] std::io::Error),
|
|
|
|
// Auth
|
|
#[error("auth error: {0}")]
|
|
Auth(String),
|
|
#[error("token expired")]
|
|
TokenExpired,
|
|
|
|
// General
|
|
#[error("not found: {0}")]
|
|
NotFound(String),
|
|
#[error("invalid input: {0}")]
|
|
InvalidInput(String),
|
|
#[error("internal error: {0}")]
|
|
Internal(String),
|
|
}
|
|
|
|
/// Convenience alias used across all public APIs.
|
|
pub type ImksResult<T> = Result<T, ImksError>;
|
|
|
|
// Conversions from submodule error types (for gradual migration).
|
|
|
|
impl From<crate::engine::packet::PacketError> for ImksError {
|
|
fn from(e: crate::engine::packet::PacketError) -> Self {
|
|
use crate::engine::packet::PacketError::*;
|
|
match e {
|
|
InvalidType(v) => ImksError::InvalidEnginePacketType(v),
|
|
InvalidTypeChar(c) => ImksError::InvalidEnginePacketTypeChar(c),
|
|
Empty => ImksError::EmptyEnginePacket,
|
|
InvalidBase64(e) => ImksError::InvalidBase64(e),
|
|
InvalidUtf8(e) => ImksError::InvalidPacketUtf8(e),
|
|
Serialization(s) => ImksError::EngineSerialization(s),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<crate::engine::upgrade::UpgradeError> for ImksError {
|
|
fn from(e: crate::engine::upgrade::UpgradeError) -> Self {
|
|
use crate::engine::upgrade::UpgradeError::*;
|
|
match e {
|
|
SessionNotFound => ImksError::UpgradeSessionNotFound,
|
|
SessionClosed => ImksError::UpgradeSessionClosed,
|
|
InvalidState => ImksError::UpgradeInvalidState,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<crate::socket::packet::PacketError> for ImksError {
|
|
fn from(e: crate::socket::packet::PacketError) -> Self {
|
|
use crate::socket::packet::PacketError::*;
|
|
match e {
|
|
InvalidType(v) => ImksError::InvalidSocketPacketType(v),
|
|
InvalidTypeChar(c) => ImksError::InvalidSocketPacketTypeChar(c),
|
|
Empty => ImksError::EmptySocketPacket,
|
|
InvalidFormat(s) => ImksError::InvalidSocketPacketFormat(s),
|
|
Json(e) => ImksError::Json(e),
|
|
MissingNamespace => ImksError::MissingNamespace,
|
|
InvalidAttachmentCount => ImksError::InvalidAttachmentCount,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<crate::socket::adapter::AdapterError> for ImksError {
|
|
fn from(e: crate::socket::adapter::AdapterError) -> Self {
|
|
use crate::socket::adapter::AdapterError::*;
|
|
match e {
|
|
Redis(s) => ImksError::AdapterRedis(s),
|
|
Nats(s) => ImksError::AdapterNats(s),
|
|
MessageBus(s) => ImksError::AdapterMessageBus(s),
|
|
Serialization(s) => ImksError::AdapterSerialization(s),
|
|
Room(s) => ImksError::AdapterRoom(s),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<crate::socket::message_bus::MessageBusError> for ImksError {
|
|
fn from(e: crate::socket::message_bus::MessageBusError) -> Self {
|
|
use crate::socket::message_bus::MessageBusError::*;
|
|
match e {
|
|
Redis(s) => ImksError::AdapterRedis(s),
|
|
Nats(s) => ImksError::AdapterNats(s),
|
|
ConnectionClosed => ImksError::MessageBusConnectionClosed,
|
|
ChannelNotFound(s) => ImksError::MessageBusChannelNotFound(s),
|
|
Serialization(s) => ImksError::AdapterSerialization(s),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<crate::socket::session_store::SessionError> for ImksError {
|
|
fn from(e: crate::socket::session_store::SessionError) -> Self {
|
|
use crate::socket::session_store::SessionError::*;
|
|
match e {
|
|
Redis(s) => ImksError::SessionRedis(s),
|
|
NotFound(s) => ImksError::SessionNotFound(s),
|
|
Serialization(s) => ImksError::SessionSerialization(s),
|
|
Expired(s) => ImksError::SessionExpired(s),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<tokio::sync::mpsc::error::TrySendError<crate::socket::packet::Packet>> for ImksError {
|
|
fn from(_: tokio::sync::mpsc::error::TrySendError<crate::socket::packet::Packet>) -> Self {
|
|
ImksError::SocketSendFull
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_imks_error_display() {
|
|
let err = ImksError::NotFound("message 01909abc".into());
|
|
assert_eq!(err.to_string(), "not found: message 01909abc");
|
|
}
|
|
|
|
#[test]
|
|
fn test_imks_error_from_base64() {
|
|
let b64_err = base64::DecodeError::InvalidByte(0, b'!');
|
|
let err: ImksError = b64_err.into();
|
|
assert!(matches!(err, ImksError::InvalidBase64(_)));
|
|
}
|
|
|
|
#[test]
|
|
fn test_imks_error_from_sqlx() {
|
|
// sqlx::Error doesn't impl PartialEq, so just check the variant
|
|
let db_err = sqlx::Error::PoolClosed;
|
|
let err: ImksError = db_err.into();
|
|
assert!(matches!(err, ImksError::Database(_)));
|
|
}
|
|
|
|
#[test]
|
|
fn test_imks_error_from_serde_json() {
|
|
let json_err = serde_json::from_str::<serde_json::Value>("not json").unwrap_err();
|
|
let err: ImksError = json_err.into();
|
|
assert!(matches!(err, ImksError::Json(_)));
|
|
}
|
|
|
|
#[test]
|
|
#[allow(clippy::unnecessary_literal_unwrap)]
|
|
fn test_imks_result_ok() {
|
|
let result: ImksResult<i32> = Ok(42);
|
|
assert_eq!(result.unwrap(), 42);
|
|
}
|
|
|
|
#[test]
|
|
fn test_imks_result_err() {
|
|
let result: ImksResult<i32> = Err(ImksError::TokenExpired);
|
|
assert!(result.is_err());
|
|
}
|
|
}
|