1use dig_chainsource_interface::ChainSourceError;
10use thiserror::Error;
11
12#[derive(Debug, Error, Clone, PartialEq, Eq)]
14pub enum ChiaPeerError {
15 #[error("chia peer transport error: {0}")]
18 Transport(String),
19
20 #[error("chia peer rejected the request: {0}")]
23 Rejected(String),
24
25 #[error("malformed chia peer response: {0}")]
28 Malformed(String),
29
30 #[error("chia peer request timed out")]
33 Timeout,
34
35 #[error("chia peer discovery failed")]
37 PeerDiscoveryFailed,
38
39 #[error("chia light client is not connected")]
41 NotConnected,
42
43 #[error("chia peer TLS setup error: {0}")]
45 Tls(String),
46}
47
48impl ChiaPeerError {
49 fn looks_like_timeout(message: &str) -> bool {
52 let lower = message.to_ascii_lowercase();
53 lower.contains("timed out") || lower.contains("timeout")
54 }
55}
56
57impl From<ChiaPeerError> for ChainSourceError {
58 fn from(error: ChiaPeerError) -> Self {
61 match error {
62 ChiaPeerError::Timeout => ChainSourceError::Timeout,
63 ChiaPeerError::Transport(msg) if ChiaPeerError::looks_like_timeout(&msg) => {
64 ChainSourceError::Timeout
65 }
66 ChiaPeerError::Transport(msg) => ChainSourceError::Transport(msg),
67 ChiaPeerError::Rejected(msg) => ChainSourceError::Transport(msg),
68 ChiaPeerError::Malformed(msg) => ChainSourceError::Malformed(msg),
69 ChiaPeerError::PeerDiscoveryFailed => {
70 ChainSourceError::Transport("peer discovery failed".to_string())
71 }
72 ChiaPeerError::NotConnected => {
73 ChainSourceError::Transport("light client is not connected".to_string())
74 }
75 ChiaPeerError::Tls(msg) => ChainSourceError::Transport(format!("TLS: {msg}")),
76 }
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn timeout_variant_maps_to_timeout() {
86 assert_eq!(
87 ChainSourceError::from(ChiaPeerError::Timeout),
88 ChainSourceError::Timeout
89 );
90 }
91
92 #[test]
93 fn timed_out_transport_message_maps_to_timeout() {
94 let mapped = ChainSourceError::from(ChiaPeerError::Transport("request timed out".into()));
95 assert_eq!(mapped, ChainSourceError::Timeout);
96 }
97
98 #[test]
99 fn plain_transport_maps_to_transport_never_absence() {
100 let mapped = ChainSourceError::from(ChiaPeerError::Transport("socket reset".into()));
101 assert_eq!(mapped, ChainSourceError::Transport("socket reset".into()));
102 }
103
104 #[test]
105 fn rejected_maps_to_transport() {
106 let mapped = ChainSourceError::from(ChiaPeerError::Rejected("reorg".into()));
107 assert!(matches!(mapped, ChainSourceError::Transport(_)));
108 }
109
110 #[test]
111 fn malformed_maps_to_malformed() {
112 let mapped = ChainSourceError::from(ChiaPeerError::Malformed("bad bytes".into()));
113 assert!(matches!(mapped, ChainSourceError::Malformed(_)));
114 }
115
116 #[test]
117 fn not_connected_and_discovery_and_tls_map_to_transport() {
118 assert!(matches!(
119 ChainSourceError::from(ChiaPeerError::NotConnected),
120 ChainSourceError::Transport(_)
121 ));
122 assert!(matches!(
123 ChainSourceError::from(ChiaPeerError::PeerDiscoveryFailed),
124 ChainSourceError::Transport(_)
125 ));
126 assert!(matches!(
127 ChainSourceError::from(ChiaPeerError::Tls("x".into())),
128 ChainSourceError::Transport(_)
129 ));
130 }
131}