Skip to main content

reth_rpc_server_types/
result.rs

1//! Additional helpers for converting errors.
2
3use std::fmt;
4
5use alloy_rpc_types_engine::PayloadError;
6use jsonrpsee_core::RpcResult;
7use reth_errors::ConsensusError;
8
9/// Helper trait to easily convert various `Result` types into [`RpcResult`]
10pub trait ToRpcResult<Ok, Err>: Sized {
11    /// Converts result to [`RpcResult`] by converting error variant to
12    /// [`jsonrpsee_types::error::ErrorObject`]
13    fn to_rpc_result(self) -> RpcResult<Ok>
14    where
15        Err: fmt::Display,
16    {
17        self.map_internal_err(|err| err.to_string())
18    }
19
20    /// Converts this type into an [`RpcResult`]
21    fn map_rpc_err<'a, F, M>(self, op: F) -> RpcResult<Ok>
22    where
23        F: FnOnce(Err) -> (i32, M, Option<&'a [u8]>),
24        M: Into<String>;
25
26    /// Converts this type into an [`RpcResult`] with the
27    /// [`jsonrpsee_types::error::INTERNAL_ERROR_CODE`] and the given message.
28    fn map_internal_err<F, M>(self, op: F) -> RpcResult<Ok>
29    where
30        F: FnOnce(Err) -> M,
31        M: Into<String>;
32
33    /// Converts this type into an [`RpcResult`] with the
34    /// [`jsonrpsee_types::error::INTERNAL_ERROR_CODE`] and given message and data.
35    fn map_internal_err_with_data<'a, F, M>(self, op: F) -> RpcResult<Ok>
36    where
37        F: FnOnce(Err) -> (M, &'a [u8]),
38        M: Into<String>;
39
40    /// Adds a message to the error variant and returns an internal Error.
41    ///
42    /// This is shorthand for `Self::map_internal_err(|err| format!("{msg}: {err}"))`.
43    fn with_message(self, msg: &str) -> RpcResult<Ok>;
44}
45
46/// A macro that implements the `ToRpcResult` for a specific error type
47#[macro_export]
48macro_rules! impl_to_rpc_result {
49    ($err:ty) => {
50        impl<Ok> ToRpcResult<Ok, $err> for Result<Ok, $err> {
51            #[inline]
52            fn map_rpc_err<'a, F, M>(self, op: F) -> jsonrpsee_core::RpcResult<Ok>
53            where
54                F: FnOnce($err) -> (i32, M, Option<&'a [u8]>),
55                M: Into<String>,
56            {
57                match self {
58                    Ok(t) => Ok(t),
59                    Err(err) => {
60                        let (code, msg, data) = op(err);
61                        Err($crate::result::rpc_err(code, msg, data))
62                    }
63                }
64            }
65
66            #[inline]
67            fn map_internal_err<F, M>(self, op: F) -> jsonrpsee_core::RpcResult<Ok>
68            where
69                F: FnOnce($err) -> M,
70                M: Into<String>,
71            {
72                self.map_err(|err| $crate::result::internal_rpc_err(op(err)))
73            }
74
75            #[inline]
76            fn map_internal_err_with_data<'a, F, M>(self, op: F) -> jsonrpsee_core::RpcResult<Ok>
77            where
78                F: FnOnce($err) -> (M, &'a [u8]),
79                M: Into<String>,
80            {
81                match self {
82                    Ok(t) => Ok(t),
83                    Err(err) => {
84                        let (msg, data) = op(err);
85                        Err($crate::result::internal_rpc_err_with_data(msg, data))
86                    }
87                }
88            }
89
90            #[inline]
91            fn with_message(self, msg: &str) -> jsonrpsee_core::RpcResult<Ok> {
92                match self {
93                    Ok(t) => Ok(t),
94                    Err(err) => {
95                        let msg = format!("{msg}: {err}");
96                        Err($crate::result::internal_rpc_err(msg))
97                    }
98                }
99            }
100        }
101    };
102}
103
104impl_to_rpc_result!(PayloadError);
105impl_to_rpc_result!(ConsensusError);
106impl_to_rpc_result!(reth_errors::RethError);
107impl_to_rpc_result!(reth_errors::ProviderError);
108impl_to_rpc_result!(reth_network_api::NetworkError);
109
110/// Constructs an invalid params JSON-RPC error.
111pub fn invalid_params_rpc_err(
112    msg: impl Into<String>,
113) -> jsonrpsee_types::error::ErrorObject<'static> {
114    rpc_err(jsonrpsee_types::error::INVALID_PARAMS_CODE, msg, None)
115}
116
117/// Constructs an internal JSON-RPC error.
118pub fn internal_rpc_err(msg: impl Into<String>) -> jsonrpsee_types::error::ErrorObject<'static> {
119    rpc_err(jsonrpsee_types::error::INTERNAL_ERROR_CODE, msg, None)
120}
121
122/// Constructs an internal JSON-RPC error with data
123pub fn internal_rpc_err_with_data(
124    msg: impl Into<String>,
125    data: &[u8],
126) -> jsonrpsee_types::error::ErrorObject<'static> {
127    rpc_err(jsonrpsee_types::error::INTERNAL_ERROR_CODE, msg, Some(data))
128}
129
130/// Constructs an internal JSON-RPC error with code and message
131pub fn rpc_error_with_code(
132    code: i32,
133    msg: impl Into<String>,
134) -> jsonrpsee_types::error::ErrorObject<'static> {
135    rpc_err(code, msg, None)
136}
137
138/// Constructs a JSON-RPC error, consisting of `code`, `message` and optional `data`.
139pub fn rpc_err(
140    code: i32,
141    msg: impl Into<String>,
142    data: Option<&[u8]>,
143) -> jsonrpsee_types::error::ErrorObject<'static> {
144    jsonrpsee_types::error::ErrorObject::owned(
145        code,
146        msg.into(),
147        data.map(|data| {
148            jsonrpsee_core::to_json_raw_value(&alloy_primitives::hex::encode_prefixed(data))
149                .expect("serializing String can't fail")
150        }),
151    )
152}
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157    use reth_errors::{RethError, RethResult};
158
159    const fn assert_rpc_result<T, E, TRR: ToRpcResult<T, E>>() {}
160
161    #[test]
162    fn can_convert_rpc() {
163        assert_rpc_result::<(), RethError, RethResult<()>>();
164
165        let res = RethResult::Ok(100);
166        let rpc_res = res.map_internal_err(|_| "This is a message");
167        let val = rpc_res.unwrap();
168        assert_eq!(val, 100);
169    }
170}