detsys_ids_client/
builder.rs

1use std::time::Duration;
2
3use reqwest::Certificate;
4use url::Url;
5
6use crate::identity::AnonymousDistinctId;
7use crate::storage::Storage;
8use crate::transport::TransportsError;
9use crate::{DeviceId, DistinctId, Map, system_snapshot::SystemSnapshotter};
10use crate::{Groups, Recorder, Worker};
11
12#[derive(Default, Clone)]
13pub struct Builder {
14    device_id: Option<DeviceId>,
15    distinct_id: Option<DistinctId>,
16    anonymous_distinct_id: Option<AnonymousDistinctId>,
17    enable_reporting: bool,
18    endpoint: Option<String>,
19    facts: Option<Map>,
20    groups: Option<Groups>,
21    proxy: Option<Url>,
22    certificate: Option<Certificate>,
23    timeout: Option<Duration>,
24}
25
26impl Builder {
27    pub fn new() -> Self {
28        Builder {
29            device_id: None,
30            distinct_id: None,
31            anonymous_distinct_id: None,
32            enable_reporting: true,
33            endpoint: None,
34            facts: None,
35            groups: None,
36            proxy: None,
37            certificate: None,
38            timeout: None,
39        }
40    }
41
42    pub fn anonymous_distinct_id(
43        mut self,
44        anonymous_distinct_id: Option<AnonymousDistinctId>,
45    ) -> Self {
46        self.set_anonymous_distinct_id(anonymous_distinct_id);
47        self
48    }
49
50    pub fn set_anonymous_distinct_id(
51        &mut self,
52        anonymous_distinct_id: Option<AnonymousDistinctId>,
53    ) -> &mut Self {
54        self.anonymous_distinct_id = anonymous_distinct_id;
55        self
56    }
57
58    pub fn distinct_id(mut self, distinct_id: Option<DistinctId>) -> Self {
59        self.set_distinct_id(distinct_id);
60        self
61    }
62
63    pub fn set_distinct_id(&mut self, distinct_id: Option<DistinctId>) -> &mut Self {
64        self.distinct_id = distinct_id;
65        self
66    }
67
68    pub fn device_id(mut self, device_id: Option<DeviceId>) -> Self {
69        self.set_device_id(device_id);
70        self
71    }
72
73    pub fn set_device_id(&mut self, device_id: Option<DeviceId>) -> &mut Self {
74        self.device_id = device_id;
75        self
76    }
77
78    pub fn facts(mut self, facts: Option<Map>) -> Self {
79        self.set_facts(facts);
80        self
81    }
82
83    pub fn set_facts(&mut self, facts: Option<Map>) -> &mut Self {
84        self.facts = facts;
85        self
86    }
87
88    pub fn groups(mut self, groups: Option<Groups>) -> Self {
89        self.set_groups(groups);
90        self
91    }
92
93    pub fn set_groups(&mut self, groups: Option<Groups>) -> &mut Self {
94        self.groups = groups;
95        self
96    }
97
98    pub fn fact(mut self, key: impl Into<String>, value: impl Into<serde_json::Value>) -> Self {
99        self.set_fact(key, value);
100        self
101    }
102
103    pub fn set_fact(
104        &mut self,
105        key: impl Into<String>,
106        value: impl Into<serde_json::Value>,
107    ) -> &mut Self {
108        self.facts
109            .get_or_insert_with(Default::default)
110            .insert(key.into(), value.into());
111        self
112    }
113
114    pub fn endpoint(mut self, endpoint: Option<String>) -> Self {
115        self.set_endpoint(endpoint);
116        self
117    }
118
119    pub fn set_endpoint(&mut self, endpoint: Option<String>) -> &mut Self {
120        self.endpoint = endpoint;
121        self
122    }
123
124    /// Set whether reporting is enabled or disabled.
125    /// Reporting is enabled by default, but this function can be used in a pipeline for easy configuration:
126    ///
127    /// ```rust
128    /// use detsys_ids_client::builder;
129    ///
130    /// struct Cli {
131    ///   no_telemetry: bool,
132    /// }
133    ///
134    ///
135    /// # tokio_test::block_on(async {
136    ///
137    /// let cli = Cli { no_telemetry: false, };
138    ///
139    /// let (recorder, worker) = builder!()
140    ///   .enable_reporting(!cli.no_telemetry)
141    ///   .build_or_default()
142    ///   .await;
143    /// # })
144    /// ```
145    pub fn enable_reporting(mut self, enable_reporting: bool) -> Self {
146        self.set_enable_reporting(enable_reporting);
147        self
148    }
149
150    pub fn set_enable_reporting(&mut self, enable_reporting: bool) -> &mut Self {
151        self.enable_reporting = enable_reporting;
152        self
153    }
154
155    pub fn timeout(mut self, duration: Option<Duration>) -> Self {
156        self.set_timeout(duration);
157        self
158    }
159
160    pub fn set_timeout(&mut self, duration: Option<Duration>) -> &mut Self {
161        self.timeout = duration;
162        self
163    }
164
165    pub fn certificate(mut self, certificate: Option<Certificate>) -> Self {
166        self.set_certificate(certificate);
167        self
168    }
169
170    pub fn set_certificate(&mut self, certificate: Option<Certificate>) -> &mut Self {
171        self.certificate = certificate;
172        self
173    }
174
175    pub fn proxy(mut self, proxy: Option<Url>) -> Self {
176        self.set_proxy(proxy);
177        self
178    }
179
180    pub fn set_proxy(&mut self, proxy: Option<Url>) -> &mut Self {
181        self.proxy = proxy;
182        self
183    }
184
185    #[tracing::instrument(skip(self))]
186    pub async fn try_build(mut self) -> Result<(Recorder, Worker), TransportsError> {
187        let transport = self.transport().await?;
188
189        Ok(self
190            .build_with(
191                transport,
192                crate::system_snapshot::Generic::default(),
193                crate::storage::Generic::default(),
194            )
195            .await)
196    }
197
198    #[tracing::instrument(skip(self))]
199    pub async fn build_or_default(mut self) -> (Recorder, Worker) {
200        let transport = self.transport_or_default().await;
201
202        self.build_with(
203            transport,
204            crate::system_snapshot::Generic::default(),
205            crate::storage::Generic::default(),
206        )
207        .await
208    }
209
210    #[tracing::instrument(skip(self, snapshotter, storage))]
211    pub async fn try_build_with<S: SystemSnapshotter, P: Storage>(
212        mut self,
213        snapshotter: S,
214        storage: P,
215    ) -> Result<(Recorder, Worker), TransportsError> {
216        let transport = self.transport().await?;
217
218        Ok(self.build_with(transport, snapshotter, storage).await)
219    }
220
221    #[tracing::instrument(skip(self, snapshotter, storage))]
222    pub async fn build_or_default_with<S: SystemSnapshotter, P: Storage>(
223        mut self,
224        snapshotter: S,
225        storage: P,
226    ) -> (Recorder, Worker) {
227        let transport = self.transport_or_default().await;
228
229        self.build_with(transport, snapshotter, storage).await
230    }
231
232    #[tracing::instrument(skip(self, transport, snapshotter, storage))]
233    async fn build_with<S: SystemSnapshotter, P: Storage>(
234        &mut self,
235        transport: crate::transport::Transports,
236        snapshotter: S,
237        storage: P,
238    ) -> (Recorder, Worker) {
239        Worker::new(
240            self.anonymous_distinct_id.take(),
241            self.distinct_id.take(),
242            self.device_id.take(),
243            self.facts.take(),
244            self.groups.take(),
245            snapshotter,
246            storage,
247            transport,
248        )
249        .await
250    }
251
252    async fn transport_or_default(&mut self) -> crate::transport::Transports {
253        match self.transport().await {
254            Ok(t) => {
255                return t;
256            }
257            Err(e) => {
258                tracing::warn!(%e, "Failed to construct the transport as configured, falling back to the default");
259            }
260        }
261
262        match crate::transport::Transports::try_new(
263            None,
264            self.timeout
265                .take()
266                .unwrap_or_else(|| Duration::from_secs(3)),
267            None,
268            None,
269        )
270        .await
271        {
272            Ok(t) => {
273                return t;
274            }
275            Err(e) => {
276                tracing::warn!(%e, "Failed to construct the default transport, falling back to none");
277            }
278        }
279
280        crate::transport::Transports::none()
281    }
282
283    async fn transport(&mut self) -> Result<crate::transport::Transports, TransportsError> {
284        if self.enable_reporting {
285            crate::transport::Transports::try_new(
286                self.endpoint.take(),
287                self.timeout.unwrap_or_else(|| Duration::from_secs(3)),
288                self.certificate.take(),
289                self.proxy.take(),
290            )
291            .await
292        } else {
293            Ok(crate::transport::Transports::none())
294        }
295    }
296}