use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum Error {
NotFound(String),
AlreadyExists(String),
InvalidArgument(String),
Conflict(String),
AlreadyCommitted(String),
AlreadyAborted(String),
InvalidTransaction(String),
Capacity(String),
Internal(String),
Unsupported(String),
}
impl Error {
pub fn not_found(message: impl Into<String>) -> Self {
Self::NotFound(message.into())
}
pub fn already_exists(message: impl Into<String>) -> Self {
Self::AlreadyExists(message.into())
}
pub fn invalid_argument(message: impl Into<String>) -> Self {
Self::InvalidArgument(message.into())
}
pub fn conflict(message: impl Into<String>) -> Self {
Self::Conflict(message.into())
}
pub fn already_committed(message: impl Into<String>) -> Self {
Self::AlreadyCommitted(message.into())
}
pub fn already_aborted(message: impl Into<String>) -> Self {
Self::AlreadyAborted(message.into())
}
pub fn invalid_transaction(message: impl Into<String>) -> Self {
Self::InvalidTransaction(message.into())
}
pub fn capacity(message: impl Into<String>) -> Self {
Self::Capacity(message.into())
}
pub fn internal(message: impl Into<String>) -> Self {
Self::Internal(message.into())
}
pub fn unsupported(message: impl Into<String>) -> Self {
Self::Unsupported(message.into())
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NotFound(message) => write!(f, "not found: {message}"),
Self::AlreadyExists(message) => write!(f, "already exists: {message}"),
Self::InvalidArgument(message) => write!(f, "invalid argument: {message}"),
Self::Conflict(message) => write!(f, "conflict: {message}"),
Self::AlreadyCommitted(message) => write!(f, "already committed: {message}"),
Self::AlreadyAborted(message) => write!(f, "already aborted: {message}"),
Self::InvalidTransaction(message) => write!(f, "invalid transaction: {message}"),
Self::Capacity(message) => write!(f, "capacity exceeded: {message}"),
Self::Internal(message) => write!(f, "internal error: {message}"),
Self::Unsupported(message) => write!(f, "unsupported: {message}"),
}
}
}
impl std::error::Error for Error {}
impl From<std::io::Error> for Error {
fn from(error: std::io::Error) -> Self {
Self::Internal(format!("io error: {error}"))
}
}
pub type Result<T> = std::result::Result<T, Error>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn display_includes_variant_context() {
assert_eq!(
Error::not_found("player row").to_string(),
"not found: player row"
);
assert_eq!(
Error::conflict("version mismatch on row 7").to_string(),
"conflict: version mismatch on row 7"
);
assert_eq!(
Error::already_exists("table players").to_string(),
"already exists: table players"
);
}
#[test]
fn helper_constructors_build_correct_variants() {
assert!(matches!(Error::not_found("x"), Error::NotFound(_)));
assert!(matches!(
Error::already_exists("x"),
Error::AlreadyExists(_)
));
assert!(matches!(
Error::invalid_argument("x"),
Error::InvalidArgument(_)
));
assert!(matches!(Error::conflict("x"), Error::Conflict(_)));
assert!(matches!(
Error::already_committed("x"),
Error::AlreadyCommitted(_)
));
assert!(matches!(
Error::already_aborted("x"),
Error::AlreadyAborted(_)
));
assert!(matches!(
Error::invalid_transaction("x"),
Error::InvalidTransaction(_)
));
assert!(matches!(Error::capacity("x"), Error::Capacity(_)));
assert!(matches!(Error::internal("x"), Error::Internal(_)));
assert!(matches!(Error::unsupported("x"), Error::Unsupported(_)));
}
#[test]
fn result_alias_carries_error() {
fn fail() -> Result<u64> {
Err(Error::conflict("retry"))
}
let outcome: Result<u64> = fail();
assert_eq!(outcome, Err(Error::Conflict("retry".to_string())));
assert!(outcome.is_err());
}
}