Skip to main content

alloy_json_rpc/
result.rs

1use crate::{Response, ResponsePayload, RpcError, RpcRecv};
2use serde_json::value::RawValue;
3use std::{any::TypeId, borrow::Borrow};
4
5/// The result of a JSON-RPC request.
6///
7/// Either a success response, an error response, or a non-response error. The
8/// non-response error is intended to be used for errors returned by a
9/// transport, or serde errors.
10///
11/// The common cases are:
12/// - `Ok(T)` - The server returned a successful response.
13/// - `Err(RpcError::ErrorResp(ErrorPayload<ErrResp>))` - The server returned an error response.
14/// - `Err(RpcError::SerError(serde_json::Error))` - A serialization error occurred.
15/// - `Err(RpcError::DeserError { err: serde_json::Error, text: String })` - A deserialization error
16///   occurred.
17/// - `Err(RpcError::Transport(E))` - Some client-side or communication error occurred.
18pub type RpcResult<T, E, ErrResp = Box<RawValue>> = Result<T, RpcError<E, ErrResp>>;
19
20/// A partially deserialized [`RpcResult`], borrowing from the deserializer.
21pub type BorrowedRpcResult<'a, E> = RpcResult<&'a RawValue, E, &'a RawValue>;
22
23/// Transform a transport response into an [`RpcResult`], discarding the [`Id`].
24///
25/// [`Id`]: crate::Id
26pub fn transform_response<T, E, ErrResp>(response: Response<T, ErrResp>) -> RpcResult<T, E, ErrResp>
27where
28    ErrResp: RpcRecv,
29{
30    match response {
31        Response { payload: ResponsePayload::Failure(err_resp), .. } => {
32            Err(RpcError::err_resp(err_resp))
33        }
34        Response { payload: ResponsePayload::Success(result), .. } => Ok(result),
35    }
36}
37
38/// Transform a transport outcome into an [`RpcResult`], discarding the [`Id`].
39///
40/// [`Id`]: crate::Id
41pub fn transform_result<T, E, ErrResp>(
42    response: Result<Response<T, ErrResp>, E>,
43) -> Result<T, RpcError<E, ErrResp>>
44where
45    ErrResp: RpcRecv,
46{
47    match response {
48        Ok(resp) => transform_response(resp),
49        Err(e) => Err(RpcError::Transport(e)),
50    }
51}
52
53/// Attempt to deserialize the `Ok(_)` variant of an [`RpcResult`].
54pub fn try_deserialize_ok<J, T, E, ErrResp>(
55    result: RpcResult<J, E, ErrResp>,
56) -> RpcResult<T, E, ErrResp>
57where
58    J: Borrow<RawValue> + 'static,
59    T: RpcRecv,
60    ErrResp: RpcRecv,
61{
62    let json = result?;
63
64    // Fast path: the caller wants the already-owned `Box<RawValue>` back unchanged. Hand it
65    // over directly, skipping the byte copy and full JSON validation scan that
66    // `from_str::<Box<RawValue>>` would repeat over an already-validated value.
67    if TypeId::of::<J>() == TypeId::of::<Box<RawValue>>()
68        && TypeId::of::<T>() == TypeId::of::<Box<RawValue>>()
69    {
70        // SAFETY: `J` and `T` are both `Box<RawValue>`, so this is a no-op reinterpretation.
71        // `transmute_copy` stands in for the unstable `transmute_unchecked` (sizes are equal).
72        let json = std::mem::ManuallyDrop::new(json);
73        return Ok(unsafe { std::mem::transmute_copy::<J, T>(&json) });
74    }
75
76    let _guard = debug_span!("deserialize_response", ty=%std::any::type_name::<T>()).entered();
77    let json = json.borrow().get();
78    trace!(%json, "deserializing");
79    serde_json::from_str(json)
80        .inspect(|response| trace!(?response, "deserialized"))
81        .inspect_err(|err| trace!(?err, "failed to deserialize"))
82        .map_err(|err| RpcError::deser_err(err, json))
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88    use serde_json::value::to_raw_value;
89
90    #[test]
91    fn raw_value_success_returns_payload_without_reencoding() {
92        // Deliberately non-canonical spacing: a byte-identical result proves the payload
93        // was not re-parsed and re-encoded.
94        let src = "{  \"a\" :1,\"b\": [ 2 ,3 ] }";
95        let raw = RawValue::from_string(src.to_owned()).unwrap();
96        let ptr = raw.get().as_ptr();
97        let input: RpcResult<Box<RawValue>, (), Box<RawValue>> = Ok(raw);
98
99        let out = try_deserialize_ok::<_, Box<RawValue>, (), Box<RawValue>>(input).unwrap();
100
101        assert_eq!(out.get(), src);
102        // Same allocation: the owned payload was handed back, not rebuilt.
103        assert_eq!(out.get().as_ptr(), ptr);
104    }
105
106    #[test]
107    fn generic_deserialize_path_unchanged() {
108        let raw = to_raw_value(&42u64).unwrap();
109        let input: RpcResult<Box<RawValue>, (), Box<RawValue>> = Ok(raw);
110        let out = try_deserialize_ok::<_, u64, (), Box<RawValue>>(input).unwrap();
111        assert_eq!(out, 42);
112    }
113}