nyquest_interface/async/
any.rs

1//! Type-erased async client interface traits.
2//!
3//! This module provides trait definitions for type-erased asynchronous HTTP client
4//! implementations, allowing different backend implementations to be used interchangeably.
5//!
6//! The traits in this module are automatically implemented for types that implement the
7//! corresponding traits from the `async::backend` module, so backend developers don't need
8//! to implement them directly.
9
10use std::any::Any;
11use std::fmt;
12
13use futures_core::future::BoxFuture;
14
15use super::backend::AsyncResponse;
16use super::Request;
17use crate::client::{BuildClientResult, ClientOptions};
18use crate::Result;
19
20/// Trait for type-erased async backend implementations.
21///
22/// Automatically implemented for types implementing `AsyncBackend`.
23pub trait AnyAsyncBackend: Send + Sync + 'static {
24    /// Creates a new async client with the given options.
25    fn create_async_client(
26        &self,
27        options: ClientOptions,
28    ) -> BoxFuture<BuildClientResult<Box<dyn AnyAsyncClient>>>;
29}
30
31/// Trait for type-erased async HTTP clients.
32///
33/// Automatically implemented for types implementing `AsyncClient`.
34pub trait AnyAsyncClient: Any + Send + Sync + 'static {
35    /// Provides a textual description of this client.
36    fn describe(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
37    /// Creates a cloned boxed version of this client.
38    fn clone_boxed(&self) -> Box<dyn AnyAsyncClient>;
39    /// Sends an HTTP request and returns the response.
40    fn request(&self, req: Request) -> BoxFuture<Result<Box<dyn AnyAsyncResponse>>>;
41}
42
43/// Trait for type-erased async HTTP responses.
44///
45/// Automatically implemented for types implementing `AsyncResponse`.
46pub trait AnyAsyncResponse: Any + Send + Sync + 'static {
47    /// Provides a textual description of this response.
48    fn describe(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
49    /// Returns the HTTP status code of this response.
50    fn status(&self) -> u16;
51    /// Returns the content-length of the response body, if known.
52    fn content_length(&self) -> Option<u64>;
53    /// Gets all values for the specified header.
54    fn get_header(&self, header: &str) -> Result<Vec<String>>;
55    /// Reads the response body as text.
56    fn text(&mut self) -> BoxFuture<Result<String>>;
57    /// Reads the response body as bytes.
58    fn bytes(&mut self) -> BoxFuture<Result<Vec<u8>>>;
59}
60
61// These implementations allow backend types implementing the base traits
62// to be used with the type-erased trait system automatically.
63
64impl<R> AnyAsyncResponse for R
65where
66    R: AsyncResponse,
67{
68    fn status(&self) -> u16 {
69        AsyncResponse::status(self)
70    }
71
72    fn content_length(&self) -> Option<u64> {
73        AsyncResponse::content_length(self)
74    }
75
76    fn get_header(&self, header: &str) -> Result<Vec<String>> {
77        AsyncResponse::get_header(self, header)
78    }
79
80    fn text(&mut self) -> BoxFuture<Result<String>> {
81        Box::pin(AsyncResponse::text(self))
82    }
83
84    fn bytes(&mut self) -> BoxFuture<Result<Vec<u8>>> {
85        Box::pin(AsyncResponse::bytes(self))
86    }
87
88    fn describe(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89        AsyncResponse::describe(self, f)
90    }
91}
92
93impl<A> AnyAsyncBackend for A
94where
95    A: super::backend::AsyncBackend,
96    A::AsyncClient: super::backend::AsyncClient,
97{
98    fn create_async_client(
99        &self,
100        options: ClientOptions,
101    ) -> BoxFuture<BuildClientResult<Box<dyn AnyAsyncClient>>> {
102        Box::pin(async {
103            super::backend::AsyncBackend::create_async_client(self, options)
104                .await
105                .map(|client| Box::new(client) as Box<dyn AnyAsyncClient>)
106        }) as _
107    }
108}
109
110impl<A> AnyAsyncClient for A
111where
112    A: super::backend::AsyncClient,
113{
114    fn describe(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115        super::backend::AsyncClient::describe(self, f)
116    }
117
118    fn clone_boxed(&self) -> Box<dyn AnyAsyncClient> {
119        Box::new(self.clone())
120    }
121
122    fn request(&self, req: Request) -> BoxFuture<Result<Box<dyn AnyAsyncResponse>>> {
123        Box::pin(async {
124            self.request(req)
125                .await
126                .map(|res| Box::new(res) as Box<dyn AnyAsyncResponse>)
127        }) as _
128    }
129}