1use serde::{Deserialize, Serialize};
35use std::collections::HashMap;
36use std::sync::Arc;
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct RetryConfig {
41 pub max_retries: u32,
43
44 pub initial_backoff_ms: u64,
46
47 pub backoff_multiplier: f32,
49
50 pub max_backoff_ms: u64,
52}
53
54impl Default for RetryConfig {
55 fn default() -> Self {
56 Self {
57 max_retries: 3,
58 initial_backoff_ms: 1000,
59 backoff_multiplier: 2.0,
60 max_backoff_ms: 30000,
61 }
62 }
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct ProviderConfig {
71 pub api_key: String,
73
74 #[serde(skip_serializing_if = "Option::is_none")]
77 pub base_url: Option<String>,
78
79 #[serde(skip_serializing_if = "Option::is_none")]
81 pub timeout_ms: Option<u64>,
82
83 #[serde(skip_serializing_if = "Option::is_none")]
85 pub proxy: Option<String>,
86
87 #[serde(skip_serializing_if = "Option::is_none")]
89 pub retry: Option<RetryConfig>,
90
91 #[serde(skip_serializing_if = "Option::is_none")]
93 pub headers: Option<HashMap<String, String>>,
94
95 #[serde(skip_serializing_if = "Option::is_none")]
97 pub max_concurrent_requests: Option<usize>,
98}
99
100impl ProviderConfig {
101 pub fn new(api_key: impl Into<String>) -> Self {
103 Self {
104 api_key: api_key.into(),
105 base_url: None,
106 timeout_ms: None,
107 proxy: None,
108 retry: None,
109 headers: None,
110 max_concurrent_requests: None,
111 }
112 }
113
114 pub fn with_base_url(mut self, base_url: impl Into<String>) -> Self {
116 self.base_url = Some(base_url.into());
117 self
118 }
119
120 pub fn with_timeout_ms(mut self, timeout_ms: u64) -> Self {
122 self.timeout_ms = Some(timeout_ms);
123 self
124 }
125
126 pub fn with_proxy(mut self, proxy: impl Into<String>) -> Self {
128 self.proxy = Some(proxy.into());
129 self
130 }
131
132 pub fn with_retry(mut self, retry: RetryConfig) -> Self {
134 self.retry = Some(retry);
135 self
136 }
137
138 pub fn with_header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
140 self.headers
141 .get_or_insert_with(HashMap::new)
142 .insert(key.into(), value.into());
143 self
144 }
145
146 pub fn with_headers(mut self, headers: HashMap<String, String>) -> Self {
148 self.headers = Some(headers);
149 self
150 }
151
152 pub fn with_max_concurrent_requests(mut self, max: usize) -> Self {
154 self.max_concurrent_requests = Some(max);
155 self
156 }
157
158 pub fn timeout(&self) -> std::time::Duration {
160 std::time::Duration::from_millis(self.timeout_ms.unwrap_or(30000))
161 }
162
163 pub fn retry_config(&self) -> RetryConfig {
165 self.retry.clone().unwrap_or_default()
166 }
167}
168
169#[derive(Debug, Clone)]
174pub struct SharedProviderConfig {
175 inner: Arc<ProviderConfig>,
176}
177
178impl SharedProviderConfig {
179 pub fn new(config: ProviderConfig) -> Self {
181 Self {
182 inner: Arc::new(config),
183 }
184 }
185
186 pub fn get(&self) -> &ProviderConfig {
188 &self.inner
189 }
190}
191
192impl From<ProviderConfig> for SharedProviderConfig {
193 fn from(config: ProviderConfig) -> Self {
194 Self::new(config)
195 }
196}
197
198impl std::ops::Deref for SharedProviderConfig {
199 type Target = ProviderConfig;
200
201 fn deref(&self) -> &Self::Target {
202 &self.inner
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 #[test]
211 fn test_provider_config_builder() {
212 let config = ProviderConfig::new("test-key")
213 .with_base_url("https://api.example.com")
214 .with_timeout_ms(5000)
215 .with_header("X-Custom", "value")
216 .with_retry(RetryConfig::default());
217
218 assert_eq!(config.api_key, "test-key");
219 assert_eq!(config.base_url, Some("https://api.example.com".to_string()));
220 assert_eq!(config.timeout_ms, Some(5000));
221 assert!(config.headers.is_some());
222 assert!(config.retry.is_some());
223 }
224
225 #[test]
226 fn test_retry_config_default() {
227 let retry = RetryConfig::default();
228 assert_eq!(retry.max_retries, 3);
229 assert_eq!(retry.initial_backoff_ms, 1000);
230 assert_eq!(retry.backoff_multiplier, 2.0);
231 assert_eq!(retry.max_backoff_ms, 30000);
232 }
233
234 #[test]
235 fn test_shared_config() {
236 let config = ProviderConfig::new("test-key");
237 let shared1 = SharedProviderConfig::new(config.clone());
238 let shared2 = shared1.clone();
239
240 assert_eq!(shared1.api_key, shared2.api_key);
241 assert_eq!(Arc::strong_count(&shared1.inner), 2);
242 }
243}