rusty_cat/meow_config.rs
1use std::time::Duration;
2
3use crate::error::{InnerErrorCode, MeowError};
4use crate::http_breakpoint::BreakpointDownloadHttpConfig;
5
6/// Runtime and transport configuration for [`crate::MeowClient`].
7///
8/// This type is immutable after client creation. Use [`Self::default`] for a
9/// safe baseline, or [`Self::builder`] for validated customization.
10///
11/// # Recommended workflow
12///
13/// 1. Start with `MeowConfig::default()` for safe baseline values.
14/// 2. Use [`Self::builder`] when you need custom values.
15/// 3. Tune concurrency and queue capacities according to workload.
16/// 4. Set HTTP timeout and keepalive for your network environment.
17/// 5. Optionally inject a preconfigured `reqwest::Client`.
18///
19/// # Value constraints
20///
21/// - Concurrency values must be `>= 1`.
22/// - Queue capacities must be `>= 1`.
23/// - `http_timeout` and `tcp_keepalive` must be positive durations.
24#[derive(Debug, Clone)]
25pub struct MeowConfig {
26 /// Maximum number of upload groups processed concurrently.
27 ///
28 /// Recommended range: `1..=64`.
29 max_upload_concurrency: usize,
30 /// Maximum number of download groups processed concurrently.
31 ///
32 /// Recommended range: `1..=64`.
33 max_download_concurrency: usize,
34 /// HTTP-level settings used by range download requests.
35 breakpoint_download_http: BreakpointDownloadHttpConfig,
36 /// Optional custom HTTP client used by transfer requests.
37 ///
38 /// If `None`, the library builds an internal `reqwest::Client`.
39 http_client: Option<reqwest::Client>,
40 /// Per-request timeout used when building internal HTTP clients.
41 ///
42 /// Recommended range: `1s..=120s`.
43 http_timeout: Duration,
44 /// TCP keepalive duration for internal HTTP clients.
45 ///
46 /// Recommended range: `10s..=5min`.
47 tcp_keepalive: Duration,
48 /// Capacity of the control-plane command queue.
49 ///
50 /// Recommended range: `16..=4096`.
51 command_queue_capacity: usize,
52 /// Capacity of the worker event queue (progress/state events).
53 ///
54 /// Recommended range: `32..=8192`.
55 worker_event_queue_capacity: usize,
56}
57
58/// Builder for validated [`MeowConfig`] construction.
59///
60/// The builder starts from the same safe defaults as [`MeowConfig::default`].
61/// Invalid hard-constraint values are rejected by [`Self::build`] instead of
62/// being silently clamped or delayed until runtime initialization.
63#[derive(Debug, Clone)]
64pub struct MeowConfigBuilder {
65 max_upload_concurrency: usize,
66 max_download_concurrency: usize,
67 breakpoint_download_http: BreakpointDownloadHttpConfig,
68 http_client: Option<reqwest::Client>,
69 http_timeout: Duration,
70 tcp_keepalive: Duration,
71 command_queue_capacity: usize,
72 worker_event_queue_capacity: usize,
73}
74
75impl Default for MeowConfig {
76 fn default() -> Self {
77 let builder = MeowConfigBuilder::default();
78 Self {
79 max_upload_concurrency: builder.max_upload_concurrency,
80 max_download_concurrency: builder.max_download_concurrency,
81 breakpoint_download_http: builder.breakpoint_download_http,
82 http_client: builder.http_client,
83 http_timeout: builder.http_timeout,
84 tcp_keepalive: builder.tcp_keepalive,
85 command_queue_capacity: builder.command_queue_capacity,
86 worker_event_queue_capacity: builder.worker_event_queue_capacity,
87 }
88 }
89}
90
91impl Default for MeowConfigBuilder {
92 fn default() -> Self {
93 Self {
94 max_upload_concurrency: 2,
95 max_download_concurrency: 2,
96 breakpoint_download_http: BreakpointDownloadHttpConfig::default(),
97 http_client: None,
98 http_timeout: Duration::from_secs(5),
99 tcp_keepalive: Duration::from_secs(30),
100 command_queue_capacity: 128,
101 worker_event_queue_capacity: 256,
102 }
103 }
104}
105
106impl MeowConfig {
107 /// Starts a validated config builder from safe defaults.
108 ///
109 /// # Examples
110 ///
111 /// ```no_run
112 /// use rusty_cat::api::MeowConfig;
113 ///
114 /// let config = MeowConfig::builder()
115 /// .max_upload_concurrency(4)
116 /// .max_download_concurrency(4)
117 /// .build()?;
118 /// assert_eq!(config.max_upload_concurrency(), 4);
119 /// # Ok::<(), rusty_cat::api::MeowError>(())
120 /// ```
121 pub fn builder() -> MeowConfigBuilder {
122 MeowConfigBuilder::default()
123 }
124
125 /// Returns maximum upload concurrency.
126 ///
127 /// Guaranteed effective range: `>= 1`.
128 pub fn max_upload_concurrency(&self) -> usize {
129 self.max_upload_concurrency
130 }
131
132 /// Returns maximum download concurrency.
133 ///
134 /// Guaranteed effective range: `>= 1`.
135 pub fn max_download_concurrency(&self) -> usize {
136 self.max_download_concurrency
137 }
138
139 /// Returns range-download HTTP behavior configuration.
140 pub fn breakpoint_download_http(&self) -> &BreakpointDownloadHttpConfig {
141 &self.breakpoint_download_http
142 }
143
144 /// Returns request timeout used by internal HTTP clients.
145 pub fn http_timeout(&self) -> Duration {
146 self.http_timeout
147 }
148
149 /// Returns TCP keepalive used by internal HTTP clients.
150 pub fn tcp_keepalive(&self) -> Duration {
151 self.tcp_keepalive
152 }
153
154 /// Returns the injected custom HTTP client, if present.
155 ///
156 /// This is an internal accessor used by runtime components.
157 pub(crate) fn http_client_ref(&self) -> Option<&reqwest::Client> {
158 self.http_client.as_ref()
159 }
160
161 /// Returns control-plane command queue capacity.
162 pub fn command_queue_capacity(&self) -> usize {
163 self.command_queue_capacity
164 }
165
166 /// Returns worker event queue capacity.
167 pub fn worker_event_queue_capacity(&self) -> usize {
168 self.worker_event_queue_capacity
169 }
170}
171
172impl MeowConfigBuilder {
173 /// Sets maximum upload concurrency.
174 ///
175 /// Must be `>= 1`; validated by [`Self::build`].
176 pub fn max_upload_concurrency(mut self, max_upload_concurrency: usize) -> Self {
177 self.max_upload_concurrency = max_upload_concurrency;
178 self
179 }
180
181 /// Sets maximum download concurrency.
182 ///
183 /// Must be `>= 1`; validated by [`Self::build`].
184 pub fn max_download_concurrency(mut self, max_download_concurrency: usize) -> Self {
185 self.max_download_concurrency = max_download_concurrency;
186 self
187 }
188
189 /// Injects a custom HTTP client for all transfer requests.
190 ///
191 /// Use this when you need custom proxy, TLS, default headers, middleware,
192 /// or observability behavior.
193 ///
194 /// # Usage rules
195 ///
196 /// - The provided client should be reusable and long-lived.
197 /// - Keep timeout/connection settings aligned with your workload.
198 ///
199 /// # Examples
200 ///
201 /// ```no_run
202 /// use rusty_cat::api::MeowConfig;
203 ///
204 /// let client = reqwest::Client::new();
205 /// let config = MeowConfig::builder().http_client(client).build()?;
206 /// let _ = config;
207 /// # Ok::<(), rusty_cat::api::MeowError>(())
208 /// ```
209 pub fn http_client(mut self, client: reqwest::Client) -> Self {
210 self.http_client = Some(client);
211 self
212 }
213
214 /// Sets request timeout used by internally created HTTP clients.
215 ///
216 /// # Range guidance
217 ///
218 /// - Typical: `3s..=60s`.
219 /// - High-latency network: `30s..=120s`.
220 /// - Avoid zero duration.
221 ///
222 /// # Examples
223 ///
224 /// ```no_run
225 /// use std::time::Duration;
226 /// use rusty_cat::api::MeowConfig;
227 ///
228 /// let config = MeowConfig::builder()
229 /// .http_timeout(Duration::from_secs(20))
230 /// .build()?;
231 /// assert_eq!(config.http_timeout(), Duration::from_secs(20));
232 /// # Ok::<(), rusty_cat::api::MeowError>(())
233 /// ```
234 pub fn http_timeout(mut self, timeout: Duration) -> Self {
235 self.http_timeout = timeout;
236 self
237 }
238
239 /// Sets TCP keepalive for internally created HTTP clients.
240 ///
241 /// # Range guidance
242 ///
243 /// - Typical: `15s..=120s`.
244 /// - Keepalive too small may increase churn.
245 /// - Keepalive too large may delay broken-connection detection.
246 ///
247 /// # Examples
248 ///
249 /// ```no_run
250 /// use std::time::Duration;
251 /// use rusty_cat::api::MeowConfig;
252 ///
253 /// let config = MeowConfig::builder()
254 /// .tcp_keepalive(Duration::from_secs(60))
255 /// .build()?;
256 /// assert_eq!(config.tcp_keepalive(), Duration::from_secs(60));
257 /// # Ok::<(), rusty_cat::api::MeowError>(())
258 /// ```
259 pub fn tcp_keepalive(mut self, keepalive: Duration) -> Self {
260 self.tcp_keepalive = keepalive;
261 self
262 }
263
264 /// Sets command queue capacity for control-plane operations.
265 ///
266 /// This queue carries operations such as enqueue, pause, resume, cancel,
267 /// snapshot, and close.
268 ///
269 /// # Range guidance
270 ///
271 /// Recommended range: `16..=4096`; must be `>= 1`.
272 ///
273 /// # Examples
274 ///
275 /// ```no_run
276 /// use rusty_cat::api::MeowConfig;
277 ///
278 /// let config = MeowConfig::builder()
279 /// .command_queue_capacity(512)
280 /// .build()?;
281 /// assert_eq!(config.command_queue_capacity(), 512);
282 /// # Ok::<(), rusty_cat::api::MeowError>(())
283 /// ```
284 pub fn command_queue_capacity(mut self, command_queue_capacity: usize) -> Self {
285 self.command_queue_capacity = command_queue_capacity;
286 self
287 }
288
289 /// Sets worker event queue capacity for runtime task events.
290 ///
291 /// Events include progress updates and task terminal states.
292 ///
293 /// # Range guidance
294 ///
295 /// Recommended range: `32..=8192`; must be `>= 1`.
296 ///
297 /// # Examples
298 ///
299 /// ```no_run
300 /// use rusty_cat::api::MeowConfig;
301 ///
302 /// let config = MeowConfig::builder()
303 /// .worker_event_queue_capacity(1024)
304 /// .build()?;
305 /// assert_eq!(config.worker_event_queue_capacity(), 1024);
306 /// # Ok::<(), rusty_cat::api::MeowError>(())
307 /// ```
308 pub fn worker_event_queue_capacity(mut self, worker_event_queue_capacity: usize) -> Self {
309 self.worker_event_queue_capacity = worker_event_queue_capacity;
310 self
311 }
312
313 /// Overrides range-download HTTP behavior configuration.
314 ///
315 /// Use this to customize request headers (for example `Accept`) for range
316 /// download chunks.
317 ///
318 /// # Examples
319 ///
320 /// ```no_run
321 /// use rusty_cat::api::{BreakpointDownloadHttpConfig, MeowConfig};
322 ///
323 /// let config = MeowConfig::builder().breakpoint_download_http(
324 /// BreakpointDownloadHttpConfig {
325 /// range_accept: "application/octet-stream".to_string(),
326 /// },
327 /// ).build()?;
328 /// let _ = config;
329 /// # Ok::<(), rusty_cat::api::MeowError>(())
330 /// ```
331 pub fn breakpoint_download_http(mut self, config: BreakpointDownloadHttpConfig) -> Self {
332 self.breakpoint_download_http = config;
333 self
334 }
335
336 /// Validates and builds a [`MeowConfig`].
337 pub fn build(self) -> Result<MeowConfig, MeowError> {
338 validate_non_zero("max_upload_concurrency", self.max_upload_concurrency)?;
339 validate_non_zero("max_download_concurrency", self.max_download_concurrency)?;
340 validate_non_zero("command_queue_capacity", self.command_queue_capacity)?;
341 validate_non_zero(
342 "worker_event_queue_capacity",
343 self.worker_event_queue_capacity,
344 )?;
345 validate_positive_duration("http_timeout", self.http_timeout)?;
346 validate_positive_duration("tcp_keepalive", self.tcp_keepalive)?;
347 Ok(MeowConfig {
348 max_upload_concurrency: self.max_upload_concurrency,
349 max_download_concurrency: self.max_download_concurrency,
350 breakpoint_download_http: self.breakpoint_download_http,
351 http_client: self.http_client,
352 http_timeout: self.http_timeout,
353 tcp_keepalive: self.tcp_keepalive,
354 command_queue_capacity: self.command_queue_capacity,
355 worker_event_queue_capacity: self.worker_event_queue_capacity,
356 })
357 }
358}
359
360fn validate_non_zero(name: &str, value: usize) -> Result<(), MeowError> {
361 if value == 0 {
362 return Err(MeowError::from_code(
363 InnerErrorCode::ParameterEmpty,
364 format!("{name} must be >= 1"),
365 ));
366 }
367 Ok(())
368}
369
370fn validate_positive_duration(name: &str, value: Duration) -> Result<(), MeowError> {
371 if value.is_zero() {
372 return Err(MeowError::from_code(
373 InnerErrorCode::ParameterEmpty,
374 format!("{name} must be greater than 0"),
375 ));
376 }
377 Ok(())
378}