use std::fmt;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("invalid configuration: {0}")]
Config(String),
#[error("inconsistent shapes: {0}")]
Shape(String),
#[error("numerical failure: {0}")]
Numerical(String),
#[error("condition: {0}")]
Condition(String),
#[error("input/output: {0}")]
Io(#[from] std::io::Error),
}
pub type Result<T> = std::result::Result<T, Error>;
impl Error {
#[must_use]
pub fn context(self, context: impl fmt::Display) -> Error {
match self {
Error::Config(m) => Error::Config(format!("{context}: {m}")),
Error::Shape(m) => Error::Shape(format!("{context}: {m}")),
Error::Numerical(m) => Error::Numerical(format!("{context}: {m}")),
Error::Condition(m) => Error::Condition(format!("{context}: {m}")),
Error::Io(e) => Error::Io(std::io::Error::new(e.kind(), format!("{context}: {e}"))),
}
}
}
pub fn ensure(ok: bool, msg: impl FnOnce() -> String) -> Result<()> {
if ok { Ok(()) } else { Err(Error::Config(msg())) }
}
pub fn ensure_shape(ok: bool, msg: impl FnOnce() -> String) -> Result<()> {
if ok { Ok(()) } else { Err(Error::Shape(msg())) }
}
pub fn expect_len(got: usize, want: usize, what: &str) -> Result<()> {
if got == want { Ok(()) } else { Err(Error::Shape(format!("{got} {what} for a block of {want}"))) }
}
pub fn same_len(a: usize, b: usize, what: &str) -> Result<()> {
if a == b { Ok(()) } else { Err(Error::Shape(format!("{what}: lengths {a} and {b} differ"))) }
}