Skip to main content

chainrpc_core/
transport.rs

1//! The `RpcTransport` trait — the core abstraction for all RPC providers.
2
3use async_trait::async_trait;
4use serde::de::DeserializeOwned;
5use serde_json::Value;
6
7use crate::error::TransportError;
8use crate::request::{JsonRpcRequest, JsonRpcResponse};
9
10/// Provider health status.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum HealthStatus {
13    /// Provider is responding normally.
14    Healthy,
15    /// Provider is responding but degraded (high latency, partial errors).
16    Degraded,
17    /// Provider is not responding (circuit open).
18    Unhealthy,
19    /// Health status is unknown (not yet checked).
20    Unknown,
21}
22
23impl std::fmt::Display for HealthStatus {
24    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25        match self {
26            Self::Healthy => write!(f, "healthy"),
27            Self::Degraded => write!(f, "degraded"),
28            Self::Unhealthy => write!(f, "unhealthy"),
29            Self::Unknown => write!(f, "unknown"),
30        }
31    }
32}
33
34/// The central async trait every RPC transport must implement.
35///
36/// # Thread Safety
37/// Implementations must be `Send + Sync` for use across Tokio tasks.
38///
39/// # Object Safety
40/// The trait is object-safe and can be stored as `Arc<dyn RpcTransport>`.
41#[async_trait]
42pub trait RpcTransport: Send + Sync + 'static {
43    /// Send a single JSON-RPC request and return the response.
44    async fn send(&self, req: JsonRpcRequest) -> Result<JsonRpcResponse, TransportError>;
45
46    /// Send a batch of JSON-RPC requests.
47    ///
48    /// Default implementation sends them sequentially; override for true batching.
49    async fn send_batch(
50        &self,
51        reqs: Vec<JsonRpcRequest>,
52    ) -> Result<Vec<JsonRpcResponse>, TransportError> {
53        let mut responses = Vec::with_capacity(reqs.len());
54        for req in reqs {
55            responses.push(self.send(req).await?);
56        }
57        Ok(responses)
58    }
59
60    /// Return the current health status of this transport.
61    fn health(&self) -> HealthStatus {
62        HealthStatus::Unknown
63    }
64
65    /// Return the transport's identifier (URL or name).
66    fn url(&self) -> &str;
67
68    /// Convenience: call a method and deserialize the result.
69    async fn call<T: DeserializeOwned>(
70        &self,
71        id: u64,
72        method: &str,
73        params: Vec<Value>,
74    ) -> Result<T, TransportError>
75    where
76        Self: Sized,
77    {
78        let req = JsonRpcRequest::new(id, method, params);
79        let resp = self.send(req).await?;
80        let result = resp.into_result().map_err(TransportError::Rpc)?;
81        serde_json::from_value(result).map_err(TransportError::Deserialization)
82    }
83}