use std::sync::mpsc;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConfigError {
EmptyPortName,
ZeroBaudRate,
InvalidColumns(usize),
InvalidRows(usize),
InvalidBrightnessRange {
min: u8,
max: u8,
},
ZeroQueueCapacity,
}
#[derive(Debug)]
pub enum VfdError {
Config(ConfigError),
Serial(serialport::Error),
Io(std::io::Error),
InvalidCoordinate {
x: u8,
y: u8,
columns: usize,
rows: usize,
},
InvalidLine {
line: u8,
rows: usize,
},
UnsupportedBrightness {
level: u8,
min: u8,
max: u8,
},
TextTooLong {
max: usize,
},
RawPayloadTooLarge {
length: usize,
max: usize,
},
InvalidMarqueeSpeed {
cps: u32,
max: u32,
},
QueueClosed,
WorkerStopped,
WorkerPanicked,
#[cfg(feature = "tokio")]
WorkerCancelled,
}
impl std::fmt::Display for ConfigError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::EmptyPortName => f.write_str("serial port name must not be empty"),
Self::ZeroBaudRate => f.write_str("baud rate must not be zero"),
Self::InvalidColumns(columns) => {
write!(f, "columns must be in 1..=255, got {columns}")
}
Self::InvalidRows(rows) => write!(f, "rows must be in 1..=255, got {rows}"),
Self::InvalidBrightnessRange { min, max } => {
write!(
f,
"brightness range must be ordered and non-zero, got {min}..={max}"
)
}
Self::ZeroQueueCapacity => f.write_str("queue capacity must not be zero"),
}
}
}
impl std::error::Error for ConfigError {}
impl std::fmt::Display for VfdError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Config(error) => error.fmt(f),
Self::Serial(error) => error.fmt(f),
Self::Io(error) => error.fmt(f),
Self::InvalidCoordinate {
x,
y,
columns,
rows,
} => write!(
f,
"coordinate ({x}, {y}) is outside display geometry {columns}x{rows}"
),
Self::InvalidLine { line, rows } => {
write!(f, "line {line} is outside display rows 1..={rows}")
}
Self::UnsupportedBrightness { level, min, max } => {
write!(
f,
"brightness {level} is outside supported range {min}..={max}"
)
}
Self::TextTooLong { max } => {
write!(f, "marquee text exceeds the limit of {max} characters")
}
Self::RawPayloadTooLarge { length, max } => {
write!(f, "raw payload is {length} bytes, maximum is {max}")
}
Self::InvalidMarqueeSpeed { cps, max } => {
write!(f, "marquee speed {cps} exceeds maximum {max}")
}
Self::QueueClosed => f.write_str("VFD worker queue is closed"),
Self::WorkerStopped => f.write_str("VFD worker stopped before acknowledging command"),
Self::WorkerPanicked => f.write_str("VFD worker thread panicked"),
#[cfg(feature = "tokio")]
Self::WorkerCancelled => f.write_str("VFD async worker task was cancelled"),
}
}
}
impl std::error::Error for VfdError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Config(error) => Some(error),
Self::Serial(error) => Some(error),
Self::Io(error) => Some(error),
_ => None,
}
}
}
impl From<ConfigError> for VfdError {
fn from(value: ConfigError) -> Self {
Self::Config(value)
}
}
impl From<serialport::Error> for VfdError {
fn from(value: serialport::Error) -> Self {
Self::Serial(value)
}
}
impl From<std::io::Error> for VfdError {
fn from(value: std::io::Error) -> Self {
Self::Io(value)
}
}
impl<T> From<mpsc::SendError<T>> for VfdError {
fn from(_: mpsc::SendError<T>) -> Self {
Self::QueueClosed
}
}
impl From<mpsc::RecvError> for VfdError {
fn from(_: mpsc::RecvError) -> Self {
Self::WorkerStopped
}
}
pub type Result<T> = std::result::Result<T, VfdError>;