Skip to main content

moonpool_transport/rpc/
reply_error.rs

1//! Error types for request-response operations.
2//!
3//! These errors can occur during the lifecycle of a request:
4//! - Server drops the promise without responding ([`ReplyError::BrokenPromise`])
5//! - Network connection fails ([`ReplyError::ConnectionFailed`])
6//! - Request times out ([`ReplyError::Timeout`])
7//! - Serialization/deserialization fails ([`ReplyError::Serialization`])
8
9use serde::{Deserialize, Serialize};
10
11/// Errors that can occur during request-response operations.
12///
13/// These errors are serializable so they can be sent over the network
14/// (e.g., when a server sends a BrokenPromise error to the client).
15#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
16pub enum ReplyError {
17    /// The server dropped the ReplyPromise without sending a response.
18    ///
19    /// This typically indicates a bug in the server code - forgetting to
20    /// reply before the promise goes out of scope.
21    BrokenPromise,
22
23    /// The network connection failed during the request.
24    ///
25    /// The request may or may not have been delivered to the server.
26    ConnectionFailed,
27
28    /// The request timed out waiting for a response.
29    ///
30    /// The server may still be processing the request.
31    Timeout,
32
33    /// Serialization or deserialization failed.
34    ///
35    /// Contains a description of what went wrong.
36    Serialization {
37        /// Human-readable error message.
38        message: String,
39    },
40
41    /// The endpoint was not found.
42    ///
43    /// The destination endpoint is not registered.
44    EndpointNotFound,
45}
46
47impl std::fmt::Display for ReplyError {
48    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49        match self {
50            ReplyError::BrokenPromise => write!(f, "server dropped promise without reply"),
51            ReplyError::ConnectionFailed => write!(f, "connection failed"),
52            ReplyError::Timeout => write!(f, "request timed out"),
53            ReplyError::Serialization { message } => write!(f, "serialization error: {}", message),
54            ReplyError::EndpointNotFound => write!(f, "endpoint not found"),
55        }
56    }
57}
58
59impl std::error::Error for ReplyError {}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64
65    #[test]
66    fn test_reply_error_display() {
67        assert_eq!(
68            ReplyError::BrokenPromise.to_string(),
69            "server dropped promise without reply"
70        );
71        assert_eq!(
72            ReplyError::ConnectionFailed.to_string(),
73            "connection failed"
74        );
75        assert_eq!(ReplyError::Timeout.to_string(), "request timed out");
76        assert_eq!(
77            ReplyError::Serialization {
78                message: "invalid JSON".to_string()
79            }
80            .to_string(),
81            "serialization error: invalid JSON"
82        );
83        assert_eq!(
84            ReplyError::EndpointNotFound.to_string(),
85            "endpoint not found"
86        );
87    }
88
89    #[test]
90    fn test_reply_error_serde_roundtrip() {
91        let errors = vec![
92            ReplyError::BrokenPromise,
93            ReplyError::ConnectionFailed,
94            ReplyError::Timeout,
95            ReplyError::Serialization {
96                message: "test error".to_string(),
97            },
98            ReplyError::EndpointNotFound,
99        ];
100
101        for error in errors {
102            let json = serde_json::to_string(&error).expect("serialize");
103            let decoded: ReplyError = serde_json::from_str(&json).expect("deserialize");
104            assert_eq!(error, decoded);
105        }
106    }
107
108    #[test]
109    fn test_reply_error_is_error_trait() {
110        let error: Box<dyn std::error::Error> = Box::new(ReplyError::BrokenPromise);
111        assert!(error.to_string().contains("promise"));
112    }
113}