use crate::c_abi;
use crate::FileMode;
use crate::Frame;
use std::error::Error as StdError;
use std::path::{Path, PathBuf};
#[derive(Debug, Clone, PartialEq)]
pub enum Error {
CApiError { code: ErrorCode, task: ErrorTask },
WrongSizeFrame { expected: usize, found: usize },
CouldNotOpen { path: PathBuf, mode: FileMode },
InvalidOsStr(Option<std::ffi::NulError>),
CouldNotCheckNAtoms(Box<Error>),
OutOfRange {
name: &'static str,
task: ErrorTask,
value: String,
target: &'static str,
},
}
impl Error {
pub fn code(&self) -> Option<ErrorCode> {
if let Error::CApiError { code, .. } = self {
Some(*code)
} else if let Some(e) = self.source() {
e.downcast_ref::<Self>().and_then(Self::code)
} else {
None
}
}
pub fn task(&self) -> Option<ErrorTask> {
if let Error::CApiError { task, .. } = self {
Some(*task)
} else if let Some(e) = self.source() {
e.downcast_ref::<Self>().and_then(Self::task)
} else {
None
}
}
pub fn is_eof(&self) -> bool {
self.code().map_or(false, |e| e.is_eof())
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match &self {
Error::InvalidOsStr(err) => {
if let Some(err) = err {
Some(err)
} else {
None
}
},
Error::CouldNotCheckNAtoms(err) => Some(err.as_ref()),
_ => None,
}
}
}
impl From<(ErrorCode, ErrorTask)> for Error {
fn from(value: (ErrorCode, ErrorTask)) -> Self {
let (code, task) = value;
Self::CApiError { code, task }
}
}
impl From<(&Path, FileMode)> for Error {
fn from(value: (&Path, FileMode)) -> Self {
let (path, mode) = value;
Error::CouldNotOpen {
path: path.to_owned(),
mode,
}
}
}
impl From<(&Frame, usize)> for Error {
fn from(value: (&Frame, usize)) -> Self {
let (frame, num_atoms) = value;
Error::WrongSizeFrame {
expected: num_atoms,
found: frame.coords.len(),
}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::CApiError { code, task } => write!(
f,
"Error while {task}: C API returned error code {code}",
task = task,
code = code
),
Error::WrongSizeFrame { expected, found } => write!(
f,
"Expected frame of size {:?}, found {:?}",
expected, found
),
Error::CouldNotOpen { path, mode } => {
write!(f, "Could not open file at {:?} in mode {:?}", path, mode)
}
Error::InvalidOsStr(_) => write!(f, "Cannot convert path to CString."),
Error::CouldNotCheckNAtoms(_) => {
write!(f, "Failed to read number of atoms in trajectory file")
}
Error::OutOfRange {
name,
task,
value,
target,
} => write!(
f,
"Illegal {name} while {task}: Failed to cast {value} to {target}",
name = name,
task = task,
value = value,
target = target
),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ErrorTask {
ReadNumAtoms,
Read,
Write,
Flush,
Seek,
}
impl std::fmt::Display for ErrorTask {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self {
ErrorTask::ReadNumAtoms => write!(f, "reading atom number from trajectory"),
ErrorTask::Read => write!(f, "reading trajectory"),
ErrorTask::Write => write!(f, "writing trajectory"),
ErrorTask::Flush => write!(f, "flushing trajectory"),
ErrorTask::Seek => write!(f, "seeking in trajectory"),
}
}
}
#[derive(Debug, Clone, PartialEq, Copy)]
pub enum ErrorCode {
ExdrOk,
ExdrHeader,
ExdrString,
ExdrDouble,
ExdrInt,
ExdrFloat,
ExdrUint,
Exdr3dx,
ExdrClose,
ExdrMagic,
ExdrNoMem,
ExdrEndOfFile,
ExdrFileNotFound,
ExdrNr,
UnmatchedCode(i32),
}
impl ErrorCode {
pub fn is_eof(&self) -> bool {
matches!(self, Self::ExdrEndOfFile)
}
}
impl From<i32> for ErrorCode {
fn from(code: i32) -> Self {
match code {
c_abi::xdrfile::exdrOK => Self::ExdrOk,
c_abi::xdrfile::exdrHEADER => Self::ExdrHeader,
c_abi::xdrfile::exdrSTRING => Self::ExdrString,
c_abi::xdrfile::exdrDOUBLE => Self::ExdrDouble,
c_abi::xdrfile::exdrINT => Self::ExdrInt,
c_abi::xdrfile::exdrFLOAT => Self::ExdrFloat,
c_abi::xdrfile::exdrUINT => Self::ExdrUint,
c_abi::xdrfile::exdr3DX => Self::Exdr3dx,
c_abi::xdrfile::exdrCLOSE => Self::ExdrClose,
c_abi::xdrfile::exdrMAGIC => Self::ExdrMagic,
c_abi::xdrfile::exdrNOMEM => Self::ExdrNoMem,
c_abi::xdrfile::exdrENDOFFILE => Self::ExdrEndOfFile,
c_abi::xdrfile::exdrFILENOTFOUND => Self::ExdrFileNotFound,
c_abi::xdrfile::exdrNR => Self::ExdrNr,
code => Self::UnmatchedCode(code),
}
}
}
impl std::fmt::Display for ErrorCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Self::UnmatchedCode(i) = self {
write!(f, "{}", i)
} else {
write!(f, "{:?}", self)
}
}
}
pub type Result<T, E = Error> = std::result::Result<T, E>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_eof() {
let error = Error::CApiError {
code: c_abi::xdrfile::exdrENDOFFILE.into(),
task: ErrorTask::Read,
};
assert!(error.is_eof());
let error = Error::CApiError {
code: ErrorCode::ExdrEndOfFile,
task: ErrorTask::Read,
};
assert!(error.is_eof());
let error = Error::CApiError {
code: (c_abi::xdrfile::exdrENDOFFILE + 1).into(),
task: ErrorTask::Read,
};
assert!(!error.is_eof());
let error = Error::CApiError {
code: 0.into(),
task: ErrorTask::Read,
};
assert!(!error.is_eof());
let error = Error::CApiError {
code: 255.into(),
task: ErrorTask::Read,
};
assert!(!error.is_eof());
let error = Error::CouldNotOpen {
path: PathBuf::from("not/a/file"),
mode: FileMode::Read,
};
assert!(!error.is_eof());
}
#[test]
fn test_from_correct_type() {
let code = 3.into();
let task = ErrorTask::Read;
let expected = Error::CApiError { code, task };
let err = Error::from((code, task));
assert_eq!(expected, err);
let path = Path::new(".");
let mode = FileMode::Read;
let expected = Error::CouldNotOpen {
path: path.to_path_buf(),
mode: mode.to_owned(),
};
let err = Error::from((path, mode));
assert_eq!(expected, err);
let frame = Frame::with_len(0);
let expected = Error::WrongSizeFrame {
expected: 10,
found: 0,
};
let err = Error::from((&frame, 10));
assert_eq!(expected, err);
}
}