use thiserror::Error;
#[derive(Error, Debug, Clone)]
pub enum FetchError {
#[error("invalid URL: {0}")]
InvalidUrl(String),
#[error("blocked URL scheme: {0} (only http/https allowed)")]
UnsupportedScheme(String),
#[error("private network access blocked: {0}")]
PrivateNetworkBlocked(String),
#[error("redirect limit exceeded: {0} redirects followed")]
RedirectLimitExceeded(usize),
#[error("redirect target blocked: {0}")]
RedirectTargetBlocked(String),
#[error("invalid redirect location: {0}")]
InvalidRedirectLocation(String),
#[error("embedded credentials blocked: {0}")]
EmbeddedCredentialsBlocked(String),
#[error("URL too long: {0} bytes (max {1})")]
UrlTooLong(usize, usize),
#[error("timeout after {0}ms")]
Timeout(u64),
#[error("HTTP error: {0} {1}")]
HttpStatus(u16, String),
#[error("content too large: {0} bytes (max {1})")]
ContentTooLarge(usize, usize),
#[error("unsupported content type: {0}")]
UnsupportedContentType(String),
#[error("network error: {0}")]
NetworkError(String),
#[error("extraction failed: {0}")]
ExtractError(String),
#[error("{0}")]
Unknown(String),
}
#[derive(Clone, Copy, Debug)]
pub enum FetchErrorKind {
InvalidUrl,
UnsupportedScheme,
PrivateNetworkBlocked,
RedirectLimitExceeded,
RedirectTargetBlocked,
InvalidRedirectLocation,
EmbeddedCredentialsBlocked,
Timeout,
HttpStatus,
ContentTooLarge,
UnsupportedContentType,
NetworkError,
ExtractError,
Unknown,
}
impl FetchError {
pub fn kind(&self) -> FetchErrorKind {
match self {
FetchError::InvalidUrl(_) => FetchErrorKind::InvalidUrl,
FetchError::UnsupportedScheme(_) => FetchErrorKind::UnsupportedScheme,
FetchError::PrivateNetworkBlocked(_) => FetchErrorKind::PrivateNetworkBlocked,
FetchError::RedirectLimitExceeded(_) => FetchErrorKind::RedirectLimitExceeded,
FetchError::RedirectTargetBlocked(_) => FetchErrorKind::RedirectTargetBlocked,
FetchError::InvalidRedirectLocation(_) => FetchErrorKind::InvalidRedirectLocation,
FetchError::EmbeddedCredentialsBlocked(_) => FetchErrorKind::EmbeddedCredentialsBlocked,
FetchError::UrlTooLong(..) => FetchErrorKind::InvalidUrl,
FetchError::Timeout(_) => FetchErrorKind::Timeout,
FetchError::HttpStatus(..) => FetchErrorKind::HttpStatus,
FetchError::ContentTooLarge(..) => FetchErrorKind::ContentTooLarge,
FetchError::UnsupportedContentType(_) => FetchErrorKind::UnsupportedContentType,
FetchError::NetworkError(_) => FetchErrorKind::NetworkError,
FetchError::ExtractError(_) => FetchErrorKind::ExtractError,
FetchError::Unknown(_) => FetchErrorKind::Unknown,
}
}
pub fn error_code(&self) -> &'static str {
match self.kind() {
FetchErrorKind::InvalidUrl => "invalid_url",
FetchErrorKind::UnsupportedScheme => "unsupported_scheme",
FetchErrorKind::PrivateNetworkBlocked => "private_network_blocked",
FetchErrorKind::RedirectLimitExceeded => "redirect_limit_exceeded",
FetchErrorKind::RedirectTargetBlocked => "redirect_target_blocked",
FetchErrorKind::InvalidRedirectLocation => "invalid_redirect_location",
FetchErrorKind::EmbeddedCredentialsBlocked => "embedded_credentials_blocked",
FetchErrorKind::Timeout => "timeout",
FetchErrorKind::HttpStatus => "http_status",
FetchErrorKind::ContentTooLarge => "content_too_large",
FetchErrorKind::UnsupportedContentType => "unsupported_content_type",
FetchErrorKind::NetworkError => "network_error",
FetchErrorKind::ExtractError => "extract_error",
FetchErrorKind::Unknown => "unknown",
}
}
}