1use std::fmt;
2use std::time::Duration;
3
4#[derive(Debug)]
6pub enum ScatterProxyError {
7 Timeout { elapsed: Duration },
9 PoolFull { capacity: usize },
11 Init(String),
13 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}