use donglora_protocol::ErrorCode;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ClientError {
#[error("device is not configured (ENOTCONFIGURED)")]
NotConfigured,
#[error("firmware TX queue is full (EBUSY)")]
Busy,
#[error("parameter out of range (EPARAM)")]
Param,
#[error("payload length wrong (ELENGTH)")]
Length,
#[error("modulation not supported (EMODULATION)")]
Modulation,
#[error("unknown command (EUNKNOWN_CMD)")]
UnknownCmd,
#[error("radio hardware error (ERADIO)")]
Radio,
#[error("framing error (EFRAME)")]
Frame,
#[error("firmware internal error (EINTERNAL)")]
Internal,
#[error("unknown error code 0x{0:04X}")]
UnknownCode(u16),
#[error("channel busy — CAD detected activity")]
ChannelBusy,
#[error("TX cancelled before airtime")]
Cancelled,
#[error("timed out waiting for {what}")]
Timeout { what: &'static str },
#[error("transport closed: {0}")]
TransportClosed(String),
#[error("session reader exited")]
ReaderExited,
#[error("inbound frame corrupted: {0}")]
BadFrame(String),
#[error("frame encode failed: {0}")]
EncodeFailed(String),
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("config not supported by device: {reason}")]
ConfigNotSupported { reason: String },
#[error("{0}")]
Other(String),
}
impl ClientError {
#[must_use]
pub fn is_retryable(&self) -> bool {
matches!(self, Self::ChannelBusy | Self::Busy)
}
#[must_use]
pub fn from_wire(code: ErrorCode) -> Self {
match code {
ErrorCode::ENotConfigured => Self::NotConfigured,
ErrorCode::EBusy => Self::Busy,
ErrorCode::EParam => Self::Param,
ErrorCode::ELength => Self::Length,
ErrorCode::EModulation => Self::Modulation,
ErrorCode::EUnknownCmd => Self::UnknownCmd,
ErrorCode::ERadio => Self::Radio,
ErrorCode::EFrame => Self::Frame,
ErrorCode::EInternal => Self::Internal,
ErrorCode::Unknown(raw) => Self::UnknownCode(raw),
}
}
}
pub type ClientResult<T> = Result<T, ClientError>;