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::{system_snapshot::SystemSnapshotter, DeviceId, DistinctId, Map};
10use crate::{Recorder, Worker};
11
12#[derive(Default)]
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<Map>,
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 set_anonymous_distinct_id(
43        mut self,
44        anonymous_distinct_id: Option<AnonymousDistinctId>,
45    ) -> Self {
46        self.anonymous_distinct_id = anonymous_distinct_id;
47        self
48    }
49
50    pub fn set_distinct_id(mut self, distinct_id: Option<DistinctId>) -> Self {
51        self.distinct_id = distinct_id;
52        self
53    }
54
55    pub fn set_device_id(mut self, device_id: Option<DeviceId>) -> Self {
56        self.device_id = device_id;
57        self
58    }
59
60    pub fn set_facts(mut self, facts: Option<Map>) -> Self {
61        self.facts = facts;
62        self
63    }
64
65    pub fn set_groups(mut self, groups: Option<Map>) -> Self {
66        self.groups = groups;
67        self
68    }
69
70    pub fn add_fact(
71        mut self,
72        key: impl Into<String> + std::fmt::Debug,
73        value: impl Into<serde_json::Value>,
74    ) -> Self {
75        self.facts
76            .get_or_insert_with(Default::default)
77            .insert(key.into(), value.into());
78        self
79    }
80
81    pub fn set_endpoint(mut self, endpoint: Option<String>) -> Self {
82        self.endpoint = endpoint;
83        self
84    }
85
86    /// Set whether reporting is enabled or disabled.
87    /// Reporting is enabled by default, but this function can be used in a pipeline for easy configuration:
88    ///
89    /// ```rust
90    /// use detsys_ids_client::builder;
91    ///
92    /// struct Cli {
93    ///   no_telemetry: bool,
94    /// }
95    ///
96    ///
97    /// # tokio_test::block_on(async {
98    ///
99    /// let cli = Cli { no_telemetry: false, };
100    ///
101    /// let (recorder, worker) = builder!()
102    ///   .set_enable_reporting(!cli.no_telemetry)
103    ///   .build_or_default()
104    ///   .await;
105    /// # })
106    /// ```
107    pub fn set_enable_reporting(mut self, enable_reporting: bool) -> Self {
108        self.enable_reporting = enable_reporting;
109        self
110    }
111
112    pub fn set_timeout(mut self, duration: Option<Duration>) -> Self {
113        self.timeout = duration;
114        self
115    }
116
117    pub fn set_certificate(mut self, certificate: Option<Certificate>) -> Self {
118        self.certificate = certificate;
119        self
120    }
121
122    pub fn set_proxy(mut self, proxy: Option<Url>) -> Self {
123        self.proxy = proxy;
124        self
125    }
126
127    #[tracing::instrument(skip(self))]
128    pub async fn try_build(mut self) -> Result<(Recorder, Worker), TransportsError> {
129        let transport = self.transport().await?;
130
131        Ok(self
132            .build_with(
133                transport,
134                crate::system_snapshot::Generic::default(),
135                crate::storage::Generic::default(),
136            )
137            .await)
138    }
139
140    #[tracing::instrument(skip(self))]
141    pub async fn build_or_default(mut self) -> (Recorder, Worker) {
142        let transport = self.transport_or_default().await;
143
144        self.build_with(
145            transport,
146            crate::system_snapshot::Generic::default(),
147            crate::storage::Generic::default(),
148        )
149        .await
150    }
151
152    #[tracing::instrument(skip(self, snapshotter, storage))]
153    pub async fn try_build_with<S: SystemSnapshotter, P: Storage>(
154        mut self,
155        snapshotter: S,
156        storage: P,
157    ) -> Result<(Recorder, Worker), TransportsError> {
158        let transport = self.transport().await?;
159
160        Ok(self.build_with(transport, snapshotter, storage).await)
161    }
162
163    #[tracing::instrument(skip(self, snapshotter, storage))]
164    pub async fn build_or_default_with<S: SystemSnapshotter, P: Storage>(
165        mut self,
166        snapshotter: S,
167        storage: P,
168    ) -> (Recorder, Worker) {
169        let transport = self.transport_or_default().await;
170
171        self.build_with(transport, snapshotter, storage).await
172    }
173
174    #[tracing::instrument(skip(self, transport, snapshotter, storage))]
175    async fn build_with<S: SystemSnapshotter, P: Storage>(
176        &mut self,
177        transport: crate::transport::Transports,
178        snapshotter: S,
179        storage: P,
180    ) -> (Recorder, Worker) {
181        Worker::new(
182            self.anonymous_distinct_id.take(),
183            self.distinct_id.take(),
184            self.device_id.take(),
185            self.facts.take(),
186            self.groups.take(),
187            snapshotter,
188            storage,
189            transport,
190        )
191        .await
192    }
193
194    async fn transport_or_default(&mut self) -> crate::transport::Transports {
195        match self.transport().await {
196            Ok(t) => {
197                return t;
198            }
199            Err(e) => {
200                tracing::warn!(%e, "Failed to construct the transport as configured, falling back to the default");
201            }
202        }
203
204        match crate::transport::Transports::try_new(
205            None,
206            self.timeout
207                .take()
208                .unwrap_or_else(|| Duration::from_secs(3)),
209            None,
210            None,
211        )
212        .await
213        {
214            Ok(t) => {
215                return t;
216            }
217            Err(e) => {
218                tracing::warn!(%e, "Failed to construct the default transport, falling back to none");
219            }
220        }
221
222        crate::transport::Transports::none()
223    }
224
225    async fn transport(&mut self) -> Result<crate::transport::Transports, TransportsError> {
226        if self.enable_reporting {
227            crate::transport::Transports::try_new(
228                self.endpoint.take(),
229                self.timeout.unwrap_or_else(|| Duration::from_secs(3)),
230                self.certificate.take(),
231                self.proxy.take(),
232            )
233            .await
234        } else {
235            Ok(crate::transport::Transports::none())
236        }
237    }
238}