use std::path::PathBuf;
use std::time::SystemTime;
#[derive(Debug, Clone)]
pub struct ErrorContext {
pub operation: String,
pub file_path: Option<PathBuf>,
pub position: Option<u64>,
pub chunk_size: Option<usize>,
pub timestamp: SystemTime,
pub custom_context: Option<String>,
}
impl ErrorContext {
pub fn new(operation: &str) -> Self {
Self {
operation: operation.to_string(),
file_path: None,
position: None,
chunk_size: None,
timestamp: SystemTime::now(),
custom_context: None,
}
}
pub fn with_file_path<P: Into<PathBuf>>(mut self, path: P) -> Self {
self.file_path = Some(path.into());
self
}
pub fn with_position(mut self, position: u64) -> Self {
self.position = Some(position);
self
}
pub fn with_chunk_size(mut self, chunk_size: usize) -> Self {
self.chunk_size = Some(chunk_size);
self
}
pub fn with_custom_context<S: Into<String>>(mut self, context: S) -> Self {
self.custom_context = Some(context.into());
self
}
}
#[derive(Debug, Clone)]
pub struct Suggestion {
pub action: String,
pub reason: String,
pub code: Option<String>,
pub documentation_url: Option<String>,
}
impl Suggestion {
pub fn new(action: &str, reason: &str) -> Self {
Self {
action: action.to_string(),
reason: reason.to_string(),
code: None,
documentation_url: None,
}
}
pub fn with_code<S: Into<String>>(mut self, code: S) -> Self {
self.code = Some(code.into());
self
}
pub fn with_documentation<S: Into<String>>(mut self, url: S) -> Self {
self.documentation_url = Some(url.into());
self
}
}
pub trait ErrorExt<T> {
fn with_context(self, context: ErrorContext) -> EnhancedError<T>;
fn with_suggestion(self, suggestion: Suggestion) -> EnhancedError<T>;
}
#[derive(Debug)]
pub struct EnhancedError<T> {
pub original: T,
pub context: Option<ErrorContext>,
pub suggestion: Option<Suggestion>,
}
impl<T> EnhancedError<T> {
pub fn new(original: T) -> Self {
Self {
original,
context: None,
suggestion: None,
}
}
pub fn new_with_context(original: T, context: ErrorContext) -> Self {
Self {
original,
context: Some(context),
suggestion: None,
}
}
pub fn new_with_suggestion(original: T, suggestion: Suggestion) -> Self {
Self {
original,
context: None,
suggestion: Some(suggestion),
}
}
}
impl ErrorExt<std::io::Error> for std::io::Error {
fn with_context(self, context: ErrorContext) -> EnhancedError<std::io::Error> {
EnhancedError::new_with_context(self, context)
}
fn with_suggestion(self, suggestion: Suggestion) -> EnhancedError<std::io::Error> {
EnhancedError::new_with_suggestion(self, suggestion)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_context_builder() {
let context = ErrorContext::new("test_operation")
.with_file_path("/test/file.txt")
.with_position(1024)
.with_chunk_size(4096);
assert_eq!(context.operation, "test_operation");
assert_eq!(context.file_path, Some(PathBuf::from("/test/file.txt")));
assert_eq!(context.position, Some(1024));
assert_eq!(context.chunk_size, Some(4096));
}
#[test]
fn test_suggestion_builder() {
let suggestion = Suggestion::new("Check permissions", "Directory not writable")
.with_code("ERR_PERMISSIONS")
.with_documentation("https://docs.example.com/permissions");
assert_eq!(suggestion.action, "Check permissions");
assert_eq!(suggestion.reason, "Directory not writable");
assert_eq!(suggestion.code, Some("ERR_PERMISSIONS".to_string()));
assert_eq!(
suggestion.documentation_url,
Some("https://docs.example.com/permissions".to_string())
);
}
#[test]
fn test_enhanced_error() {
let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
let context = ErrorContext::new("read_file").with_position(100);
let enhanced = EnhancedError::new_with_context(io_error, context);
assert!(enhanced.context.is_some());
assert_eq!(enhanced.context.unwrap().position, Some(100));
}
}