1use crate::{Error, Result};
2#[cfg(feature = "native-tls")]
3use native_tls::{Certificate, Identity, Protocol, TlsConnector, TlsConnectorBuilder};
4#[cfg(feature = "rustls")]
5use std::sync::Arc;
6use std::{
7 collections::HashMap,
8 fmt::{self, Display, Write},
9 str::FromStr,
10 time::Duration,
11};
12use url::Url;
13
14const DEFAULT_PORT: u16 = 6379;
15const DEFAULT_DATABASE: usize = 0;
16const DEFAULT_WAIT_BETWEEN_FAILURES: u64 = 250;
17const DEFAULT_CONNECT_TIMEOUT: u64 = 10_000;
18const DEFAULT_COMMAND_TIMEOUT: u64 = 0;
19const DEFAULT_AUTO_RESUBSCRTBE: bool = true;
20const DEFAULT_AUTO_REMONITOR: bool = true;
21const DEFAULT_KEEP_ALIVE: Option<Duration> = None;
22const DEFAULT_NO_DELAY: bool = true;
23const DEFAULT_RETRY_ON_ERROR: bool = false;
24
25type Uri<'a> = (
26 &'a str,
27 Option<&'a str>,
28 Option<&'a str>,
29 Vec<(&'a str, u16)>,
30 Vec<&'a str>,
31 Option<HashMap<String, String>>,
32);
33
34#[derive(Debug, Clone)]
37pub struct Config {
38 pub server: ServerConfig,
40 pub username: Option<String>,
44 pub password: Option<String>,
52 pub database: usize,
57 #[cfg_attr(docsrs, doc(cfg(any(feature = "native-tls", feature = "rustls"))))]
59 #[cfg(any(feature = "native-tls", feature = "rustls"))]
60 pub tls_config: Option<TlsConfig>,
61 pub connect_timeout: Duration,
63 pub command_timeout: Duration,
70 pub auto_resubscribe: bool,
75 pub auto_remonitor: bool,
81 pub connection_name: String,
85 pub keep_alive: Option<Duration>,
89 pub no_delay: bool,
93 pub retry_on_error: bool,
106 pub reconnection: ReconnectionConfig,
108}
109
110impl Default for Config {
111 fn default() -> Self {
112 Self {
113 server: Default::default(),
114 username: Default::default(),
115 password: Default::default(),
116 database: Default::default(),
117 #[cfg(any(feature = "native-tls", feature = "rustls"))]
118 tls_config: Default::default(),
119 connect_timeout: Duration::from_millis(DEFAULT_CONNECT_TIMEOUT),
120 command_timeout: Duration::from_millis(DEFAULT_COMMAND_TIMEOUT),
121 auto_resubscribe: DEFAULT_AUTO_RESUBSCRTBE,
122 auto_remonitor: DEFAULT_AUTO_REMONITOR,
123 connection_name: String::from(""),
124 keep_alive: DEFAULT_KEEP_ALIVE,
125 no_delay: DEFAULT_NO_DELAY,
126 retry_on_error: DEFAULT_RETRY_ON_ERROR,
127 reconnection: Default::default(),
128 }
129 }
130}
131
132impl FromStr for Config {
133 type Err = Error;
134
135 fn from_str(str: &str) -> Result<Config> {
137 if let Some(config) = Self::parse_uri(str) {
138 Ok(config)
139 } else if let Some(addr) = Self::parse_addr(str) {
140 addr.into_config()
141 } else {
142 Err(Error::Config(format!("Cannot parse config from {str}")))
143 }
144 }
145}
146
147impl Config {
148 pub fn from_uri(uri: Url) -> Result<Config> {
150 Self::from_str(uri.as_str())
151 }
152
153 fn parse_addr(str: &str) -> Option<(&str, u16)> {
155 let mut iter = str.split(':');
156
157 match (iter.next(), iter.next(), iter.next()) {
158 (Some(host), Some(port), None) => {
159 if let Ok(port) = port.parse::<u16>() {
160 Some((host, port))
161 } else {
162 None
163 }
164 }
165 (Some(host), None, None) => Some((host, DEFAULT_PORT)),
166 _ => None,
167 }
168 }
169
170 fn parse_uri(uri: &str) -> Option<Config> {
171 let (scheme, username, password, hosts, path_segments, mut query) =
172 Self::break_down_uri(uri)?;
173 let mut hosts = hosts;
174 let mut path_segments = path_segments.into_iter();
175
176 enum ServerType {
177 Standalone,
178 Sentinel,
179 Cluster,
180 }
181
182 #[cfg(any(feature = "native-tls", feature = "rustls"))]
183 let (tls_config, server_type) = match scheme {
184 "redis" => (None, ServerType::Standalone),
185 "rediss" => (Some(TlsConfig::default()), ServerType::Standalone),
186 "redis+sentinel" | "redis-sentinel" => (None, ServerType::Sentinel),
187 "rediss+sentinel" | "rediss-sentinel" => {
188 (Some(TlsConfig::default()), ServerType::Sentinel)
189 }
190 "redis+cluster" | "redis-cluster" => (None, ServerType::Cluster),
191 "rediss+cluster" | "rediss-cluster" => {
192 (Some(TlsConfig::default()), ServerType::Cluster)
193 }
194 _ => {
195 return None;
196 }
197 };
198
199 #[cfg(not(any(feature = "native-tls", feature = "rustls")))]
200 let server_type = match scheme {
201 "redis" => ServerType::Standalone,
202 "redis+sentinel" | "redis-sentinel" => ServerType::Sentinel,
203 "redis+cluster" | "redis-cluster" => ServerType::Cluster,
204 _ => {
205 return None;
206 }
207 };
208
209 let server = match server_type {
210 ServerType::Standalone => {
211 if hosts.len() > 1 {
212 return None;
213 } else {
214 let (host, port) = hosts.pop()?;
215 ServerConfig::Standalone {
216 host: host.to_owned(),
217 port,
218 }
219 }
220 }
221 ServerType::Sentinel => {
222 let instances = hosts
223 .iter()
224 .map(|(host, port)| ((*host).to_owned(), *port))
225 .collect::<Vec<_>>();
226
227 let service_name = match path_segments.next() {
228 Some(service_name) => service_name.to_owned(),
229 None => {
230 return None;
231 }
232 };
233
234 let mut sentinel_config = SentinelConfig {
235 instances,
236 service_name,
237 ..Default::default()
238 };
239
240 if let Some(ref mut query) = query {
241 if let Some(millis) = query.remove("wait_between_failures")
242 && let Ok(millis) = millis.parse::<u64>()
243 {
244 sentinel_config.wait_between_failures = Duration::from_millis(millis);
245 }
246
247 sentinel_config.username = query.remove("sentinel_username");
248 sentinel_config.password = query.remove("sentinel_password");
249 }
250
251 ServerConfig::Sentinel(sentinel_config)
252 }
253 ServerType::Cluster => {
254 let nodes = hosts
255 .iter()
256 .map(|(host, port)| ((*host).to_owned(), *port))
257 .collect::<Vec<_>>();
258
259 ServerConfig::Cluster(ClusterConfig { nodes })
260 }
261 };
262
263 let database = match path_segments.next() {
264 Some(database) => match database.parse::<usize>() {
265 Ok(database) => database,
266 Err(_) => {
267 return None;
268 }
269 },
270 None => DEFAULT_DATABASE,
271 };
272
273 let mut config = Config {
274 server,
275 username: username.map(|u| u.to_owned()),
276 password: password.map(|p| p.to_owned()),
277 database,
278 #[cfg(any(feature = "native-tls", feature = "rustls"))]
279 tls_config,
280 ..Default::default()
281 };
282
283 if let Some(ref mut query) = query {
284 if let Some(millis) = query.remove("connect_timeout")
285 && let Ok(millis) = millis.parse::<u64>()
286 {
287 config.connect_timeout = Duration::from_millis(millis);
288 }
289
290 if let Some(millis) = query.remove("command_timeout")
291 && let Ok(millis) = millis.parse::<u64>()
292 {
293 config.command_timeout = Duration::from_millis(millis);
294 }
295
296 if let Some(auto_resubscribe) = query.remove("auto_resubscribe")
297 && let Ok(auto_resubscribe) = auto_resubscribe.parse::<bool>()
298 {
299 config.auto_resubscribe = auto_resubscribe;
300 }
301
302 if let Some(auto_remonitor) = query.remove("auto_remonitor")
303 && let Ok(auto_remonitor) = auto_remonitor.parse::<bool>()
304 {
305 config.auto_remonitor = auto_remonitor;
306 }
307
308 if let Some(connection_name) = query.remove("connection_name") {
309 config.connection_name = connection_name;
310 }
311
312 if let Some(keep_alive) = query.remove("keep_alive")
313 && let Ok(keep_alive) = keep_alive.parse::<u64>()
314 {
315 config.keep_alive = Some(Duration::from_millis(keep_alive));
316 }
317
318 if let Some(no_delay) = query.remove("no_delay")
319 && let Ok(no_delay) = no_delay.parse::<bool>()
320 {
321 config.no_delay = no_delay;
322 }
323
324 if let Some(retry_on_error) = query.remove("retry_on_error")
325 && let Ok(retry_on_error) = retry_on_error.parse::<bool>()
326 {
327 config.retry_on_error = retry_on_error;
328 }
329 }
330
331 Some(config)
332 }
333
334 fn break_down_uri<'a>(uri: &'a str) -> Option<Uri<'a>> {
336 let end_of_scheme = match uri.find("://") {
337 Some(index) => index,
338 None => {
339 return None;
340 }
341 };
342
343 let scheme = &uri[..end_of_scheme];
344
345 let after_scheme = &uri[end_of_scheme + 3..];
346
347 let (before_query, query) = match after_scheme.find('?') {
348 Some(index) => match Self::exclusive_split_at(after_scheme, index) {
349 (Some(before_query), after_query) => (before_query, after_query),
350 _ => {
351 return None;
352 }
353 },
354 None => (after_scheme, None),
355 };
356
357 let (authority, path) = match before_query.find('/') {
358 Some(index) => match Self::exclusive_split_at(before_query, index) {
359 (Some(authority), path) => (authority, path),
360 _ => {
361 return None;
362 }
363 },
364 None => (before_query, None),
365 };
366
367 let (user_info, hosts) = match authority.rfind('@') {
368 Some(index) => {
369 let (user_info, hosts) = Self::exclusive_split_at(authority, index);
372 match hosts {
373 Some(hosts) => (user_info, hosts),
374 None => {
375 return None;
377 }
378 }
379 }
380 None => (None, authority),
381 };
382
383 let (username, password) = match user_info {
384 Some(user_info) => match user_info.find(':') {
385 Some(index) => match Self::exclusive_split_at(user_info, index) {
386 (username, None) => (username, Some("")),
387 (username, password) => (username, password),
388 },
389 None => {
390 return None;
392 }
393 },
394 None => (None, None),
395 };
396
397 let hosts = hosts
398 .split(',')
399 .map(Self::parse_addr)
400 .collect::<Option<Vec<_>>>();
401 let hosts = hosts?;
402
403 let path_segments = match path {
404 Some(path) => path.split('/').collect::<Vec<_>>(),
405 None => Vec::new(),
406 };
407
408 let query = match query.map(|q| {
409 q.split('&')
410 .map(|s| s.split_once('=').map(|(k, v)| (k.to_owned(), v.to_owned())))
411 .collect::<Option<HashMap<String, String>>>()
412 }) {
413 Some(Some(query)) => Some(query),
414 Some(None) => return None,
415 None => None,
416 };
417
418 Some((scheme, username, password, hosts, path_segments, query))
419 }
420
421 fn exclusive_split_at(s: &str, i: usize) -> (Option<&str>, Option<&str>) {
424 let (l, r) = s.split_at(i);
425
426 let lout = if !l.is_empty() { Some(l) } else { None };
427 let rout = if r.len() > 1 { Some(&r[1..]) } else { None };
428
429 (lout, rout)
430 }
431}
432
433impl Display for Config {
434 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
435 #[cfg(any(feature = "native-tls", feature = "rustls"))]
436 if self.tls_config.is_some() {
437 match &self.server {
438 ServerConfig::Standalone { host: _, port: _ } => f.write_str("rediss://")?,
439 ServerConfig::Sentinel(_) => f.write_str("rediss+sentinel://")?,
440 ServerConfig::Cluster(_) => f.write_str("rediss+cluster://")?,
441 }
442 } else {
443 match &self.server {
444 ServerConfig::Standalone { host: _, port: _ } => f.write_str("redis://")?,
445 ServerConfig::Sentinel(_) => f.write_str("redis+sentinel://")?,
446 ServerConfig::Cluster(_) => f.write_str("redis+cluster://")?,
447 }
448 }
449
450 #[cfg(not(any(feature = "native-tls", feature = "rustls")))]
451 match &self.server {
452 ServerConfig::Standalone { host: _, port: _ } => f.write_str("redis://")?,
453 ServerConfig::Sentinel(_) => f.write_str("redis+sentinel://")?,
454 ServerConfig::Cluster(_) => f.write_str("redis+cluster://")?,
455 }
456
457 if let Some(username) = &self.username {
458 f.write_str(username)?;
459 }
460
461 if let Some(password) = &self.password {
462 f.write_char(':')?;
463 f.write_str(password)?;
464 f.write_char('@')?;
465 }
466
467 match &self.server {
468 ServerConfig::Standalone { host, port } => {
469 f.write_str(host)?;
470 if *port != DEFAULT_PORT {
471 f.write_char(':')?;
472 f.write_str(&port.to_string())?;
473 }
474 }
475 ServerConfig::Sentinel(SentinelConfig {
476 instances,
477 service_name,
478 wait_between_failures: _,
479 password: _,
480 username: _,
481 }) => {
482 f.write_str(
483 &instances
484 .iter()
485 .map(|(host, port)| format!("{host}:{port}"))
486 .collect::<Vec<String>>()
487 .join(","),
488 )?;
489 f.write_char('/')?;
490 f.write_str(service_name)?;
491 }
492 ServerConfig::Cluster(ClusterConfig { nodes }) => {
493 f.write_str(
494 &nodes
495 .iter()
496 .map(|(host, port)| format!("{host}:{port}"))
497 .collect::<Vec<String>>()
498 .join(","),
499 )?;
500 }
501 }
502
503 if self.database > 0 {
504 f.write_char('/')?;
505 f.write_str(&self.database.to_string())?;
506 }
507
508 let mut query_separator = false;
511
512 let connect_timeout = self.connect_timeout.as_millis() as u64;
513 if connect_timeout != DEFAULT_CONNECT_TIMEOUT {
514 if !query_separator {
515 query_separator = true;
516 f.write_char('?')?;
517 } else {
518 f.write_char('&')?;
519 }
520 f.write_fmt(format_args!("connect_timeout={connect_timeout}"))?;
521 }
522
523 let command_timeout = self.command_timeout.as_millis() as u64;
524 if command_timeout != DEFAULT_COMMAND_TIMEOUT {
525 if !query_separator {
526 query_separator = true;
527 f.write_char('?')?;
528 } else {
529 f.write_char('&')?;
530 }
531 f.write_fmt(format_args!("command_timeout={command_timeout}"))?;
532 }
533
534 if self.auto_resubscribe != DEFAULT_AUTO_RESUBSCRTBE {
535 if !query_separator {
536 query_separator = true;
537 f.write_char('?')?;
538 } else {
539 f.write_char('&')?;
540 }
541 f.write_fmt(format_args!("auto_resubscribe={}", self.auto_resubscribe))?;
542 }
543
544 if self.auto_remonitor != DEFAULT_AUTO_REMONITOR {
545 if !query_separator {
546 query_separator = true;
547 f.write_char('?')?;
548 } else {
549 f.write_char('&')?;
550 }
551 f.write_fmt(format_args!("auto_remonitor={}", self.auto_remonitor))?;
552 }
553
554 if !self.connection_name.is_empty() {
555 if !query_separator {
556 query_separator = true;
557 f.write_char('?')?;
558 } else {
559 f.write_char('&')?;
560 }
561 f.write_fmt(format_args!("connection_name={}", self.connection_name))?;
562 }
563
564 if let Some(keep_alive) = self.keep_alive {
565 if !query_separator {
566 query_separator = true;
567 f.write_char('?')?;
568 } else {
569 f.write_char('&')?;
570 }
571 f.write_fmt(format_args!("keep_alive={}", keep_alive.as_millis()))?;
572 }
573
574 if self.no_delay != DEFAULT_NO_DELAY {
575 if !query_separator {
576 query_separator = true;
577 f.write_char('?')?;
578 } else {
579 f.write_char('&')?;
580 }
581 f.write_fmt(format_args!("no_delay={}", self.no_delay))?;
582 }
583
584 if self.retry_on_error != DEFAULT_RETRY_ON_ERROR {
585 if !query_separator {
586 query_separator = true;
587 f.write_char('?')?;
588 } else {
589 f.write_char('&')?;
590 }
591 f.write_fmt(format_args!("retry_on_error={}", self.retry_on_error))?;
592 }
593
594 if let ServerConfig::Sentinel(SentinelConfig {
595 instances: _,
596 service_name: _,
597 wait_between_failures: wait_beetween_failures,
598 password,
599 username,
600 }) = &self.server
601 {
602 let wait_between_failures = wait_beetween_failures.as_millis() as u64;
603 if wait_between_failures != DEFAULT_WAIT_BETWEEN_FAILURES {
604 if !query_separator {
605 query_separator = true;
606 f.write_char('?')?;
607 } else {
608 f.write_char('&')?;
609 }
610 f.write_fmt(format_args!(
611 "wait_between_failures={wait_between_failures}"
612 ))?;
613 }
614 if let Some(username) = username {
615 if !query_separator {
616 query_separator = true;
617 f.write_char('?')?;
618 } else {
619 f.write_char('&')?;
620 }
621 f.write_str("sentinel_username=")?;
622 f.write_str(username)?;
623 }
624 if let Some(password) = password {
625 if !query_separator {
626 f.write_char('?')?;
627 } else {
628 f.write_char('&')?;
629 }
630 f.write_str("sentinel_password=")?;
631 f.write_str(password)?;
632 }
633 }
634
635 Ok(())
636 }
637}
638
639#[derive(Debug, Clone)]
641pub enum ServerConfig {
642 Standalone {
644 host: String,
646 port: u16,
648 },
649 Sentinel(SentinelConfig),
651 Cluster(ClusterConfig),
653}
654
655impl Default for ServerConfig {
656 fn default() -> Self {
657 ServerConfig::Standalone {
658 host: "127.0.0.1".to_owned(),
659 port: 6379,
660 }
661 }
662}
663
664#[derive(Debug, Clone)]
666pub struct SentinelConfig {
667 pub instances: Vec<(String, u16)>,
669
670 pub service_name: String,
672
673 pub wait_between_failures: Duration,
675
676 pub username: Option<String>,
678
679 pub password: Option<String>,
681}
682
683impl Default for SentinelConfig {
684 fn default() -> Self {
685 Self {
686 instances: Default::default(),
687 service_name: Default::default(),
688 wait_between_failures: Duration::from_millis(DEFAULT_WAIT_BETWEEN_FAILURES),
689 password: None,
690 username: None,
691 }
692 }
693}
694
695#[derive(Debug, Clone, Default)]
697pub struct ClusterConfig {
698 pub nodes: Vec<(String, u16)>,
700}
701
702#[cfg(feature = "rustls")]
706#[derive(Debug, Clone)]
707pub struct TlsConfig {
708 pub rustls_config: Arc<rustls::ClientConfig>,
709}
710
711#[cfg(feature = "rustls")]
712impl Default for TlsConfig {
713 fn default() -> Self {
714 let root_store =
715 rustls::RootCertStore::from_iter(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
716 let rustls_config = rustls::ClientConfig::builder()
717 .with_root_certificates(root_store)
718 .with_no_client_auth();
719
720 Self {
721 rustls_config: Arc::new(rustls_config),
722 }
723 }
724}
725
726#[cfg(feature = "native-tls")]
730#[derive(Clone)]
731pub struct TlsConfig {
732 identity: Option<Identity>,
733 root_certificates: Option<Vec<Certificate>>,
734 min_protocol_version: Option<Protocol>,
735 max_protocol_version: Option<Protocol>,
736 disable_built_in_roots: bool,
737 danger_accept_invalid_certs: bool,
738 danger_accept_invalid_hostnames: bool,
739 use_sni: bool,
740}
741
742#[cfg(feature = "native-tls")]
743impl Default for TlsConfig {
744 fn default() -> Self {
745 Self {
746 identity: None,
747 root_certificates: None,
748 min_protocol_version: Some(Protocol::Tlsv10),
749 max_protocol_version: None,
750 disable_built_in_roots: false,
751 danger_accept_invalid_certs: false,
752 danger_accept_invalid_hostnames: false,
753 use_sni: true,
754 }
755 }
756}
757
758#[cfg(feature = "native-tls")]
759impl std::fmt::Debug for TlsConfig {
760 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
761 f.debug_struct("TlsConfig")
762 .field("min_protocol_version", &self.min_protocol_version)
763 .field("max_protocol_version", &self.max_protocol_version)
764 .field("disable_built_in_roots", &self.disable_built_in_roots)
765 .field(
766 "danger_accept_invalid_certs",
767 &self.danger_accept_invalid_certs,
768 )
769 .field(
770 "danger_accept_invalid_hostnames",
771 &self.danger_accept_invalid_hostnames,
772 )
773 .field("use_sni", &self.use_sni)
774 .finish()
775 }
776}
777
778#[cfg(feature = "native-tls")]
779impl TlsConfig {
780 pub fn identity(&mut self, identity: Identity) -> &mut Self {
781 self.identity = Some(identity);
782 self
783 }
784
785 pub fn root_certificates(&mut self, root_certificates: Vec<Certificate>) -> &mut Self {
786 self.root_certificates = Some(root_certificates);
787 self
788 }
789
790 pub fn min_protocol_version(&mut self, min_protocol_version: Protocol) -> &mut Self {
791 self.min_protocol_version = Some(min_protocol_version);
792 self
793 }
794
795 pub fn max_protocol_version(&mut self, max_protocol_version: Protocol) -> &mut Self {
796 self.max_protocol_version = Some(max_protocol_version);
797 self
798 }
799
800 pub fn disable_built_in_roots(&mut self, disable_built_in_roots: bool) -> &mut Self {
801 self.disable_built_in_roots = disable_built_in_roots;
802 self
803 }
804
805 pub fn danger_accept_invalid_certs(&mut self, danger_accept_invalid_certs: bool) -> &mut Self {
806 self.danger_accept_invalid_certs = danger_accept_invalid_certs;
807 self
808 }
809
810 pub fn use_sni(&mut self, use_sni: bool) -> &mut Self {
811 self.use_sni = use_sni;
812 self
813 }
814
815 pub fn danger_accept_invalid_hostnames(
816 &mut self,
817 danger_accept_invalid_hostnames: bool,
818 ) -> &mut Self {
819 self.danger_accept_invalid_hostnames = danger_accept_invalid_hostnames;
820 self
821 }
822
823 pub fn into_tls_connector_builder(&self) -> TlsConnectorBuilder {
824 let mut builder = TlsConnector::builder();
825
826 if let Some(root_certificates) = &self.root_certificates {
827 for root_certificate in root_certificates {
828 builder.add_root_certificate(root_certificate.clone());
829 }
830 }
831
832 builder.min_protocol_version(self.min_protocol_version);
833 builder.max_protocol_version(self.max_protocol_version);
834 builder.disable_built_in_roots(self.disable_built_in_roots);
835 builder.danger_accept_invalid_certs(self.danger_accept_invalid_certs);
836 builder.danger_accept_invalid_hostnames(self.danger_accept_invalid_hostnames);
837 builder.use_sni(self.use_sni);
838
839 builder
840 }
841}
842
843pub trait IntoConfig {
849 fn into_config(self) -> Result<Config>;
851}
852
853impl IntoConfig for Config {
854 fn into_config(self) -> Result<Config> {
855 Ok(self)
856 }
857}
858
859impl<T: Into<String>> IntoConfig for (T, u16) {
860 fn into_config(self) -> Result<Config> {
861 Ok(Config {
862 server: ServerConfig::Standalone {
863 host: self.0.into(),
864 port: self.1,
865 },
866 ..Default::default()
867 })
868 }
869}
870
871impl IntoConfig for &str {
872 fn into_config(self) -> Result<Config> {
873 Config::from_str(self)
874 }
875}
876
877impl IntoConfig for String {
878 fn into_config(self) -> Result<Config> {
879 Config::from_str(&self)
880 }
881}
882
883impl IntoConfig for Url {
884 fn into_config(self) -> Result<Config> {
885 Config::from_uri(self)
886 }
887}
888
889#[derive(Debug, Clone)]
892pub enum ReconnectionConfig {
893 Constant {
895 max_attempts: u32,
897 delay: u32,
899 jitter: u32,
901 },
902 Linear {
904 max_attempts: u32,
906 max_delay: u32,
908 delay: u32,
910 jitter: u32,
912 },
913 Exponential {
917 max_attempts: u32,
919 min_delay: u32,
921 max_delay: u32,
923 multiplicative_factor: u32,
925 jitter: u32,
927 },
928}
929
930const DEFAULT_JITTER_MS: u32 = 100;
932const DEFAULT_DELAY_MS: u32 = 1000;
933
934impl Default for ReconnectionConfig {
935 fn default() -> Self {
936 Self::Constant {
937 max_attempts: 0,
938 delay: DEFAULT_DELAY_MS,
939 jitter: DEFAULT_JITTER_MS,
940 }
941 }
942}
943
944impl ReconnectionConfig {
945 pub fn new_constant(max_attempts: u32, delay: u32) -> Self {
947 Self::Constant {
948 max_attempts,
949 delay,
950 jitter: DEFAULT_JITTER_MS,
951 }
952 }
953
954 pub fn new_linear(max_attempts: u32, max_delay: u32, delay: u32) -> Self {
956 Self::Linear {
957 max_attempts,
958 max_delay,
959 delay,
960 jitter: DEFAULT_JITTER_MS,
961 }
962 }
963
964 pub fn new_exponential(
966 max_attempts: u32,
967 min_delay: u32,
968 max_delay: u32,
969 multiplicative_factor: u32,
970 ) -> Self {
971 Self::Exponential {
972 max_delay,
973 max_attempts,
974 min_delay,
975 multiplicative_factor,
976 jitter: DEFAULT_JITTER_MS,
977 }
978 }
979
980 pub fn set_jitter(&mut self, jitter_ms: u32) {
984 match self {
985 Self::Constant { jitter, .. } => {
986 *jitter = jitter_ms;
987 }
988 Self::Linear { jitter, .. } => {
989 *jitter = jitter_ms;
990 }
991 Self::Exponential { jitter, .. } => {
992 *jitter = jitter_ms;
993 }
994 }
995 }
996}