1use std::{path::PathBuf, time::Duration};
2
3use opcua_core::config::{Config, ConfigError};
4use tracing::error;
5
6use super::{Client, ClientConfig, ClientEndpoint, ClientUserToken, ANONYMOUS_USER_TOKEN_ID};
7
8#[derive(Default)]
9pub struct ClientBuilder {
11 config: ClientConfig,
12}
13
14impl ClientBuilder {
15 pub fn new() -> ClientBuilder {
17 ClientBuilder::default()
18 }
19
20 pub fn from_config(path: impl Into<PathBuf>) -> Result<ClientBuilder, ConfigError> {
22 Ok(ClientBuilder {
23 config: ClientConfig::load(&path.into())?,
24 })
25 }
26
27 pub fn client(self) -> Result<Client, Vec<String>> {
32 if let Err(e) = self.config.validate() {
33 for err in &e {
34 error!("{err}");
35 }
36 Err(e)
37 } else {
38 Ok(Client::new(self.config))
39 }
40 }
41
42 pub fn config(&self) -> &ClientConfig {
44 &self.config
45 }
46
47 pub fn config_mut(&mut self) -> &mut ClientConfig {
49 &mut self.config
50 }
51
52 pub fn into_config(self) -> ClientConfig {
54 self.config
55 }
56
57 pub fn is_valid(&self) -> bool {
59 self.config.validate().is_ok()
60 }
61
62 pub fn application_name(mut self, application_name: impl Into<String>) -> Self {
64 self.config.application_name = application_name.into();
65 self
66 }
67
68 pub fn application_uri(mut self, application_uri: impl Into<String>) -> Self {
70 self.config.application_uri = application_uri.into();
71 self
72 }
73
74 pub fn product_uri(mut self, product_uri: impl Into<String>) -> Self {
76 self.config.product_uri = product_uri.into();
77 self
78 }
79
80 pub fn create_sample_keypair(mut self, create_sample_keypair: bool) -> Self {
83 self.config.create_sample_keypair = create_sample_keypair;
84 self
85 }
86
87 pub fn certificate_path(mut self, certificate_path: impl Into<PathBuf>) -> Self {
91 self.config.certificate_path = Some(certificate_path.into());
92 self
93 }
94
95 pub fn private_key_path(mut self, private_key_path: impl Into<PathBuf>) -> Self {
99 self.config.private_key_path = Some(private_key_path.into());
100 self
101 }
102
103 pub fn trust_server_certs(mut self, trust_server_certs: bool) -> Self {
108 self.config.trust_server_certs = trust_server_certs;
109 self
110 }
111
112 pub fn verify_server_certs(mut self, verify_server_certs: bool) -> Self {
117 self.config.verify_server_certs = verify_server_certs;
118 self
119 }
120
121 pub fn pki_dir(mut self, pki_dir: impl Into<PathBuf>) -> Self {
124 self.config.pki_dir = pki_dir.into();
125 self
126 }
127
128 pub fn preferred_locales(mut self, preferred_locales: Vec<String>) -> Self {
131 self.config.preferred_locales = preferred_locales;
132 self
133 }
134
135 pub fn default_endpoint(mut self, endpoint_id: impl Into<String>) -> Self {
137 self.config.default_endpoint = endpoint_id.into();
138 self
139 }
140
141 pub fn endpoint(mut self, endpoint_id: impl Into<String>, endpoint: ClientEndpoint) -> Self {
143 self.config.endpoints.insert(endpoint_id.into(), endpoint);
144 self
145 }
146
147 pub fn endpoints(mut self, endpoints: Vec<(impl Into<String>, ClientEndpoint)>) -> Self {
149 for e in endpoints {
150 self.config.endpoints.insert(e.0.into(), e.1);
151 }
152 self
153 }
154
155 pub fn user_token(
157 mut self,
158 user_token_id: impl Into<String>,
159 user_token: ClientUserToken,
160 ) -> Self {
161 let user_token_id = user_token_id.into();
162 if user_token_id == ANONYMOUS_USER_TOKEN_ID {
163 panic!("User token id {user_token_id} is reserved");
164 }
165 self.config.user_tokens.insert(user_token_id, user_token);
166 self
167 }
168
169 pub fn channel_lifetime(mut self, channel_lifetime: u32) -> Self {
173 self.config.channel_lifetime = channel_lifetime;
174 self
175 }
176
177 pub fn session_retry_limit(mut self, session_retry_limit: i32) -> Self {
183 if session_retry_limit < 0 && session_retry_limit != -1 {
184 panic!("Session retry limit must be -1, 0 or a positive number");
185 }
186 self.config.session_retry_limit = session_retry_limit;
187 self
188 }
189
190 pub fn session_retry_initial(mut self, session_retry_initial: Duration) -> Self {
192 self.config.session_retry_initial = session_retry_initial;
193 self
194 }
195
196 pub fn session_retry_max(mut self, session_retry_max: Duration) -> Self {
198 self.config.session_retry_max = session_retry_max;
199 self
200 }
201
202 pub fn keep_alive_interval(mut self, keep_alive_interval: Duration) -> Self {
205 self.config.keep_alive_interval = keep_alive_interval;
206 self
207 }
208
209 pub fn max_array_length(mut self, max_array_length: usize) -> Self {
211 self.config.decoding_options.max_array_length = max_array_length;
212 self
213 }
214
215 pub fn max_byte_string_length(mut self, max_byte_string_length: usize) -> Self {
217 self.config.decoding_options.max_byte_string_length = max_byte_string_length;
218 self
219 }
220
221 pub fn max_chunk_count(mut self, max_chunk_count: usize) -> Self {
223 self.config.decoding_options.max_chunk_count = max_chunk_count;
224 self
225 }
226
227 pub fn max_chunk_size(mut self, max_chunk_size: usize) -> Self {
229 self.config.decoding_options.max_chunk_size = max_chunk_size;
230 self
231 }
232
233 pub fn max_incoming_chunk_size(mut self, max_incoming_chunk_size: usize) -> Self {
235 self.config.decoding_options.max_incoming_chunk_size = max_incoming_chunk_size;
236 self
237 }
238
239 pub fn max_message_size(mut self, max_message_size: usize) -> Self {
241 self.config.decoding_options.max_message_size = max_message_size;
242 self
243 }
244
245 pub fn max_string_length(mut self, max_string_length: usize) -> Self {
247 self.config.decoding_options.max_string_length = max_string_length;
248 self
249 }
250
251 pub fn max_failed_keep_alive_count(mut self, max_failed_keep_alive_count: u64) -> Self {
257 self.config.max_failed_keep_alive_count = max_failed_keep_alive_count;
258 self
259 }
260
261 pub fn request_timeout(mut self, request_timeout: Duration) -> Self {
263 self.config.request_timeout = request_timeout;
264 self
265 }
266
267 pub fn publish_timeout(mut self, publish_timeout: Duration) -> Self {
269 self.config.publish_timeout = publish_timeout;
270 self
271 }
272
273 pub fn min_publish_interval(mut self, min_publish_interval: Duration) -> Self {
276 self.config.min_publish_interval = min_publish_interval;
277 self
278 }
279
280 pub fn ignore_clock_skew(mut self, ignore_clock_skew: bool) -> Self {
283 self.config.performance.ignore_clock_skew = ignore_clock_skew;
284 self
285 }
286
287 pub fn recreate_monitored_items_chunk(mut self, recreate_monitored_items_chunk: usize) -> Self {
291 self.config.performance.recreate_monitored_items_chunk = recreate_monitored_items_chunk;
292 self
293 }
294
295 pub fn recreate_subscriptions(mut self, recreate_subscriptions: bool) -> Self {
302 self.config.recreate_subscriptions = recreate_subscriptions;
303 self
304 }
305
306 pub fn session_name(mut self, session_name: impl Into<String>) -> Self {
308 self.config.session_name = session_name.into();
309 self
310 }
311
312 pub fn session_timeout(mut self, session_timeout: u32) -> Self {
314 self.config.session_timeout = session_timeout;
315 self
316 }
317
318 pub fn session_nonce_length(mut self, session_nonce_length: usize) -> Self {
320 self.config.session_nonce_length = session_nonce_length;
321 self
322 }
323}