Files
zhenyi 821537186e refactor(tests): reformat code and update dependency management
- 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
2026-06-11 12:11:05 +08:00

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());
}
}