use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub enum Error {
File(String),
Version(String),
Schema(String),
Validation {
message: String,
code: Option<String>,
},
Integrity(String),
Key(String),
Value(String),
Type(String),
Index(String),
Io(String),
NotImplemented(String),
Runtime(String),
}
pub type Result<T, E = Error> = std::result::Result<T, E>;
impl Error {
pub fn coded(code: &str, message: impl Into<String>) -> Self {
Error::Validation { message: message.into(), code: Some(code.to_string()) }
}
pub fn invalid(message: impl Into<String>) -> Self {
Error::Validation { message: message.into(), code: None }
}
pub fn code(&self) -> Option<&str> {
match self {
Error::Validation { code, .. } => code.as_deref(),
_ => None,
}
}
pub fn message(&self) -> &str {
match self {
Error::File(m)
| Error::Version(m)
| Error::Schema(m)
| Error::Integrity(m)
| Error::Key(m)
| Error::Value(m)
| Error::Type(m)
| Error::Index(m)
| Error::Io(m)
| Error::NotImplemented(m)
| Error::Runtime(m) => m,
Error::Validation { message, .. } => message,
}
}
pub fn kind_name(&self) -> &'static str {
match self {
Error::File(_) => "MEDH5FileError",
Error::Version(_) => "MEDH5VersionError",
Error::Schema(_) => "MEDH5SchemaError",
Error::Validation { .. } => "MEDH5ValidationError",
Error::Integrity(_) => "MEDH5IntegrityError",
Error::Key(_) => "KeyError",
Error::Value(_) => "ValueError",
Error::Type(_) => "TypeError",
Error::Index(_) => "IndexError",
Error::Io(_) => "OSError",
Error::NotImplemented(_) => "NotImplementedError",
Error::Runtime(_) => "RuntimeError",
}
}
pub fn python_str(&self) -> String {
match self {
Error::Key(m) => crate::json::repr_str(m),
other => other.to_string(),
}
}
pub fn python_line(&self) -> String {
format!("{}: {}", self.kind_name(), self.python_str())
}
pub fn is_medh5(&self) -> bool {
matches!(
self,
Error::File(_) | Error::Version(_) | Error::Schema(_) | Error::Validation { .. } | Error::Integrity(_)
)
}
pub fn is_lookup(&self) -> bool {
matches!(self, Error::Key(_) | Error::Index(_))
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Validation { message, code: Some(code) } => write!(f, "[{code}] {message}"),
other => f.write_str(other.message()),
}
}
}
impl std::error::Error for Error {}
impl From<hdf5::Error> for Error {
fn from(err: hdf5::Error) -> Self {
Error::Io(err.to_string())
}
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
Error::Io(err.to_string())
}
}
impl From<serde_json::Error> for Error {
fn from(err: serde_json::Error) -> Self {
Error::Value(err.to_string())
}
}
impl From<ndarray::ShapeError> for Error {
fn from(err: ndarray::ShapeError) -> Self {
Error::Value(err.to_string())
}
}
pub fn os_error(err: &std::io::Error, path: impl AsRef<std::path::Path>) -> Error {
match err.raw_os_error() {
Some(code) => {
let full = err.to_string();
let reason = full.strip_suffix(&format!(" (os error {code})")).unwrap_or(&full);
let shown = path.as_ref().to_string_lossy();
Error::Io(format!("[Errno {code}] {reason}: {}", crate::json::repr_str(&shown)))
}
None => Error::Io(err.to_string()),
}
}
#[macro_export]
macro_rules! bail_coded {
($code:expr, $($arg:tt)*) => {
return Err($crate::Error::coded($code, format!($($arg)*)))
};
}
#[macro_export]
macro_rules! bail_invalid {
($($arg:tt)*) => {
return Err($crate::Error::invalid(format!($($arg)*)))
};
}