use thiserror::Error;
#[derive(Error, Debug)]
pub enum CanError {
#[error("[1001] Backend not found: {name}")]
BackendNotFound {
name: String,
},
#[error("[1002] Backend '{name}' is already registered")]
BackendAlreadyRegistered {
name: String,
},
#[error("[1003] Backend initialization failed: {reason}")]
InitializationFailed {
reason: String,
},
#[error("[1004] Device not found: {device}")]
DeviceNotFound {
device: String,
},
#[error("[1005] Channel {channel} does not exist (max: {max})")]
ChannelNotFound {
channel: u8,
max: u8,
},
#[error("[1006] Channel {channel} is already open")]
ChannelAlreadyOpen {
channel: u8,
},
#[error("[1007] Channel {channel} is not open")]
ChannelNotOpen {
channel: u8,
},
#[error("[2001] Invalid CAN ID: {value:#X} (max: {max:#X})")]
InvalidId {
value: u32,
max: u32,
},
#[error("[2002] Invalid data length: expected max {expected}, got {actual}")]
InvalidDataLength {
expected: usize,
actual: usize,
},
#[error("[2003] Invalid message format: {reason}")]
InvalidFormat {
reason: String,
},
#[error("[3001] Configuration error: {reason}")]
ConfigError {
reason: String,
},
#[error("[3002] Invalid parameter '{parameter}': {reason}")]
InvalidParameter {
parameter: String,
reason: String,
},
#[error("[3003] Version incompatible: backend {backend_version}, expected {expected_version}")]
VersionIncompatible {
backend_version: String,
expected_version: String,
},
#[error("[4001] Operation timed out after {timeout_ms}ms")]
Timeout {
timeout_ms: u64,
},
#[error("[4002] Insufficient resources: {resource}")]
InsufficientResources {
resource: String,
},
#[error("[4003] Permission denied: {operation}")]
PermissionDenied {
operation: String,
},
#[error("Send failed: {reason}")]
SendFailed {
reason: String,
},
#[error("Receive failed: {reason}")]
ReceiveFailed {
reason: String,
},
#[error("Bus error: {kind:?}")]
BusError {
kind: BusErrorKind,
},
#[error("Unsupported feature: {feature}")]
UnsupportedFeature {
feature: String,
},
#[error("Invalid state: expected {expected}, current {current}")]
InvalidState {
expected: String,
current: String,
},
#[error("Other error: {message}")]
Other {
message: String,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BusErrorKind {
BitError,
StuffError,
CrcError,
AckError,
FormError,
BusOff,
ErrorPassive,
ErrorWarning,
}
impl BusErrorKind {
#[must_use]
pub fn description(&self) -> &'static str {
match self {
Self::BitError => "Bit error",
Self::StuffError => "Stuff error",
Self::CrcError => "CRC error",
Self::AckError => "ACK error",
Self::FormError => "Form error",
Self::BusOff => "Bus-off",
Self::ErrorPassive => "Error passive",
Self::ErrorWarning => "Error warning",
}
}
}
pub type CanResult<T> = Result<T, CanError>;
#[derive(Error, Debug)]
pub enum FilterError {
#[error("Invalid filter configuration: {reason}")]
InvalidConfig {
reason: String,
},
#[error("Filter ID {id:#X} out of range (max: {max:#X})")]
IdOutOfRange {
id: u32,
max: u32,
},
#[error("Invalid ID range: start {start:#X} > end {end:#X}")]
InvalidRange {
start: u32,
end: u32,
},
#[error("Hardware filter limit exceeded: max {max}, requested {requested}")]
HardwareFilterLimitExceeded {
max: usize,
requested: usize,
},
#[error("Filter not found at index {index}")]
FilterNotFound {
index: usize,
},
}
pub type FilterResult<T> = Result<T, FilterError>;
#[derive(Error, Debug)]
pub enum QueueError {
#[error("Queue full: capacity {capacity}")]
QueueFull {
capacity: usize,
},
#[error("Message dropped (ID: {id:#X}): {reason}")]
MessageDropped {
id: u32,
reason: String,
},
#[error("Invalid queue capacity: {capacity} (min: 1)")]
InvalidCapacity {
capacity: usize,
},
#[error("Queue operation timed out after {timeout_ms}ms")]
Timeout {
timeout_ms: u64,
},
}
pub type QueueResult<T> = Result<T, QueueError>;
#[derive(Error, Debug)]
pub enum MonitorError {
#[error("Reconnect failed: {reason}")]
ReconnectFailed {
reason: String,
},
#[error("Monitor not started")]
NotStarted,
#[error("Monitor already running")]
AlreadyRunning,
#[error("Backend error: {0}")]
BackendError(#[from] CanError),
#[error("Invalid monitor configuration: {reason}")]
InvalidConfig {
reason: String,
},
#[error("Heartbeat timeout after {timeout_ms}ms")]
HeartbeatTimeout {
timeout_ms: u64,
},
}
pub type MonitorResult<T> = Result<T, MonitorError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
let err = CanError::InvalidId {
value: 0x800,
max: 0x7FF,
};
let msg = format!("{err}");
assert!(msg.contains("0x800"));
assert!(msg.contains("0x7FF"));
}
#[test]
fn test_bus_error_kind() {
let error = BusErrorKind::BitError;
assert_eq!(error.description(), "Bit error");
}
#[test]
fn test_error_codes() {
let err = CanError::BackendNotFound {
name: "test".to_string(),
};
let msg = format!("{err}");
assert!(msg.contains("[1001]"));
}
}