Files
aria2-rust-pro/crates/aria2-rust-pro-core/src/runtime.rs
T

207 lines
6.8 KiB
Rust

//! Runtime configuration defaults and human-readable size parsing helpers.
use std::path::PathBuf;
/// Runtime configuration used to build and operate the download engine.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RuntimeConfig {
/// Number of worker threads available to the runtime.
pub worker_threads: usize,
/// Maximum number of simultaneously active downloads.
pub max_active_downloads: usize,
/// Maximum number of tracked downloads.
pub max_downloads: usize,
/// Desired split count per download.
pub split: usize,
/// Canonical maximum connections per server option.
pub max_connections_per_server: usize,
/// Compatibility alias for the maximum connections per server option.
pub max_connection_per_server: usize,
/// Global download-rate cap in bytes per second.
pub max_overall_download_limit: Option<u64>,
/// Per-download download-rate cap in bytes per second.
pub max_download_limit: Option<u64>,
/// Global upload-rate cap in bytes per second.
pub max_overall_upload_limit: Option<u64>,
/// Per-download upload-rate cap in bytes per second.
pub max_upload_limit: Option<u64>,
/// Minimum size used when splitting work into segments.
pub min_split_size: u64,
/// Default piece length for newly created downloads.
pub piece_length: u64,
/// RPC listen port.
pub rpc_port: u16,
/// `BitTorrent` listen port.
pub listen_port: u16,
/// Configured disk-cache size in bytes.
pub disk_cache_bytes: u64,
/// Event queue buffer size.
pub event_buffer_size: usize,
/// Optional session file path.
pub session_path: Option<PathBuf>,
/// Interval between session saves in seconds.
pub save_session_interval_secs: u64,
/// Graceful shutdown timeout in seconds.
pub graceful_shutdown_timeout_secs: u64,
/// Whether XML-RPC endpoints are enabled.
pub allow_xmlrpc: bool,
/// Whether JSON-RPC endpoints are enabled.
pub allow_jsonrpc: bool,
/// Whether resume behavior is enabled.
pub allow_resume: bool,
/// Whether IPv6 support is enabled.
pub enable_ipv6: bool,
/// Whether HTTP 400 responses are retryable.
pub retry_on_400: bool,
/// Whether HTTP 403 responses are retryable.
pub retry_on_403: bool,
/// Whether HTTP 406 responses are retryable.
pub retry_on_406: bool,
/// Whether unknown failures are retryable.
pub retry_on_unknown: bool,
}
impl Default for RuntimeConfig {
fn default() -> Self {
Self {
worker_threads: 4,
max_active_downloads: 5,
max_downloads: 16,
split: 5,
max_connections_per_server: 1,
max_connection_per_server: 1,
max_overall_download_limit: None,
max_download_limit: None,
max_overall_upload_limit: None,
max_upload_limit: None,
min_split_size: 1_024,
piece_length: 1_024,
rpc_port: 6_800,
listen_port: 6_881,
disk_cache_bytes: 16 * 1_024 * 1_024,
event_buffer_size: 256,
session_path: None,
save_session_interval_secs: 30,
graceful_shutdown_timeout_secs: 10,
allow_xmlrpc: true,
allow_jsonrpc: true,
allow_resume: true,
enable_ipv6: false,
retry_on_400: true,
retry_on_403: true,
retry_on_406: true,
retry_on_unknown: true,
}
}
}
impl RuntimeConfig {
/// Returns a copy with the session path set.
#[must_use]
pub fn with_session_path(mut self, path: impl Into<PathBuf>) -> Self {
self.session_path = Some(path.into());
self
}
/// Returns a copy with the worker-thread count overridden.
#[must_use]
pub fn with_worker_threads(mut self, count: usize) -> Self {
self.worker_threads = count;
self
}
/// Returns a copy with the RPC port overridden.
#[must_use]
pub fn with_rpc_port(mut self, port: u16) -> Self {
self.rpc_port = port;
self
}
/// Returns a copy with the session path parsed from a string-like value.
#[must_use]
pub fn with_session_path_str(mut self, path: impl Into<String>) -> Self {
self.session_path = Some(PathBuf::from(path.into()));
self
}
/// Returns the effective max-connections-per-server setting.
#[must_use]
pub fn effective_max_connections_per_server(&self) -> usize {
self.max_connections_per_server
.max(self.max_connection_per_server)
.max(1)
}
/// Returns the effective split count.
#[must_use]
pub fn effective_split(&self) -> usize {
self.split.max(1)
}
/// Returns the effective maximum parallel segment count.
#[must_use]
pub fn effective_parallel_segments(&self) -> usize {
self.effective_split()
.min(self.effective_max_connections_per_server())
.max(1)
}
/// Returns retryability flags for the common HTTP error buckets.
#[must_use]
pub fn retryable_status_codes(&self) -> [bool; 4] {
[
self.retry_on_400,
self.retry_on_403,
self.retry_on_406,
self.retry_on_unknown,
]
}
}
/// Parses a human-readable byte-size string into a byte count.
#[must_use]
#[expect(
clippy::redundant_pub_crate,
reason = "session and request helpers reuse the parser while the runtime module remains crate-private"
)]
pub(crate) fn parse_human_size_text(value: &str) -> Option<u64> {
let trimmed = value.trim();
if trimmed.is_empty() {
return None;
}
let split_index = trimmed
.find(|ch: char| !ch.is_ascii_digit())
.unwrap_or(trimmed.len());
let (digits, suffix) = trimmed.split_at(split_index);
let base = digits.parse::<u64>().ok()?;
let suffix = suffix.trim();
let factor = if suffix.is_empty() {
1
} else if suffix.eq_ignore_ascii_case("k") || suffix.eq_ignore_ascii_case("kb") {
1_024
} else if suffix.eq_ignore_ascii_case("m") || suffix.eq_ignore_ascii_case("mb") {
1_024 * 1_024
} else if suffix.eq_ignore_ascii_case("g") || suffix.eq_ignore_ascii_case("gb") {
1_024 * 1_024 * 1_024
} else {
return None;
};
Some(base.saturating_mul(factor))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_human_size_text_supports_plain_and_suffix_forms() {
assert_eq!(parse_human_size_text("2048"), Some(2048));
assert_eq!(parse_human_size_text("2K"), Some(2 * 1024));
assert_eq!(parse_human_size_text("4M"), Some(4 * 1024 * 1024));
assert_eq!(parse_human_size_text("3gb"), Some(3 * 1024 * 1024 * 1024));
assert_eq!(parse_human_size_text(""), None);
assert_eq!(parse_human_size_text("12T"), None);
}
}