bonsaidb_client/
builder.rs

1use std::collections::HashMap;
2use std::marker::PhantomData;
3use std::sync::Arc;
4use std::time::Duration;
5
6use bonsaidb_core::api;
7use bonsaidb_core::api::ApiName;
8use bonsaidb_core::networking::CURRENT_PROTOCOL_VERSION;
9#[cfg(not(target_arch = "wasm32"))]
10use fabruic::Certificate;
11#[cfg(not(target_arch = "wasm32"))]
12use tokio::runtime::Handle;
13use url::Url;
14
15use crate::client::{AnyApiCallback, ApiCallback};
16#[cfg(not(target_arch = "wasm32"))]
17use crate::BlockingClient;
18use crate::{AsyncClient, Error};
19
20pub struct Async;
21#[cfg(not(target_arch = "wasm32"))]
22pub struct Blocking;
23
24/// Builder for a [`BlockingClient`] or an [`AsyncClient`].
25#[must_use]
26pub struct Builder<AsyncMode> {
27    url: Url,
28    protocol_version: &'static str,
29    custom_apis: HashMap<ApiName, Option<Arc<dyn AnyApiCallback>>>,
30    connect_timeout: Option<Duration>,
31    request_timeout: Option<Duration>,
32    #[cfg(not(target_arch = "wasm32"))]
33    certificate: Option<fabruic::Certificate>,
34    #[cfg(not(target_arch = "wasm32"))]
35    tokio: Option<Handle>,
36    mode: PhantomData<AsyncMode>,
37}
38
39impl<AsyncMode> Builder<AsyncMode> {
40    /// Creates a new builder for a client connecting to `url`.
41    pub(crate) fn new(url: Url) -> Self {
42        Self {
43            url,
44            protocol_version: CURRENT_PROTOCOL_VERSION,
45            custom_apis: HashMap::new(),
46            request_timeout: None,
47            connect_timeout: None,
48            #[cfg(not(target_arch = "wasm32"))]
49            certificate: None,
50            #[cfg(not(target_arch = "wasm32"))]
51            tokio: None,
52            mode: PhantomData,
53        }
54    }
55
56    /// Specifies the tokio runtime this client should use for its async tasks.
57    /// If not specified, `Client` will try to acquire a handle via
58    /// `tokio::runtime::Handle::try_current()`.
59    #[cfg(not(target_arch = "wasm32"))]
60    #[allow(clippy::missing_const_for_fn)]
61    pub fn with_runtime(mut self, handle: Handle) -> Self {
62        self.tokio = Some(handle);
63        self
64    }
65
66    /// Enables using a [`Api`](api::Api) with this client. If you want to
67    /// receive out-of-band API requests, set a callback using
68    /// `with_custom_api_callback` instead.
69    pub fn with_api<Api: api::Api>(mut self) -> Self {
70        self.custom_apis.insert(Api::name(), None);
71        self
72    }
73
74    /// Enables using a [`Api`](api::Api) with this client. `callback` will be
75    /// invoked when custom API responses are received from the server.
76    pub fn with_api_callback<Api: api::Api>(mut self, callback: ApiCallback<Api>) -> Self {
77        self.custom_apis
78            .insert(Api::name(), Some(Arc::new(callback)));
79        self
80    }
81
82    /// Connects to a server using a pinned `certificate`. Only supported with BonsaiDb protocol-based connections.
83    #[cfg(not(target_arch = "wasm32"))]
84    #[allow(clippy::missing_const_for_fn)]
85    pub fn with_certificate(mut self, certificate: Certificate) -> Self {
86        self.certificate = Some(certificate);
87        self
88    }
89
90    /// Overrides the protocol version. Only for testing purposes.
91    #[cfg(feature = "test-util")]
92    #[allow(clippy::missing_const_for_fn)]
93    pub fn with_protocol_version(mut self, version: &'static str) -> Self {
94        self.protocol_version = version;
95        self
96    }
97
98    /// Sets the request timeout for the client.
99    ///
100    /// If not specified, requests will time out after 60 seconds.
101    pub fn with_request_timeout(mut self, timeout: impl Into<Duration>) -> Self {
102        self.request_timeout = Some(timeout.into());
103        self
104    }
105
106    /// Sets the connection timeout for the client.
107    ///
108    /// If not specified, the client will time out after 60 seconds if a
109    /// connection cannot be established.
110    pub fn with_connect_timeout(mut self, timeout: impl Into<Duration>) -> Self {
111        self.connect_timeout = Some(timeout.into());
112        self
113    }
114
115    fn finish_internal(self) -> Result<AsyncClient, Error> {
116        AsyncClient::new_from_parts(
117            self.url,
118            self.protocol_version,
119            self.custom_apis,
120            self.connect_timeout,
121            self.request_timeout,
122            #[cfg(not(target_arch = "wasm32"))]
123            self.certificate,
124            #[cfg(not(target_arch = "wasm32"))]
125            self.tokio.or_else(|| Handle::try_current().ok()),
126        )
127    }
128}
129
130#[cfg(not(target_arch = "wasm32"))]
131impl Builder<Blocking> {
132    /// Finishes building the client for use in a blocking (not async) context.
133    pub fn build(self) -> Result<BlockingClient, Error> {
134        self.finish_internal().map(BlockingClient)
135    }
136}
137
138impl Builder<Async> {
139    /// Finishes building the client for use in a tokio async context.
140    pub fn build(self) -> Result<AsyncClient, Error> {
141        self.finish_internal()
142    }
143}