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