use std::error::Error as StdError;
use thiserror::Error;
pub type Result<T> = std::result::Result<T, Error>;
#[non_exhaustive]
#[derive(Debug, Error)]
pub enum Error {
#[error("io error: {0}")]
Io(#[from] std::io::Error),
#[error("json error: {0}")]
Json(#[from] serde_json::Error),
#[error("storage error: {message}")]
Storage {
message: String,
#[source]
source: Option<Box<dyn StdError + Send + Sync>>,
},
#[error("config error: {0}")]
Config(String),
#[error("{0}")]
Internal(String),
#[error("persist queue closed")]
PersistQueueClosed,
}
impl Error {
pub fn storage(message: impl Into<String>) -> Self {
Self::Storage {
message: message.into(),
source: None,
}
}
pub fn storage_source(
message: impl Into<String>,
source: impl StdError + Send + Sync + 'static,
) -> Self {
Self::Storage {
message: message.into(),
source: Some(Box::new(source)),
}
}
pub fn config(message: impl Into<String>) -> Self {
Self::Config(message.into())
}
pub fn internal(message: impl Into<String>) -> Self {
Self::Internal(message.into())
}
}
#[cfg(test)]
mod tests {
use std::error::Error as StdError;
use super::Error;
#[test]
fn storage_source_preserves_chain() {
let err = Error::storage_source("sqlite open failed", std::io::Error::other("disk full"));
match &err {
Error::Storage {
message,
source: Some(_),
} => assert!(message.contains("sqlite")),
_ => panic!("expected Storage with source"),
}
assert!(err.source().is_some());
}
#[test]
fn config_display() {
let err = Error::config("metrics_backend is required");
assert_eq!(err.to_string(), "config error: metrics_backend is required");
}
}