use std::io;
use std::path::PathBuf;
use thiserror::Error as ThisError;
#[derive(ThisError, Debug)]
pub enum Error {
#[error("I/O error: {0}")]
Io(#[from] io::Error),
#[error("File not found: {path}")]
FileNotFound { path: PathBuf },
#[error("Invalid file path: {reason}")]
InvalidPath { reason: String },
#[error("Path traversal detected: {path}")]
PathTraversalAttempt { path: PathBuf },
#[error("File too large ({size} bytes, max {max} bytes): {path}")]
FileTooLarge { path: PathBuf, size: u64, max: u64 },
#[error("Parse error: {reason}")]
ParseError { reason: String },
#[error("Configuration error: {reason}")]
ConfigError { reason: String },
#[error("Validation error: {reason}")]
ValidationError { reason: String },
#[error("Concurrent access conflict: {reason}")]
ConcurrencyError { reason: String },
#[error("Not found in graph: {key}")]
NotFound { key: String },
#[error("Error: {0}")]
Other(String),
#[error("Wrapped error: {0}")]
Wrapped(Box<dyn std::error::Error + Send + Sync>),
}
pub type Result<T> = std::result::Result<T, Error>;
impl Error {
pub fn io(err: io::Error) -> Self {
Error::Io(err)
}
pub fn file_not_found(path: impl Into<PathBuf>) -> Self {
Error::FileNotFound { path: path.into() }
}
pub fn invalid_path(reason: impl Into<String>) -> Self {
Error::InvalidPath {
reason: reason.into(),
}
}
pub fn path_traversal(path: impl Into<PathBuf>) -> Self {
Error::PathTraversalAttempt { path: path.into() }
}
pub fn file_too_large(path: impl Into<PathBuf>, size: u64, max: u64) -> Self {
Error::FileTooLarge {
path: path.into(),
size,
max,
}
}
pub fn parse_error(reason: impl Into<String>) -> Self {
Error::ParseError {
reason: reason.into(),
}
}
pub fn config_error(reason: impl Into<String>) -> Self {
Error::ConfigError {
reason: reason.into(),
}
}
pub fn validation_error(reason: impl Into<String>) -> Self {
Error::ValidationError {
reason: reason.into(),
}
}
pub fn concurrency_error(reason: impl Into<String>) -> Self {
Error::ConcurrencyError {
reason: reason.into(),
}
}
pub fn not_found(key: impl Into<String>) -> Self {
Error::NotFound { key: key.into() }
}
pub fn other(msg: impl Into<String>) -> Self {
Error::Other(msg.into())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_creation() {
let err = Error::file_not_found("/path/to/file");
assert!(err.to_string().contains("File not found"));
let err = Error::invalid_path("contains .. traversal");
assert!(err.to_string().contains("Invalid file path"));
}
}