1use crate::{client::*, error::*};
6
7#[cfg(not(feature = "wasm"))]
8use tokio::{runtime::Runtime, sync::broadcast::channel};
9
10#[cfg(not(feature = "wasm"))]
11use std::collections::HashSet;
12
13use std::{
14 collections::HashMap,
15 sync::{Arc, RwLock},
16 time::Duration,
17};
18
19const DEFAULT_REMOTE_POW_TIMEOUT: Duration = Duration::from_secs(100);
20pub(crate) const GET_API_TIMEOUT: Duration = Duration::from_secs(15);
21#[cfg(not(feature = "wasm"))]
22const NODE_SYNC_INTERVAL: Duration = Duration::from_secs(60);
23pub const TIPS_INTERVAL: u64 = 15;
25const DEFAULT_MIN_POW: f64 = 4000f64;
26const DEFAULT_BECH32_HRP: &str = "iota";
27
28#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
30pub struct NetworkInfo {
31 pub network: Option<String>,
33 #[serde(rename = "networkId")]
35 pub network_id: Option<u64>,
36 #[serde(rename = "bech32HRP")]
38 pub bech32_hrp: String,
39 #[serde(rename = "minPoWScore")]
41 pub min_pow_score: f64,
42 #[serde(rename = "localPow")]
44 pub local_pow: bool,
45 #[serde(rename = "fallbackToLocalPow")]
47 pub fallback_to_local_pow: bool,
48 #[serde(rename = "tipsInterval")]
50 pub tips_interval: u64,
51}
52
53#[derive(Clone)]
55pub struct ClientBuilder {
56 node_manager_builder: crate::node_manager::NodeManagerBuilder,
57 #[cfg(not(feature = "wasm"))]
58 node_sync_interval: Duration,
59 #[cfg(not(feature = "wasm"))]
60 node_sync_enabled: bool,
61 #[cfg(feature = "mqtt")]
62 broker_options: BrokerOptions,
63 pub(crate) network_info: NetworkInfo,
64 request_timeout: Duration,
65 api_timeout: HashMap<Api, Duration>,
66 offline: bool,
67}
68
69impl Default for NetworkInfo {
70 fn default() -> Self {
71 Self {
72 network: None,
73 network_id: None,
74 min_pow_score: DEFAULT_MIN_POW,
75 #[cfg(not(feature = "wasm"))]
76 local_pow: true,
77 #[cfg(feature = "wasm")]
78 local_pow: false,
79 fallback_to_local_pow: true,
80 bech32_hrp: DEFAULT_BECH32_HRP.into(),
81 tips_interval: TIPS_INTERVAL,
82 }
83 }
84}
85
86impl Default for ClientBuilder {
87 fn default() -> Self {
88 Self {
89 node_manager_builder: crate::node_manager::NodeManager::builder(),
90 #[cfg(not(feature = "wasm"))]
91 node_sync_interval: NODE_SYNC_INTERVAL,
92 #[cfg(not(feature = "wasm"))]
93 node_sync_enabled: true,
94 #[cfg(feature = "mqtt")]
95 broker_options: Default::default(),
96 network_info: NetworkInfo::default(),
97 request_timeout: GET_API_TIMEOUT,
98 api_timeout: Default::default(),
99 offline: false,
100 }
101 }
102}
103
104impl ClientBuilder {
105 pub fn new() -> Self {
107 Default::default()
108 }
109
110 pub fn with_node(mut self, url: &str) -> Result<Self> {
112 self.node_manager_builder = self.node_manager_builder.with_node(url)?;
113 Ok(self)
114 }
115
116 pub fn with_primary_node(
118 mut self,
119 url: &str,
120 jwt: Option<String>,
121 basic_auth_name_pwd: Option<(&str, &str)>,
122 ) -> Result<Self> {
123 self.node_manager_builder = self
124 .node_manager_builder
125 .with_primary_node(url, jwt, basic_auth_name_pwd)?;
126 Ok(self)
127 }
128
129 pub fn with_primary_pow_node(
132 mut self,
133 url: &str,
134 jwt: Option<String>,
135 basic_auth_name_pwd: Option<(&str, &str)>,
136 ) -> Result<Self> {
137 self.node_manager_builder = self
138 .node_manager_builder
139 .with_primary_pow_node(url, jwt, basic_auth_name_pwd)?;
140 Ok(self)
141 }
142
143 pub fn with_permanode(
145 mut self,
146 url: &str,
147 jwt: Option<String>,
148 basic_auth_name_pwd: Option<(&str, &str)>,
149 ) -> Result<Self> {
150 self.node_manager_builder = self
151 .node_manager_builder
152 .with_permanode(url, jwt, basic_auth_name_pwd)?;
153 Ok(self)
154 }
155
156 pub fn with_node_auth(
158 mut self,
159 url: &str,
160 jwt: Option<String>,
161 basic_auth_name_pwd: Option<(&str, &str)>,
162 ) -> Result<Self> {
163 self.node_manager_builder = self
164 .node_manager_builder
165 .with_node_auth(url, jwt, basic_auth_name_pwd)?;
166 Ok(self)
167 }
168
169 pub fn with_nodes(mut self, urls: &[&str]) -> Result<Self> {
171 self.node_manager_builder = self.node_manager_builder.with_nodes(urls)?;
172 Ok(self)
173 }
174
175 pub fn with_node_sync_interval(mut self, node_sync_interval: Duration) -> Self {
177 self.node_manager_builder = self.node_manager_builder.with_node_sync_interval(node_sync_interval);
178 self
179 }
180
181 pub fn with_node_sync_disabled(mut self) -> Self {
184 self.node_manager_builder = self.node_manager_builder.with_node_sync_disabled();
185 self
186 }
187
188 pub fn with_offline_mode(mut self) -> Self {
190 self.offline = true;
191 self
192 }
193
194 pub async fn with_node_pool_urls(mut self, node_pool_urls: &[String]) -> Result<Self> {
196 self.node_manager_builder = self.node_manager_builder.with_node_pool_urls(node_pool_urls).await?;
197 Ok(self)
198 }
199
200 pub fn with_quorum(mut self, quorum: bool) -> Self {
202 self.node_manager_builder = self.node_manager_builder.with_quorum(quorum);
203 self
204 }
205
206 pub fn with_quorum_size(mut self, quorum_size: usize) -> Self {
208 self.node_manager_builder = self.node_manager_builder.with_quorum_size(quorum_size);
209 self
210 }
211
212 pub fn with_quorum_threshold(mut self, threshold: usize) -> Self {
214 let threshold = if threshold > 100 { 100 } else { threshold };
215 self.node_manager_builder = self.node_manager_builder.with_quorum_threshold(threshold);
216 self
217 }
218
219 pub fn with_network(mut self, network: &str) -> Self {
224 self.network_info.network.replace(network.into());
225 self
226 }
227
228 #[cfg(feature = "mqtt")]
230 pub fn with_mqtt_broker_options(mut self, options: BrokerOptions) -> Self {
231 self.broker_options = options;
232 self
233 }
234
235 pub fn with_local_pow(mut self, local: bool) -> Self {
237 self.network_info.local_pow = local;
238 self
239 }
240
241 pub fn with_fallback_to_local_pow(mut self, fallback_to_local_pow: bool) -> Self {
243 self.network_info.fallback_to_local_pow = fallback_to_local_pow;
244 self
245 }
246
247 pub fn with_tips_interval(mut self, tips_interval: u64) -> Self {
249 self.network_info.tips_interval = tips_interval;
250 self
251 }
252
253 pub fn with_request_timeout(mut self, timeout: Duration) -> Self {
255 self.request_timeout = timeout;
256 self
257 }
258
259 pub fn with_api_timeout(mut self, api: Api, timeout: Duration) -> Self {
261 self.api_timeout.insert(api, timeout);
262 self
263 }
264
265 pub async fn finish(mut self) -> Result<Client> {
267 if !self.offline {
269 self.node_manager_builder = self.node_manager_builder.add_default_nodes(&self.network_info).await?;
270 if self.node_manager_builder.nodes.is_empty() && self.node_manager_builder.primary_node.is_none() {
272 return Err(Error::MissingParameter("Node"));
273 }
274 }
275 let network_info = Arc::new(RwLock::new(self.network_info));
276 let nodes = self.node_manager_builder.nodes.clone();
277 #[cfg(not(feature = "wasm"))]
278 let node_sync_interval = self.node_sync_interval;
279 #[cfg(feature = "wasm")]
280 let (sync, network_info) = (Arc::new(RwLock::new(nodes)), network_info);
281 #[cfg(not(feature = "wasm"))]
282 let (runtime, sync, sync_kill_sender, network_info) = if self.node_sync_enabled {
283 let sync = Arc::new(RwLock::new(HashSet::new()));
284 let sync_ = sync.clone();
285 let network_info_ = network_info.clone();
286 let (sync_kill_sender, sync_kill_receiver) = channel(1);
287 let runtime = std::thread::spawn(move || {
288 let runtime = Runtime::new().expect("Failed to create Tokio runtime");
289 runtime.block_on(Client::sync_nodes(&sync_, &nodes, &network_info_));
290 Client::start_sync_process(
291 &runtime,
292 sync_,
293 nodes,
294 node_sync_interval,
295 network_info_,
296 sync_kill_receiver,
297 );
298 runtime
299 })
300 .join()
301 .expect("failed to init node syncing process");
302 (Some(runtime), sync, Some(sync_kill_sender), network_info)
303 } else {
304 (None, Arc::new(RwLock::new(nodes)), None, network_info)
305 };
306
307 let mut api_timeout = HashMap::new();
308 api_timeout.insert(
309 Api::GetInfo,
310 self.api_timeout.remove(&Api::GetInfo).unwrap_or(self.request_timeout),
311 );
312 api_timeout.insert(
313 Api::GetPeers,
314 self.api_timeout.remove(&Api::GetPeers).unwrap_or(self.request_timeout),
315 );
316 api_timeout.insert(
317 Api::GetHealth,
318 self.api_timeout.remove(&Api::GetHealth).unwrap_or(self.request_timeout),
319 );
320 api_timeout.insert(
321 Api::GetMilestone,
322 self.api_timeout
323 .remove(&Api::GetMilestone)
324 .unwrap_or(self.request_timeout),
325 );
326 api_timeout.insert(
327 Api::GetBalance,
328 self.api_timeout
329 .remove(&Api::GetBalance)
330 .unwrap_or(self.request_timeout),
331 );
332 api_timeout.insert(
333 Api::GetMessage,
334 self.api_timeout
335 .remove(&Api::GetMessage)
336 .unwrap_or(self.request_timeout),
337 );
338 api_timeout.insert(
339 Api::GetTips,
340 self.api_timeout.remove(&Api::GetTips).unwrap_or(self.request_timeout),
341 );
342 api_timeout.insert(
343 Api::PostMessage,
344 self.api_timeout
345 .remove(&Api::PostMessage)
346 .unwrap_or(self.request_timeout),
347 );
348 api_timeout.insert(
349 Api::PostMessageWithRemotePow,
350 self.api_timeout
351 .remove(&Api::PostMessageWithRemotePow)
352 .unwrap_or(DEFAULT_REMOTE_POW_TIMEOUT),
353 );
354 api_timeout.insert(
355 Api::GetOutput,
356 self.api_timeout.remove(&Api::GetOutput).unwrap_or(self.request_timeout),
357 );
358
359 #[cfg(feature = "mqtt")]
360 let (mqtt_event_tx, mqtt_event_rx) = tokio::sync::watch::channel(MqttEvent::Connected);
361 let client = Client {
362 node_manager: self.node_manager_builder.build(sync),
363 #[cfg(not(feature = "wasm"))]
364 runtime,
365 #[cfg(not(feature = "wasm"))]
366 sync_kill_sender: sync_kill_sender.map(Arc::new),
367 #[cfg(feature = "mqtt")]
368 mqtt_client: None,
369 #[cfg(feature = "mqtt")]
370 mqtt_topic_handlers: Default::default(),
371 #[cfg(feature = "mqtt")]
372 broker_options: self.broker_options,
373 #[cfg(feature = "mqtt")]
374 mqtt_event_channel: (Arc::new(mqtt_event_tx), mqtt_event_rx),
375 network_info,
376 request_timeout: self.request_timeout,
377 api_timeout,
378 };
379 Ok(client)
380 }
381}