wowlab_centrifuge/client/
config.rs1use std::{pin::Pin, sync::Arc, time::Duration};
2
3use wowlab_types::{sensitive::Sensitive, sim::FastMap};
4
5use crate::error::Error;
6
7const DEFAULT_MIN_RECONNECT_DELAY: Duration = Duration::from_millis(500);
8const DEFAULT_MAX_RECONNECT_DELAY: Duration = Duration::from_secs(20);
9
10type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
11
12type TokenCallback =
13 Arc<dyn Fn() -> BoxFuture<'static, Result<Sensitive<String>, Error>> + Send + Sync>;
14
15#[derive(Clone)]
17pub struct ClientConfig {
18 pub url: String,
19 pub token: Sensitive<String>,
20 pub name: String,
21 pub version: String,
22 pub data: Option<Vec<u8>>,
23 pub headers: FastMap<String, String>,
24 pub min_reconnect_delay: Duration,
25 pub max_reconnect_delay: Duration,
26 pub(super) get_token: Option<TokenCallback>,
27}
28
29impl ClientConfig {
30 #[must_use]
32 pub fn new(url: impl Into<String>, token: Sensitive<String>) -> Self {
33 Self {
34 url: url.into(),
35 token,
36 name: "rust".to_string(),
37 version: env!("CARGO_PKG_VERSION").to_string(),
38 data: None,
39 headers: FastMap::default(),
40 min_reconnect_delay: DEFAULT_MIN_RECONNECT_DELAY,
41 max_reconnect_delay: DEFAULT_MAX_RECONNECT_DELAY,
42 get_token: None,
43 }
44 }
45
46 #[must_use]
48 pub fn name(mut self, name: impl Into<String>) -> Self {
49 self.name = name.into();
50
51 self
52 }
53
54 #[must_use]
56 pub fn version(mut self, version: impl Into<String>) -> Self {
57 self.version = version.into();
58
59 self
60 }
61
62 #[must_use]
64 pub fn data(mut self, data: Vec<u8>) -> Self {
65 self.data = Some(data);
66
67 self
68 }
69
70 #[must_use]
72 pub fn headers(mut self, headers: FastMap<String, String>) -> Self {
73 self.headers = headers;
74
75 self
76 }
77
78 #[must_use]
80 pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
81 self.headers.insert(key.into(), value.into());
82
83 self
84 }
85
86 #[must_use]
88 pub fn get_token<F, Fut>(mut self, f: F) -> Self
89 where
90 F: Fn() -> Fut + Send + Sync + 'static,
91 Fut: Future<Output = Result<Sensitive<String>, Error>> + Send + 'static,
92 {
93 self.get_token = Some(Arc::new(move || Box::pin(f())));
94
95 self
96 }
97}
98
99impl std::fmt::Debug for ClientConfig {
100 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101 f.debug_struct("ClientConfig")
102 .field("url", &self.url)
103 .field("token", &self.token)
104 .field("name", &self.name)
105 .field("version", &self.version)
106 .field("data", &self.data)
107 .field("headers", &self.headers)
108 .field("min_reconnect_delay", &self.min_reconnect_delay)
109 .field("max_reconnect_delay", &self.max_reconnect_delay)
110 .field("get_token", &self.get_token.as_ref().map(|_| "<callback>"))
111 .finish()
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use googletest::prelude::*;
118 use wowlab_types::sensitive::Sensitive;
119
120 use super::ClientConfig;
121
122 #[gtest]
123 fn debug_redacts_connection_token() -> Result<()> {
124 let config = ClientConfig::new(
125 "https://centrifugo.example.com",
126 Sensitive::new("connection-secret".to_string()),
127 );
128 let debug = format!("{config:?}");
129
130 verify_that!(debug.as_str(), contains_substring("[REDACTED]"))?;
131
132 verify_that!(debug.as_str(), not(contains_substring("connection-secret")))
133 }
134}