use pyo3::create_exception;
use pyo3::exceptions::PyException;
use pyo3::prelude::*;
create_exception!(
eggfetch,
EggfetchError,
PyException,
"Base exception for all eggfetch errors."
);
create_exception!(
eggfetch,
RequestError,
EggfetchError,
"Error related to request construction or execution."
);
create_exception!(
eggfetch,
InvalidUrl,
RequestError,
"The provided URL is invalid."
);
create_exception!(
eggfetch,
TimeoutException,
RequestError,
"A timeout was exceeded."
);
create_exception!(
eggfetch,
PoolTimeout,
TimeoutException,
"Pool acquisition timed out."
);
create_exception!(
eggfetch,
ConnectTimeout,
TimeoutException,
"Connection establishment timed out."
);
create_exception!(
eggfetch,
ReadTimeout,
TimeoutException,
"Reading response timed out."
);
create_exception!(
eggfetch,
WriteTimeout,
TimeoutException,
"Writing request body timed out."
);
create_exception!(
eggfetch,
NetworkError,
RequestError,
"A network-level error occurred."
);
create_exception!(
eggfetch,
ProtocolError,
RequestError,
"An HTTP protocol error occurred."
);
create_exception!(
eggfetch,
BodyError,
RequestError,
"An error occurred while processing the body."
);
create_exception!(
eggfetch,
HTTPStatusError,
EggfetchError,
"The server returned an error status code."
);
create_exception!(
eggfetch,
UnsupportedKwarg,
EggfetchError,
"An unsupported keyword argument was passed."
);
create_exception!(
eggfetch,
TooManyRedirects,
RequestError,
"Too many redirects were followed."
);
create_exception!(
eggfetch,
StreamConsumed,
EggfetchError,
"The response stream has already been consumed."
);
create_exception!(
eggfetch,
StreamClosed,
EggfetchError,
"The response stream has been closed."
);
create_exception!(
eggfetch,
ResponseNotRead,
EggfetchError,
"The response body has been buffered; use the buffered API instead of streaming."
);
create_exception!(
eggfetch,
DecompressionError,
RequestError,
"An error occurred during response decompression."
);
create_exception!(
eggfetch,
UnsupportedContentEncoding,
RequestError,
"The server used an unsupported content encoding."
);
create_exception!(
eggfetch,
ProxyError,
RequestError,
"An error related to proxy configuration or connection."
);
create_exception!(
eggfetch,
ProxyConnectError,
ProxyError,
"Failed to connect to the proxy server."
);
create_exception!(
eggfetch,
ProxyAuthError,
ProxyError,
"The proxy server requires authentication."
);
create_exception!(
eggfetch,
BodyNotReplayableForRetry,
RequestError,
"The request body cannot be replayed for retry."
);
create_exception!(
eggfetch,
RetryBudgetExhausted,
RequestError,
"The retry budget was exhausted."
);
create_exception!(
eggfetch,
RetryNotConfigured,
RequestError,
"Retry is not configured for this request or client."
);
create_exception!(
eggfetch,
Http2Error,
RequestError,
"An HTTP/2 protocol error occurred."
);
create_exception!(
eggfetch,
Http2GoAway,
Http2Error,
"The HTTP/2 connection received a GOAWAY frame."
);
create_exception!(
eggfetch,
Http2StreamReset,
Http2Error,
"An HTTP/2 stream was reset."
);
create_exception!(
eggfetch,
Http2FlowControlError,
Http2Error,
"An HTTP/2 flow-control error occurred."
);
create_exception!(
eggfetch,
H3Error,
RequestError,
"An HTTP/3 protocol error occurred."
);
create_exception!(
eggfetch,
H3ConnectError,
H3Error,
"An HTTP/3 connection error occurred."
);
create_exception!(
eggfetch,
H3ProtocolError,
H3Error,
"An HTTP/3 protocol error occurred."
);
#[allow(clippy::needless_pass_by_value)]
#[allow(clippy::too_many_lines)]
pub fn map_err(err: eggfetch_core::Error) -> PyErr {
use eggfetch_core::timeout::TimeoutPhase;
match err {
eggfetch_core::Error::InvalidUrl(msg) => InvalidUrl::new_err(msg),
eggfetch_core::Error::InvalidMethod(msg)
| eggfetch_core::Error::InvalidHeaderName(msg)
| eggfetch_core::Error::InvalidHeaderValue(msg)
| eggfetch_core::Error::RequestBuild(msg)
| eggfetch_core::Error::InvalidResolvedTarget(msg)
| eggfetch_core::Error::Unsupported(msg)
| eggfetch_core::Error::Pool(msg)
| eggfetch_core::Error::InvalidRedirectLocation(msg)
| eggfetch_core::Error::InvalidAuthHeader(msg)
| eggfetch_core::Error::ConflictingAuth(msg)
| eggfetch_core::Error::JsonSerialize(msg)
| eggfetch_core::Error::JsonDeserialize(msg)
| eggfetch_core::Error::TlsConfig(msg)
| eggfetch_core::Error::CaBundle(msg)
| eggfetch_core::Error::ClientCert(msg)
| eggfetch_core::Error::PrivateKey(msg) => RequestError::new_err(msg),
eggfetch_core::Error::Connect(msg)
| eggfetch_core::Error::Tls(msg)
| eggfetch_core::Error::CertificateVerification(msg)
| eggfetch_core::Error::HostnameVerification(msg) => NetworkError::new_err(msg),
eggfetch_core::Error::Protocol(msg) => ProtocolError::new_err(msg),
eggfetch_core::Error::Body(msg) => BodyError::new_err(msg),
eggfetch_core::Error::Hyper(arc) => NetworkError::new_err(arc.to_string()),
eggfetch_core::Error::HyperClient(arc) => NetworkError::new_err(arc.to_string()),
eggfetch_core::Error::Io(arc) => NetworkError::new_err(arc.to_string()),
eggfetch_core::Error::BodyNotReplayableForRedirect => {
RequestError::new_err("request body is not replayable for redirect".to_string())
}
eggfetch_core::Error::ResolvedTargetRedirect => RequestError::new_err(
"resolved destination cannot be reused across a cross-origin redirect",
),
eggfetch_core::Error::Decompression(msg) => DecompressionError::new_err(msg),
eggfetch_core::Error::UnsupportedContentEncoding(msg) => {
UnsupportedContentEncoding::new_err(msg)
}
eggfetch_core::Error::TooManyRedirects { followed, max } => TooManyRedirects::new_err(
format!("too many redirects: followed {followed}, max is {max}"),
),
eggfetch_core::Error::InvalidProxyUrl(msg) => ProxyError::new_err(msg),
eggfetch_core::Error::ProxyConnect(msg) => ProxyConnectError::new_err(msg),
eggfetch_core::Error::ProxyAuthRequired => {
ProxyAuthError::new_err("proxy authentication required")
}
eggfetch_core::Error::ProxyConnectRejected { status, body } => {
ProxyConnectError::new_err(format!("CONNECT rejected: {status} {body}"))
}
eggfetch_core::Error::MalformedProxyResponse(msg) => {
ProtocolError::new_err(format!("malformed proxy response: {msg}"))
}
eggfetch_core::Error::Timeout { phase, elapsed } => {
let msg = format!("{phase} timeout after {elapsed:?}");
match phase {
TimeoutPhase::Pool => PoolTimeout::new_err(msg),
TimeoutPhase::Connect | TimeoutPhase::ProxyConnect | TimeoutPhase::ProxyTls => {
ConnectTimeout::new_err(msg)
}
TimeoutPhase::Read => ReadTimeout::new_err(msg),
TimeoutPhase::Write => WriteTimeout::new_err(msg),
TimeoutPhase::Total => TimeoutException::new_err(msg),
}
}
eggfetch_core::Error::DecodedBodyTooLarge => {
RequestError::new_err("decoded body too large".to_string())
}
eggfetch_core::Error::DecompressionRatioExceeded => {
RequestError::new_err("decompression ratio exceeded".to_string())
}
eggfetch_core::Error::BodyNotReplayableForRetry => {
BodyNotReplayableForRetry::new_err("request body is not replayable for retry")
}
eggfetch_core::Error::RetryBudgetExhausted { attempts } => RetryBudgetExhausted::new_err(
format!("retry budget exhausted after {attempts} attempts"),
),
eggfetch_core::Error::RetryNotConfigured => {
RetryNotConfigured::new_err("retry is not configured for this request or client")
}
eggfetch_core::Error::Http2GoAway {
last_stream_id,
debug_data,
} => Http2GoAway::new_err(format!(
"HTTP/2 GOAWAY: last_stream_id={last_stream_id}, debug={debug_data}"
)),
eggfetch_core::Error::Http2StreamReset { reason } => {
Http2StreamReset::new_err(format!("HTTP/2 stream reset: {reason}"))
}
eggfetch_core::Error::Http2FlowControl(msg) => {
Http2FlowControlError::new_err(format!("HTTP/2 flow control error: {msg}"))
}
eggfetch_core::Error::Http2Protocol(msg) => {
Http2Error::new_err(format!("HTTP/2 protocol error: {msg}"))
}
eggfetch_core::Error::H3Connect(msg) => {
H3ConnectError::new_err(format!("HTTP/3 connection error: {msg}"))
}
eggfetch_core::Error::H3ConnectionClosed(msg) => {
H3ProtocolError::new_err(format!("HTTP/3 connection closed: {msg}"))
}
eggfetch_core::Error::H3Stream(msg) => {
H3Error::new_err(format!("HTTP/3 stream error: {msg}"))
}
eggfetch_core::Error::H3Protocol(msg) => {
H3ProtocolError::new_err(format!("HTTP/3 protocol error: {msg}"))
}
eggfetch_core::Error::TraceCallbackAborted => {
pyo3::exceptions::PyRuntimeError::new_err("trace callback aborted the request")
}
}
}