Skip to main content

moonpool_transport/rpc/
rpc_error.rs

1//! Unified error type for RPC operations.
2//!
3//! `RpcError` combines `MessagingError` (send-side errors) and `ReplyError`
4//! (receive-side errors) into a single type for ergonomic RPC calls.
5//!
6//! # Example
7//!
8//! ```rust,ignore
9//! // Before: manual error handling
10//! let future = send_request(&transport, &endpoint, req, codec)?; // MessagingError
11//! let result = future.await?; // ReplyError
12//!
13//! // After: unified RpcError
14//! let result = bound_client.method(req).await?; // RpcError
15//! ```
16
17use crate::error::MessagingError;
18use crate::rpc::ReplyError;
19
20/// Unified error type for RPC operations.
21///
22/// This error type wraps both send-side errors (`MessagingError`) and
23/// receive-side errors (`ReplyError`) to provide a single error type
24/// for bound client trait methods.
25#[derive(Debug)]
26pub enum RpcError {
27    /// Error occurred while sending the request.
28    ///
29    /// This includes serialization failures, network errors, and
30    /// transport-level issues.
31    Messaging(MessagingError),
32
33    /// Error occurred while waiting for or processing the response.
34    ///
35    /// This includes broken promises, connection failures, timeouts,
36    /// and deserialization errors.
37    Reply(ReplyError),
38}
39
40impl std::fmt::Display for RpcError {
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        match self {
43            RpcError::Messaging(e) => write!(f, "messaging error: {}", e),
44            RpcError::Reply(e) => write!(f, "reply error: {}", e),
45        }
46    }
47}
48
49impl std::error::Error for RpcError {
50    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
51        match self {
52            RpcError::Messaging(e) => Some(e),
53            RpcError::Reply(e) => Some(e),
54        }
55    }
56}
57
58impl From<MessagingError> for RpcError {
59    fn from(err: MessagingError) -> Self {
60        RpcError::Messaging(err)
61    }
62}
63
64impl From<ReplyError> for RpcError {
65    fn from(err: ReplyError) -> Self {
66        RpcError::Reply(err)
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73    use crate::UID;
74    use std::error::Error;
75
76    #[test]
77    fn test_rpc_error_display() {
78        let messaging_err = RpcError::Messaging(MessagingError::EndpointNotFound {
79            token: UID::new(1, 2),
80        });
81        assert!(messaging_err.to_string().contains("messaging error"));
82
83        let reply_err = RpcError::Reply(ReplyError::BrokenPromise);
84        assert!(reply_err.to_string().contains("reply error"));
85    }
86
87    #[test]
88    fn test_rpc_error_from_messaging() {
89        let err = MessagingError::TransportClosed;
90        let rpc_err: RpcError = err.into();
91        assert!(matches!(rpc_err, RpcError::Messaging(_)));
92    }
93
94    #[test]
95    fn test_rpc_error_from_reply() {
96        let err = ReplyError::Timeout;
97        let rpc_err: RpcError = err.into();
98        assert!(matches!(rpc_err, RpcError::Reply(_)));
99    }
100
101    #[test]
102    fn test_rpc_error_source() {
103        let rpc_err = RpcError::Reply(ReplyError::ConnectionFailed);
104        assert!(rpc_err.source().is_some());
105    }
106}