Skip to main content

rs_dapi_client/
lib.rs

1//! This crate provides [DapiClient] --- transport layer for a decentralized API for Dash.
2
3#![deny(missing_docs)]
4
5mod address_ban_info;
6mod address_list;
7mod connection_pool;
8mod dapi_client;
9#[cfg(feature = "dump")]
10pub mod dump;
11mod executor;
12#[cfg(feature = "mocks")]
13pub mod mock;
14mod request_settings;
15pub mod transport;
16
17pub use address_ban_info::AddressBanInfo;
18pub use address_list::Address;
19pub use address_list::AddressList;
20pub use address_list::AddressListError;
21pub use address_list::AddressStatus;
22pub use connection_pool::ConnectionPool;
23pub use dapi_client::{update_address_ban_status, DapiClient, DapiClientError};
24#[cfg(feature = "dump")]
25pub use dump::DumpData;
26pub use executor::{
27    DapiRequestExecutor, ExecutionError, ExecutionResponse, ExecutionResult, InnerInto, IntoInner,
28    WrapToExecutionResult,
29};
30use futures::{future::BoxFuture, FutureExt};
31#[cfg(any(target_arch = "wasm32", not(feature = "mocks")))]
32pub use http::Uri;
33#[cfg(all(feature = "mocks", not(target_arch = "wasm32")))]
34pub use http_serde::http::Uri;
35pub use request_settings::RequestSettings;
36
37/// A DAPI request could be executed with an initialized [DapiClient].
38///
39/// # Examples
40/// Requires the `mocks` feature.
41/// ```
42/// # #[cfg(feature = "mocks")]
43/// # {
44/// use rs_dapi_client::{RequestSettings, AddressList, mock::MockDapiClient, DapiClientError, DapiRequest, ExecutionError};
45/// use dapi_grpc::platform::v0::{self as proto};
46///
47/// # let _ = async {
48/// let mut client = MockDapiClient::new();
49/// let request: proto::GetIdentityRequest = proto::get_identity_request::GetIdentityRequestV0 { id: b"0".to_vec(), prove: true }.into();
50/// let response = request.execute(&mut client, RequestSettings::default()).await?;
51/// # Ok::<(), ExecutionError<DapiClientError>>(())
52/// # };
53/// # }
54/// ```
55pub trait DapiRequest {
56    /// Response from DAPI for this specific request.
57    type Response;
58
59    /// Executes the request.
60    fn execute<'c, D: DapiRequestExecutor>(
61        self,
62        dapi_client: &'c D,
63        settings: RequestSettings,
64    ) -> BoxFuture<'c, ExecutionResult<Self::Response, DapiClientError>>
65    where
66        Self: 'c;
67}
68
69/// The trait is intentionally made sealed since it defines what is possible to send to DAPI.
70impl<T: transport::TransportRequest + Send> DapiRequest for T {
71    type Response = T::Response;
72
73    fn execute<'c, D: DapiRequestExecutor>(
74        self,
75        dapi_client: &'c D,
76        settings: RequestSettings,
77    ) -> BoxFuture<'c, ExecutionResult<Self::Response, DapiClientError>>
78    where
79        Self: 'c,
80    {
81        dapi_client.execute(self, settings).boxed()
82    }
83}
84
85/// Returns true if the operation can be retried.
86pub trait CanRetry {
87    /// Returns true if the operation can be retried safely.
88    fn can_retry(&self) -> bool;
89
90    /// Returns true if this error represents a "no available addresses" condition.
91    ///
92    /// When all addresses have been banned due to errors, the client returns this error.
93    /// Retry logic uses this to return the last meaningful error instead of this one.
94    fn is_no_available_addresses(&self) -> bool {
95        false
96    }
97
98    /// If this error is a gRPC `ResourceExhausted` (Envoy rate-limit) that
99    /// carries a `RateLimit-Reset` metadata header, returns the server-advertised
100    /// ban duration (clamped to a safe range).  Returns `None` for all other
101    /// errors and for rate-limit errors that carry no usable header (the caller
102    /// falls back to the normal exponential ban ladder in that case).
103    fn rate_limit_ban_duration(&self) -> Option<std::time::Duration> {
104        None
105    }
106
107    /// Get boolean flag that indicates if the error is retryable.
108    ///
109    /// Deprecated in favor of [CanRetry::can_retry].
110    #[deprecated = "Use !can_retry() instead"]
111    fn is_node_failure(&self) -> bool {
112        !self.can_retry()
113    }
114}