use thiserror::Error;
#[derive(Debug, Error)]
pub enum FetchError {
#[error("Missing required parameter: url")]
MissingUrl,
#[error("Invalid URL: must start with http:// or https://")]
InvalidUrlScheme,
#[error("Invalid method: must be GET or HEAD")]
InvalidMethod,
#[error("Blocked URL: not allowed by policy")]
BlockedUrl,
#[error("Failed to create HTTP client")]
ClientBuildError(#[source] reqwest::Error),
#[error("Request timed out: server did not respond within 1 second")]
FirstByteTimeout,
#[error("Failed to connect to server")]
ConnectError(#[source] reqwest::Error),
#[error("Request failed: {0}")]
RequestError(String),
#[error("Fetcher error: {0}")]
FetcherError(String),
#[error("Failed to save file: {0}")]
SaveError(String),
#[error("File saving not available")]
SaverNotAvailable,
}
impl FetchError {
pub fn from_reqwest(err: reqwest::Error) -> Self {
if err.is_timeout() {
FetchError::FirstByteTimeout
} else if err.is_connect() {
FetchError::ConnectError(err)
} else if err.is_redirect() {
FetchError::RequestError("redirect error".to_string())
} else if err.is_body() {
FetchError::RequestError("error reading response body".to_string())
} else if err.is_decode() {
FetchError::RequestError("error decoding response".to_string())
} else {
FetchError::RequestError("request failed".to_string())
}
}
}
#[derive(Debug, Error)]
pub enum ToolError {
#[error("{0}")]
UserFacing(String),
#[error("{0}")]
Internal(String),
}
impl ToolError {
pub fn is_user_facing(&self) -> bool {
matches!(self, Self::UserFacing(_))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_messages() {
assert_eq!(
FetchError::MissingUrl.to_string(),
"Missing required parameter: url"
);
assert_eq!(
FetchError::InvalidUrlScheme.to_string(),
"Invalid URL: must start with http:// or https://"
);
assert_eq!(
FetchError::InvalidMethod.to_string(),
"Invalid method: must be GET or HEAD"
);
assert_eq!(
FetchError::BlockedUrl.to_string(),
"Blocked URL: not allowed by policy"
);
assert_eq!(
FetchError::FirstByteTimeout.to_string(),
"Request timed out: server did not respond within 1 second"
);
}
#[test]
fn test_tool_error_classification() {
assert!(ToolError::UserFacing("url is required".to_string()).is_user_facing());
assert!(!ToolError::Internal("serde failure".to_string()).is_user_facing());
}
}