Skip to main content

thruput_json_rpc_client/
native.rs

1//! Typed native calls and read-only receipt waiting.
2
3pub mod receipt;
4
5use jsonrpsee::core::client::{ClientT, Error as TransportError};
6use jsonrpsee::types::ErrorObjectOwned;
7use serde::Deserialize;
8use thruput_json_rpc_api::params::NamedParams;
9use thruput_json_rpc_types::{error::native::DecodedError, responses::DecodedResponse};
10
11/// Native call failure. Application evidence never implies a recovery action.
12#[derive(Debug, thiserror::Error)]
13#[non_exhaustive]
14pub enum NativeClientError {
15    /// JSON-RPC error with method-aware classification. Preserve the code,
16    /// message and complete data supplied by jsonrpsee, including unknown data.
17    /// Extra top-level error members discarded by that transport are unavailable.
18    #[error("{source}")]
19    Application {
20        #[source]
21        source: ErrorObjectOwned,
22        data: Box<DecodedError>,
23    },
24    /// A malformed known result, retaining the server's complete JSON value.
25    /// This does not establish admission, commitment or execution failure.
26    #[error("Invalid {method} response: {source}")]
27    InvalidResponse {
28        method: &'static str,
29        raw: Box<serde_json::Value>,
30        #[source]
31        source: serde_json::Error,
32    },
33    /// Transport or protocol failure. This says nothing
34    /// about transaction admission or execution; preserve existing uncertainty.
35    #[error(transparent)]
36    Transport(#[from] TransportError),
37}
38
39impl NativeClientError {
40    fn from_call(method: &str, error: TransportError) -> Self {
41        match error {
42            TransportError::Call(source) => match serde_json::to_value(&source) {
43                Ok(raw) => Self::Application {
44                    source,
45                    data: Box::new(DecodedError::decode(method, raw)),
46                },
47                Err(error) => Self::Transport(TransportError::ParseError(error)),
48            },
49            other => Self::Transport(other),
50        }
51    }
52}
53
54/// A native adapter around a caller-owned JSON-RPC transport. Calls submit
55/// exactly once. Unknown result variants retain their complete raw result.
56#[derive(Debug, Clone)]
57pub struct NativeClient<C> {
58    inner: C,
59}
60
61impl<C> NativeClient<C> {
62    /// Wrap an existing transport without connecting or changing its settings.
63    pub fn new(inner: C) -> Self {
64        Self { inner }
65    }
66
67    /// Recover the caller's transport.
68    pub fn into_inner(self) -> C {
69        self.inner
70    }
71}
72
73macro_rules! client_methods {
74    ($( $(#[$doc:meta])* $name:ident($wire:literal, $parse:ident, $decorate:ident): $request:ty => $response:ty; )*) => {
75        impl<C: ClientT + Sync> NativeClient<C> {
76            $(
77                $(#[$doc])*
78                pub async fn $name(&self, request: $request)
79                    -> Result<DecodedResponse<$response>, NativeClientError>
80                {
81                    let raw: serde_json::Value = self.inner.request($wire, NamedParams(request)).await
82                        .map_err(|error| NativeClientError::from_call($wire, error))?;
83                    DecodedResponse::<$response>::deserialize(&raw).map_err(|source| {
84                        NativeClientError::InvalidResponse { method: $wire, raw: Box::new(raw), source }
85                    })
86                }
87            )*
88        }
89    };
90}
91thruput_json_rpc_api::native_methods!(client_methods);