Skip to main content

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}