Skip to main content

scatter_proxy/
error.rs

1use std::fmt;
2use std::time::Duration;
3
4/// Errors produced by the ScatterProxy scheduler.
5#[derive(Debug)]
6pub enum ScatterProxyError {
7    /// A user-specified timeout elapsed before the task completed.
8    Timeout { elapsed: Duration },
9    /// The task pool is at capacity and cannot accept new submissions (non-blocking path).
10    PoolFull { capacity: usize },
11    /// An error occurred during initialization (e.g. failed to fetch proxy sources).
12    Init(String),
13    /// The submitted request targets a host that is not registered in the router.
14    UnknownHost(String),
15}
16
17impl fmt::Display for ScatterProxyError {
18    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19        match self {
20            ScatterProxyError::Timeout { elapsed } => {
21                write!(f, "task timeout after {:.1}s", elapsed.as_secs_f64())
22            }
23            ScatterProxyError::PoolFull { capacity } => {
24                write!(f, "task pool is full (capacity: {capacity})")
25            }
26            ScatterProxyError::Init(reason) => {
27                write!(f, "initialization error: {reason}")
28            }
29            ScatterProxyError::UnknownHost(host) => {
30                write!(f, "host not registered in router: {host}")
31            }
32        }
33    }
34}
35
36impl std::error::Error for ScatterProxyError {}
37
38#[cfg(test)]
39mod tests {
40    use super::*;
41
42    #[test]
43    fn display_timeout() {
44        let err = ScatterProxyError::Timeout {
45            elapsed: Duration::from_millis(8500),
46        };
47        let msg = err.to_string();
48        assert!(msg.contains("8.5s"));
49    }
50
51    #[test]
52    fn display_pool_full() {
53        let err = ScatterProxyError::PoolFull { capacity: 1000 };
54        let msg = err.to_string();
55        assert!(msg.contains("task pool is full"));
56        assert!(msg.contains("1000"));
57    }
58
59    #[test]
60    fn display_init() {
61        let err = ScatterProxyError::Init("failed to fetch proxy list".into());
62        let msg = err.to_string();
63        assert!(msg.contains("initialization error"));
64        assert!(msg.contains("failed to fetch proxy list"));
65    }
66
67    #[test]
68    fn error_trait_is_implemented() {
69        let err: Box<dyn std::error::Error> = Box::new(ScatterProxyError::Init("test".into()));
70        assert!(err.source().is_none());
71    }
72
73    #[test]
74    fn display_unknown_host() {
75        let err = ScatterProxyError::UnknownHost("szse.cn".into());
76        let msg = err.to_string();
77        assert!(msg.contains("szse.cn"));
78        assert!(msg.contains("not registered"));
79    }
80
81    #[test]
82    fn debug_format_includes_variant_name() {
83        let err = ScatterProxyError::Timeout {
84            elapsed: Duration::from_secs(1),
85        };
86        let dbg = format!("{err:?}");
87        assert!(dbg.contains("Timeout"));
88    }
89
90    #[test]
91    fn timeout_sub_second_formatting() {
92        let err = ScatterProxyError::Timeout {
93            elapsed: Duration::from_millis(200),
94        };
95        assert!(err.to_string().contains("0.2s"));
96    }
97
98    #[test]
99    fn timeout_exact_seconds_formatting() {
100        let err = ScatterProxyError::Timeout {
101            elapsed: Duration::from_secs(60),
102        };
103        assert!(err.to_string().contains("60.0s"));
104    }
105}