use std::io;
use thiserror::Error;
pub type StorageResult<T> = Result<T, StorageError>;
#[derive(Error, Debug)]
pub enum StorageError {
#[error("object not found: {0}")]
NotFound(String),
#[error("permission denied: {0}")]
PermissionDenied(String),
#[error("I/O error: {0}")]
Io(#[from] io::Error),
#[error("invalid key: {0}")]
InvalidKey(String),
#[error("storage backend error: {0}")]
Backend(String),
#[error("operation timed out: {0}")]
Timeout(String),
#[error(transparent)]
Other(#[from] anyhow::Error),
}
impl StorageError {
pub fn not_found<S: Into<String>>(key: S) -> Self {
StorageError::NotFound(key.into())
}
pub fn permission_denied<S: Into<String>>(msg: S) -> Self {
StorageError::PermissionDenied(msg.into())
}
pub fn invalid_key<S: Into<String>>(msg: S) -> Self {
StorageError::InvalidKey(msg.into())
}
pub fn backend<S: Into<String>>(msg: S) -> Self {
StorageError::Backend(msg.into())
}
pub fn timeout<S: Into<String>>(msg: S) -> Self {
StorageError::Timeout(msg.into())
}
pub fn other<E: Into<anyhow::Error>>(error: E) -> Self {
StorageError::Other(error.into())
}
pub fn is_not_found(&self) -> bool {
matches!(self, StorageError::NotFound(_))
}
pub fn is_permission_denied(&self) -> bool {
matches!(self, StorageError::PermissionDenied(_))
}
pub fn is_invalid_key(&self) -> bool {
matches!(self, StorageError::InvalidKey(_))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_creation() {
let err = StorageError::not_found("test_key");
assert!(err.is_not_found());
assert_eq!(err.to_string(), "object not found: test_key");
}
#[test]
fn test_permission_denied_error() {
let err = StorageError::permission_denied("bucket locked");
assert!(err.is_permission_denied());
}
#[test]
fn test_invalid_key_error() {
let err = StorageError::invalid_key("empty key");
assert!(err.is_invalid_key());
}
#[test]
fn test_io_error_conversion() {
let io_err = io::Error::other("read failed");
let storage_err = StorageError::from(io_err);
assert!(matches!(storage_err, StorageError::Io(_)));
}
}