use crate::CbmStatus;
use libc::{EINVAL, EIO, ENODEV, ETIMEDOUT};
use serde::{Deserialize, Serialize};
use thiserror::Error;
use xum1541::DeviceChannel;
use xum1541::Error as Xum1541Error;
#[derive(Debug, Error, PartialEq, Serialize, Deserialize)]
pub enum Error {
#[error("{0}")]
Xum1541(#[from] Xum1541Error),
#[error("Device {device} error: {error}")]
Device { device: u8, error: DeviceError },
#[error("Device {}: File error: {message}", device)]
File { device: u8, message: String },
#[error("Device {}: Status error: {status}", status.device)]
Status { status: CbmStatus },
#[error("Timeout error, duration: {dur:?}")]
Timeout { dur: std::time::Duration },
#[error("Validation error: {message}")]
Validation { message: String },
#[error("Parse error: {message}")]
Parse { message: String },
}
#[derive(Error, Debug, PartialEq, Clone, Serialize, Deserialize)]
pub enum DeviceError {
#[error("Failed to get status: {message}")]
GetStatusFailure { message: String },
#[error("Invalid drive number: {drive_num}")]
InvalidDrive { drive_num: u8 },
#[error("Read error: Channel: {channel}, Error: {message}")]
Read { channel: u8, message: String },
#[error("Write error: Channel: {channel}, Error: {message}")]
Write { channel: u8, message: String },
#[error("Device does not exist (or at least isn't talking on channel 15)")]
NoDevice,
}
impl From<CbmStatus> for Error {
fn from(status: CbmStatus) -> Self {
Error::Status { status }
}
}
impl Error {
pub fn to_errno(&self) -> i32 {
match self {
xum @ Error::Xum1541(_) => xum.to_errno(),
e @ Error::Device { .. } => e.to_errno(),
Error::File { .. } => EIO,
Error::Timeout { .. } => ETIMEDOUT,
Error::Validation { .. } => EINVAL,
Error::Status { .. } => EIO,
Error::Parse { message: _ } => EINVAL,
}
}
}
impl DeviceError {
pub fn to_errno(&self) -> i32 {
match self {
DeviceError::GetStatusFailure { .. } => EIO,
DeviceError::InvalidDrive { .. } => EINVAL,
DeviceError::Read { .. } => EIO,
DeviceError::Write { .. } => EIO,
DeviceError::NoDevice { .. } => ENODEV,
}
}
fn with_device(&self, device: u8) -> Error {
Error::Device {
device,
error: self.clone(),
}
}
pub fn invalid_drive_num(device: u8, drive_num: u8) -> Error {
DeviceError::InvalidDrive { drive_num }.with_device(device)
}
pub fn read_error(dc: DeviceChannel, message: String) -> Error {
DeviceError::Read {
channel: dc.channel(),
message,
}
.with_device(dc.device())
}
pub fn write_error(dc: DeviceChannel, message: String) -> Error {
DeviceError::Write {
channel: dc.channel(),
message,
}
.with_device(dc.device())
}
pub fn get_status_failure(device: u8, message: String) -> Error {
DeviceError::GetStatusFailure { message }.with_device(device)
}
pub fn no_device(device: u8) -> Error {
DeviceError::NoDevice.with_device(device)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_errno() {
let error = Error::Validation {
message: "Test error".to_string(),
};
assert_eq!(error.to_errno(), EINVAL);
}
}