electrum_client/
config.rs1use std::sync::Arc;
4use std::time::Duration;
5
6pub type AuthProvider = Arc<dyn Fn() -> Option<String> + Send + Sync>;
8
9#[derive(Clone)]
16pub struct Config {
17 socks5: Option<Socks5Config>,
19 timeout: Option<Duration>,
21 retry: u8,
23 validate_domain: bool,
25 authorization_provider: Option<AuthProvider>,
27}
28
29impl std::fmt::Debug for Config {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 f.debug_struct("Config")
33 .field("socks5", &self.socks5)
34 .field("timeout", &self.timeout)
35 .field("retry", &self.retry)
36 .field("validate_domain", &self.validate_domain)
37 .field(
38 "authorization_provider",
39 &self.authorization_provider.as_ref().map(|_| "<provider>"),
40 )
41 .finish()
42 }
43}
44
45#[derive(Debug, Clone)]
47pub struct Socks5Config {
48 pub addr: String,
50 pub credentials: Option<Socks5Credential>,
52}
53
54#[derive(Debug, Clone)]
56pub struct Socks5Credential {
57 pub username: String,
58 pub password: String,
59}
60
61pub struct ConfigBuilder {
63 config: Config,
64}
65
66impl ConfigBuilder {
67 pub fn new() -> Self {
69 ConfigBuilder {
70 config: Config::default(),
71 }
72 }
73
74 pub fn socks5(mut self, socks5_config: Option<Socks5Config>) -> Self {
77 self.config.socks5 = socks5_config;
78 self
79 }
80
81 pub fn timeout(mut self, timeout: Option<Duration>) -> Self {
83 self.config.timeout = timeout;
84 self
85 }
86
87 pub fn retry(mut self, retry: u8) -> Self {
89 self.config.retry = retry;
90 self
91 }
92
93 pub fn validate_domain(mut self, validate_domain: bool) -> Self {
95 self.config.validate_domain = validate_domain;
96 self
97 }
98
99 pub fn authorization_provider(mut self, provider: Option<AuthProvider>) -> Self {
101 self.config.authorization_provider = provider;
102 self
103 }
104
105 pub fn build(self) -> Config {
107 self.config
108 }
109}
110
111impl Default for ConfigBuilder {
112 fn default() -> Self {
113 Self::new()
114 }
115}
116
117impl Socks5Config {
118 pub fn new(addr: impl ToString) -> Self {
120 let addr = addr.to_string().replacen("socks5://", "", 1);
121 Socks5Config {
122 addr,
123 credentials: None,
124 }
125 }
126
127 pub fn with_credentials(addr: impl ToString, username: String, password: String) -> Self {
129 let mut config = Socks5Config::new(addr);
130 config.credentials = Some(Socks5Credential { username, password });
131 config
132 }
133}
134
135impl Config {
136 pub fn socks5(&self) -> &Option<Socks5Config> {
140 &self.socks5
141 }
142
143 pub fn retry(&self) -> u8 {
147 self.retry
148 }
149
150 pub fn timeout(&self) -> Option<Duration> {
154 self.timeout
155 }
156
157 pub fn validate_domain(&self) -> bool {
161 self.validate_domain
162 }
163
164 pub fn authorization_provider(&self) -> Option<&AuthProvider> {
168 self.authorization_provider.as_ref()
169 }
170
171 pub fn builder() -> ConfigBuilder {
173 ConfigBuilder::new()
174 }
175}
176
177impl Default for Config {
178 fn default() -> Self {
179 Config {
180 socks5: None,
181 timeout: None,
182 retry: 1,
183 validate_domain: true,
184 authorization_provider: None,
185 }
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192
193 #[test]
194 fn test_authorization_provider_builder() {
195 let token = "test-token-123".to_string();
196 let provider = Arc::new(move || Some(format!("Bearer {}", token)));
197
198 let config = ConfigBuilder::new()
199 .authorization_provider(Some(provider.clone()))
200 .build();
201
202 assert!(config.authorization_provider().is_some());
203
204 if let Some(auth_provider) = config.authorization_provider() {
206 assert_eq!(auth_provider(), Some("Bearer test-token-123".to_string()));
207 }
208 }
209
210 #[test]
211 fn test_authorization_provider_none() {
212 let config = ConfigBuilder::new().build();
213
214 assert!(config.authorization_provider().is_none());
215 }
216
217 #[test]
218 fn test_authorization_provider_returns_none() {
219 let provider = Arc::new(|| None);
220
221 let config = ConfigBuilder::new()
222 .authorization_provider(Some(provider))
223 .build();
224
225 assert!(config.authorization_provider().is_some());
226
227 if let Some(auth_provider) = config.authorization_provider() {
229 assert_eq!(auth_provider(), None);
230 }
231 }
232
233 #[test]
234 fn test_authorization_provider_dynamic_token() {
235 use std::sync::RwLock;
236
237 let token = Arc::new(RwLock::new("initial-token".to_string()));
239 let token_clone = token.clone();
240
241 let provider = Arc::new(move || Some(token_clone.read().unwrap().clone()));
242
243 let config = ConfigBuilder::new()
244 .authorization_provider(Some(provider.clone()))
245 .build();
246
247 if let Some(auth_provider) = config.authorization_provider() {
249 assert_eq!(auth_provider(), Some("initial-token".to_string()));
250 }
251
252 *token.write().unwrap() = "refreshed-token".to_string();
254
255 if let Some(auth_provider) = config.authorization_provider() {
257 assert_eq!(auth_provider(), Some("refreshed-token".to_string()));
258 }
259 }
260
261 #[test]
262 fn test_config_debug_with_provider() {
263 let provider = Arc::new(|| Some("secret-token".to_string()));
264
265 let config = ConfigBuilder::new()
266 .authorization_provider(Some(provider))
267 .build();
268
269 let debug_str = format!("{:?}", config);
270
271 assert!(debug_str.contains("<provider>"));
273 assert!(!debug_str.contains("secret-token"));
275 }
276
277 #[test]
278 fn test_config_debug_without_provider() {
279 let config = ConfigBuilder::new().build();
280
281 let debug_str = format!("{:?}", config);
282
283 assert!(debug_str.contains("authorization_provider"));
285 }
286}