use std::fmt; use std::io; use std::path::PathBuf; use reqwest::Error as ReqwestError; use serde_json::Error as JsonError; pub type AppResult = Result; #[derive(Debug)] pub enum AppError { FileNotFound(PathBuf), IoError(io::Error), ConfigParseError { source: JsonError, path: PathBuf }, RequestFailed { url: String, source: ReqwestError }, InvalidResponse { url: String, reason: String }, Timeout { url: String }, } impl AppError { pub fn with_path(self, path: impl Into) -> Self { match self { Self::ConfigParseError { source, .. } => Self::ConfigParseError { source, path: path.into(), }, err => err, } } pub fn with_url(self, url: impl Into) -> Self { match self { Self::RequestFailed { source, .. } => Self::RequestFailed { source, url: url.into(), }, Self::InvalidResponse { reason, .. } => Self::InvalidResponse { reason, url: url.into(), }, Self::Timeout { .. } => Self::Timeout { url: url.into(), }, err => err, } } } impl fmt::Display for AppError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::FileNotFound(path) => write!(f, "File not found: {}", path.display()), Self::IoError(err) => write!(f, "I/O error: {}", err), Self::ConfigParseError { path, .. } => write!(f, "Failed to parse config at {}", path.display()), Self::RequestFailed { url, .. } => write!(f, "Request to {} failed", url), Self::InvalidResponse { url, reason } => { write!(f, "Invalid response from {}: {}", url, reason) } Self::Timeout { url } => write!(f, "Request to {} timed out", url), } } } impl std::error::Error for AppError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { Self::IoError(err) => Some(err), Self::ConfigParseError { source, .. } => Some(source), Self::RequestFailed { source, .. } => Some(source), _ => None, } } } // Convenient conversions from library errors impl From for AppError { fn from(err: io::Error) -> Self { Self::IoError(err) } } impl From for AppError { fn from(err: ReqwestError) -> Self { Self::RequestFailed { url: err.url().map_or_else(|| "unknown".to_string(), |u| u.to_string()), source: err, } } } impl From for AppError { fn from(err: JsonError) -> Self { Self::ConfigParseError { source: err, path: PathBuf::from("unknown"), // Default path } } }