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}
14
15impl fmt::Display for ScatterProxyError {
16    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17        match self {
18            ScatterProxyError::Timeout { elapsed } => {
19                write!(f, "task timeout after {:.1}s", elapsed.as_secs_f64())
20            }
21            ScatterProxyError::PoolFull { capacity } => {
22                write!(f, "task pool is full (capacity: {capacity})")
23            }
24            ScatterProxyError::Init(reason) => {
25                write!(f, "initialization error: {reason}")
26            }
27        }
28    }
29}
30
31impl std::error::Error for ScatterProxyError {}
32
33#[cfg(test)]
34mod tests {
35    use super::*;
36
37    #[test]
38    fn display_timeout() {
39        let err = ScatterProxyError::Timeout {
40            elapsed: Duration::from_millis(8500),
41        };
42        let msg = err.to_string();
43        assert!(msg.contains("8.5s"));
44    }
45
46    #[test]
47    fn display_pool_full() {
48        let err = ScatterProxyError::PoolFull { capacity: 1000 };
49        let msg = err.to_string();
50        assert!(msg.contains("task pool is full"));
51        assert!(msg.contains("1000"));
52    }
53
54    #[test]
55    fn display_init() {
56        let err = ScatterProxyError::Init("failed to fetch proxy list".into());
57        let msg = err.to_string();
58        assert!(msg.contains("initialization error"));
59        assert!(msg.contains("failed to fetch proxy list"));
60    }
61
62    #[test]
63    fn error_trait_is_implemented() {
64        let err: Box<dyn std::error::Error> = Box::new(ScatterProxyError::Init("test".into()));
65        assert!(err.source().is_none());
66    }
67
68    #[test]
69    fn debug_format_includes_variant_name() {
70        let err = ScatterProxyError::Timeout {
71            elapsed: Duration::from_secs(1),
72        };
73        let dbg = format!("{err:?}");
74        assert!(dbg.contains("Timeout"));
75    }
76
77    #[test]
78    fn timeout_sub_second_formatting() {
79        let err = ScatterProxyError::Timeout {
80            elapsed: Duration::from_millis(200),
81        };
82        assert!(err.to_string().contains("0.2s"));
83    }
84
85    #[test]
86    fn timeout_exact_seconds_formatting() {
87        let err = ScatterProxyError::Timeout {
88            elapsed: Duration::from_secs(60),
89        };
90        assert!(err.to_string().contains("60.0s"));
91    }
92}