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>;