Skip to main content

stygian_proxy/
error.rs

1/// Proxy error types and result alias.
2use thiserror::Error;
3
4/// Errors that can occur within the stygian-proxy library.
5///
6/// # Examples
7///
8/// ```rust
9/// use stygian_proxy::error::{ProxyError, ProxyResult};
10///
11/// fn example() -> ProxyResult<()> {
12///     Err(ProxyError::PoolExhausted)
13/// }
14/// ```
15#[derive(Debug, Error)]
16#[non_exhaustive]
17pub enum ProxyError {
18    /// The proxy pool has no available proxies to hand out.
19    #[error("proxy pool is exhausted")]
20    PoolExhausted,
21
22    /// Every proxy in the pool is currently unhealthy or has an open circuit.
23    #[error("all proxies are unhealthy")]
24    AllProxiesUnhealthy,
25
26    /// A supplied proxy URL failed validation.
27    #[error("invalid proxy URL `{url}`: {reason}")]
28    InvalidProxyUrl {
29        /// The URL that was rejected.
30        url: String,
31        /// Human-readable explanation of the validation failure.
32        reason: String,
33    },
34
35    /// A health check request for a proxy failed.
36    #[error("health check failed for proxy `{proxy}`: {message}")]
37    HealthCheckFailed {
38        /// Display form of the proxy URL (credentials redacted).
39        proxy: String,
40        /// Description of the underlying error.
41        message: String,
42    },
43
44    /// The circuit breaker for this proxy is open — calls are being rejected fast.
45    #[error("circuit breaker is open for proxy `{proxy}`")]
46    CircuitOpen {
47        /// Display form of the proxy URL (credentials redacted).
48        proxy: String,
49    },
50
51    /// An error from the underlying storage layer.
52    #[error("storage error: {0}")]
53    StorageError(String),
54
55    /// A configuration error.
56    #[error("configuration error: {0}")]
57    ConfigError(String),
58
59    /// A remote proxy list could not be fetched or parsed.
60    #[error("proxy fetch failed from `{origin}`: {message}")]
61    FetchFailed {
62        /// URL or identifier of the remote source.
63        origin: String,
64        /// Human-readable description of the failure.
65        message: String,
66    },
67
68    /// No proxy in the pool satisfies the requested capability set.
69    #[error("no proxy satisfies the requested capabilities")]
70    NoCompatibleProxy,
71
72    /// Coherence validator emitted a hard mismatch on a field registered
73    /// for hard-fail behaviour in [`crate::ports::coherence::CoherencePolicy`].
74    ///
75    /// Only emitted by
76    /// [`crate::manager::ProxyManager::acquire_proxy_with_coherence`] when
77    /// the `coherence-validation` cargo feature is enabled and the
78    /// configured policy lists the offending field as a hard-fail
79    /// vector.
80    #[error("coherence mismatch on `{field}` ({severity})")]
81    CoherenceMismatch {
82        /// Which vector disagreed (proxy geo vs DNS, WebRTC /16, …).
83        field: crate::ports::coherence::MismatchField,
84        /// Severity classification from the validator.
85        severity: crate::ports::coherence::MismatchSeverity,
86    },
87
88    /// A supplied geo-metadata field on
89    /// [`crate::types::ProxyCapabilities`] failed ingest-time
90    /// validation.
91    ///
92    /// Emitted by the storage adapter's `add` path (and by
93    /// [`crate::manager::ProxyManager::add_proxy_with_metadata`]) when
94    /// `asn`, `city`, or `postal_code` is malformed. Examples:
95    /// `asn = 0`, `asn = u32::MAX` (both reserved), `city = ""`,
96    /// `postal_code = ""`, or any field exceeding the documented
97    /// length ceiling.
98    #[error("invalid geo metadata on `{field}`: {reason}")]
99    InvalidGeoMetadata {
100        /// Which geo field was rejected (`"asn"`, `"city"`, or
101        /// `"postal_code"`).
102        field: String,
103        /// Human-readable explanation of the validation failure.
104        reason: String,
105    },
106}
107
108/// Convenience result alias for all stygian-proxy operations.
109pub type ProxyResult<T> = Result<T, ProxyError>;