Skip to main content

xmtp_proto/traits/
boxed_client.rs

1/// a boxed version of [`Client`]
2pub type BoxClient = Box<dyn BoxClientT>;
3
4/// An owned transport shared through an [`Arc`].
5///
6/// The named type keeps the trait object's lifetime out of generic async
7/// return types. This lets those futures retain their native `Send` bound.
8/// Clones share the same transport allocation and connection state.
9#[derive(Clone)]
10pub struct ArcClient(Arc<dyn BoxClientT>);
11
12use bytes::Bytes;
13use http::{request, uri::PathAndQuery};
14use std::sync::Arc;
15
16use crate::api::{ApiClientError, BytesStream, IsConnectedCheck};
17
18use super::Client;
19
20struct BoxedClient<C: ?Sized> {
21    inner: C,
22}
23
24impl<C> BoxedClient<C> {
25    pub fn new(client: C) -> Self {
26        Self { inner: client }
27    }
28}
29
30pub trait BoxClientT: Client + IsConnectedCheck {}
31
32impl<T> BoxClientT for T where T: ?Sized + IsConnectedCheck + Client {}
33
34impl std::ops::Deref for ArcClient {
35    type Target = Arc<dyn BoxClientT>;
36
37    fn deref(&self) -> &Self::Target {
38        &self.0
39    }
40}
41
42impl From<Arc<dyn BoxClientT>> for ArcClient {
43    fn from(client: Arc<dyn BoxClientT>) -> Self {
44        Self(client)
45    }
46}
47
48#[xmtp_common::async_trait]
49impl Client for ArcClient {
50    fn host(&self) -> &str {
51        self.0.as_ref().host()
52    }
53
54    fn has_credential_source(&self) -> bool {
55        self.0.as_ref().has_credential_source()
56    }
57
58    async fn request(
59        &self,
60        request: request::Builder,
61        path: PathAndQuery,
62        body: Bytes,
63    ) -> Result<http::Response<Bytes>, ApiClientError> {
64        self.0.as_ref().request(request, path, body).await
65    }
66
67    async fn stream(
68        &self,
69        request: request::Builder,
70        path: PathAndQuery,
71        body: Bytes,
72    ) -> Result<http::Response<BytesStream>, ApiClientError> {
73        self.0.as_ref().stream(request, path, body).await
74    }
75
76    async fn bidi_stream(
77        &self,
78        request: request::Builder,
79        path: PathAndQuery,
80        body: xmtp_common::BoxDynStream<'static, Bytes>,
81    ) -> Result<http::Response<BytesStream>, ApiClientError> {
82        self.0.as_ref().bidi_stream(request, path, body).await
83    }
84
85    fn fake_stream(&self) -> http::Response<BytesStream> {
86        self.0.as_ref().fake_stream()
87    }
88}
89
90#[xmtp_common::async_trait]
91impl IsConnectedCheck for ArcClient {
92    async fn is_connected(&self) -> bool {
93        self.0.as_ref().is_connected().await
94    }
95}
96
97#[xmtp_common::async_trait]
98impl<C> Client for BoxedClient<C>
99where
100    C: Client,
101{
102    fn host(&self) -> &str {
103        self.inner.host()
104    }
105
106    fn has_credential_source(&self) -> bool {
107        self.inner.has_credential_source()
108    }
109
110    async fn request(
111        &self,
112        request: request::Builder,
113        path: PathAndQuery,
114        body: Bytes,
115    ) -> Result<http::Response<Bytes>, ApiClientError> {
116        self.inner.request(request, path, body).await
117    }
118
119    async fn stream(
120        &self,
121        request: request::Builder,
122        path: http::uri::PathAndQuery,
123        body: Bytes,
124    ) -> Result<http::Response<BytesStream>, ApiClientError> {
125        self.inner.stream(request, path, body).await
126    }
127
128    async fn bidi_stream(
129        &self,
130        request: request::Builder,
131        path: http::uri::PathAndQuery,
132        body: xmtp_common::BoxDynStream<'static, Bytes>,
133    ) -> Result<http::Response<BytesStream>, ApiClientError> {
134        self.inner.bidi_stream(request, path, body).await
135    }
136}
137
138pub trait ToBoxedClient {
139    fn boxed(self) -> BoxClient;
140    /// Store this transport once and return a cloneable shared owner.
141    fn arced(self) -> ArcClient;
142}
143
144impl<C> ToBoxedClient for C
145where
146    C: Client + IsConnectedCheck + 'static,
147{
148    fn boxed(self) -> BoxClient {
149        Box::new(BoxedClient::new(self))
150    }
151    fn arced(self) -> ArcClient {
152        ArcClient(Arc::new(BoxedClient::new(self)))
153    }
154}
155
156#[xmtp_common::async_trait]
157impl<T> IsConnectedCheck for BoxedClient<T>
158where
159    T: ?Sized + IsConnectedCheck,
160{
161    /// Check if a client is connected
162    async fn is_connected(&self) -> bool {
163        self.inner.is_connected().await
164    }
165}