use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use std::sync::{Arc, LazyLock, Mutex};
use std::sync::atomic::{AtomicU64, AtomicUsize};
pub const MAX_PGET_LIMIT:usize = 5;
#[allow(dead_code)]
pub const PROTO_HTTP: u8 = 1;
#[allow(dead_code)]
pub const PROTO_HTTPS: u8 = 2;
#[allow(dead_code)]
pub const PROTO_RSYNC: u8 = 4;
pub const DEFAULT_LATENCY_MS: u32 = 100;
pub const DEFAULT_BANDWIDTH_MBPS: u32 = 128;
pub const MIN_THROUGHPUT_BPS: u32 = 1000;
pub const MAX_THROUGHPUT_BPS: u32 = 10_000_000;
pub const COUNTRY_BONUS_MULTIPLIER: u32 = 8;
pub const MIN_LATENCY_MS: u32 = 10;
pub const MAX_LATENCY_MS: u32 = 500;
pub const DAYS_PER_MONTH: i64 = 30;
pub const SECONDS_PER_DAY: u64 = 24 * 3600;
pub const SECONDS_PER_MONTH: u64 = SECONDS_PER_DAY * DAYS_PER_MONTH as u64;
pub const HTTP_FORBIDDEN: u16 = 403;
pub const HTTP_SERVER_ERROR_START: u16 = 500;
pub const MAX_DISPLAY_MIRRORS: usize = 100;
pub const DEFAULT_DISPLAY_MIRRORS: usize = 10;
pub const MIN_MIRRORS_FOR_FILTERING: usize = 3;
pub const RATIO_MIRRORS_FOR_EXPLORATION: usize = 8;
pub const ENOUGH_LOCAL_MIRRORS: usize = 10;
pub const INCLUDE_WORLD_MIRRORS: usize = 90;
pub const MIN_ATTEMPTS_FOR_NOONLINE: usize = 2;
pub const MAX_NOONLINE_FRACTION_DENOM: usize = 3;
pub static STATS_CALL_COUNT: AtomicU64 = AtomicU64::new(0);
#[derive(Debug, Clone, Deserialize, Serialize)]
pub enum HttpEvent {
Latency(u64),
NoRange,
NetError(String),
HttpStatus(u16),
TooManyRequests(u32),
OldContent,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct PerformanceLog {
pub timestamp: u64,
pub url: String,
pub offset: u64,
pub bytes_transferred: u64,
pub duration_ms: u64,
pub throughput_bps: u64,
pub success: bool,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct HttpLog {
pub timestamp: u64,
pub url: String,
pub event: HttpEvent,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Mirror {
#[serde(default)]
#[serde(skip_serializing)]
pub url: String,
#[serde(rename = "top")]
#[serde(default)]
pub top_os: Option<String>,
#[serde(rename = "ls")]
#[serde(default)]
pub distro_dirs: HashSet<String>,
#[serde(rename = "cc")]
#[serde(default)]
pub country_code: Option<String>,
#[serde(rename = "p", default)]
pub protocols: u8,
#[serde(rename = "bw")]
#[serde(default)]
pub bandwidth: Option<u32>,
#[serde(rename = "i2", default, deserialize_with = "crate::mirror::bool_from_number")]
pub internet2: bool,
#[serde(skip_serializing, skip_deserializing)]
pub shared_usage: Arc<SharedUsageStats>,
#[serde(skip_serializing, skip_deserializing)]
pub stats: MirrorStats,
#[serde(skip_serializing, skip_deserializing)]
pub is_near: bool,
#[serde(skip_serializing, skip_deserializing)]
pub skip_urls: HashSet<String>,
}
#[derive(Debug, Default)]
pub struct SharedUsageStats {
pub active_downloads: AtomicUsize,
pub total_uses: AtomicU64,
pub last_used: AtomicU64,
}
#[derive(Debug)]
pub struct MirrorStats {
pub score: u64,
pub throughputs: Vec<u32>,
pub latencies: Vec<u32>,
pub avg_throughput: Option<u32>,
pub no_range: bool,
pub no_online: bool,
pub no_content: u32,
pub old_content: bool,
pub max_parallel_conns: Option<u32>,
pub http_errors: HashMap<u16, u32>,
pub other_errors: u32,
pub last_success: Option<u64>,
pub last_check: Option<u64>,
}
impl Default for MirrorStats {
fn default() -> Self {
Self {
score: 0,
throughputs: Vec::new(),
latencies: Vec::new(),
avg_throughput: None,
no_range: false,
no_online: false,
no_content: 0,
old_content: false,
max_parallel_conns: None,
http_errors: HashMap::new(),
other_errors: 0,
last_success: None,
last_check: None,
}
}
}
impl Clone for MirrorStats {
fn clone(&self) -> Self {
Self {
score: self.score,
throughputs: self.throughputs.clone(),
latencies: self.latencies.clone(),
avg_throughput: self.avg_throughput,
no_range: self.no_range,
no_online: self.no_online,
no_content: self.no_content,
old_content: self.old_content,
max_parallel_conns: self.max_parallel_conns,
http_errors: self.http_errors.clone(),
other_errors: self.other_errors,
last_success: self.last_success,
last_check: self.last_check,
}
}
}
impl Default for Mirror {
fn default() -> Self {
Self {
url: String::new(),
top_os: None,
distro_dirs: HashSet::new(),
country_code: None,
protocols: 0,
bandwidth: None,
internet2: false,
shared_usage: Arc::new(SharedUsageStats::default()),
stats: MirrorStats::default(),
is_near: false,
skip_urls: std::collections::HashSet::new(),
}
}
}
impl Clone for Mirror {
fn clone(&self) -> Self {
Self {
url: self.url.clone(),
top_os: self.top_os.clone(),
distro_dirs: self.distro_dirs.clone(),
country_code: self.country_code.clone(),
protocols: self.protocols,
bandwidth: self.bandwidth,
internet2: self.internet2,
shared_usage: Arc::clone(&self.shared_usage),
stats: self.stats.clone(),
is_near: self.is_near,
skip_urls: self.skip_urls.clone(),
}
}
}
impl Drop for Mirror {
fn drop(&mut self) {
self.stop_usage_tracking();
}
}
pub struct Mirrors {
pub mirrors: HashMap<String, Mirror>,
pub available_mirrors: Vec<String>,
pub pget_limit: usize,
}
* ============================================================================
* STREAMLINED MIRROR MANAGEMENT SYSTEM
* ============================================================================
*
* SIMPLIFIED DESIGN PHILOSOPHY:
*
* This system implements country-aware distro-filtered mirror initialization
* for optimal performance and geographic proximity:
*
* 1. **Direct Initialization**: Mirrors are loaded with distro filtering at
* startup time using channel_config().distro directly
*
* 2. **Country-Based Filtering**: When user country code is available, filters
* mirrors to match the user's country for better performance
*
* 3. **Smart Fallback Strategy**: If fewer than 3 country-specific mirrors are
* found, falls back to all distro mirrors (not all mirrors globally)
*
* 4. **Bulk Performance Loading**: All 6 months of performance logs are loaded
* at initialization time in a single efficient pass
*
* 5. **Integrated Usage Tracking**: Mirror usage is tracked within the Mirrors
* struct itself, eliminating the need for separate global state
*
* 6. **Date-Based Log Rotation**: Performance logs use monthly rotation with
* key=value format for better compatibility and debugging
*
* IMPLEMENTATION BENEFITS:
*
* - Geographic optimization: Country-based mirror selection when possible
* - Smart fallback: Ensures adequate mirror availability
* - Single initialization: No complex re-initialization sequences
* - Immediate performance data: 6 months of logs loaded at startup
* - Clean architecture: All mirror state in one place
* - Future-proof logging: Extensible key=value log format
*/
pub static MIRRORS: LazyLock<Mutex<Mirrors>> = LazyLock::new(|| {
Mutex::new(Mirrors {
mirrors: HashMap::new(),
available_mirrors: Vec::new(),
pget_limit: 1,
})
});
pub fn bool_from_number<'de, D>(deserializer: D) -> Result<bool, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::{Error, Visitor};
use std::fmt;
struct BoolVisitor;
impl<'de> Visitor<'de> for BoolVisitor {
type Value = bool;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a boolean or 0/1")
}
fn visit_bool<E>(self, v: bool) -> Result<bool, E> {
Ok(v)
}
fn visit_u64<E>(self, v: u64) -> Result<bool, E>
where
E: Error,
{
Ok(v != 0)
}
fn visit_i64<E>(self, v: i64) -> Result<bool, E>
where
E: Error,
{
Ok(v != 0)
}
fn visit_str<E>(self, v: &str) -> Result<bool, E>
where
E: Error,
{
match v.to_ascii_lowercase().as_str() {
"1" | "true" | "yes" | "y" => Ok(true),
"0" | "false" | "no" | "n" => Ok(false),
_ => Err(E::custom(format!("invalid bool value: {}", v))),
}
}
}
deserializer.deserialize_any(BoolVisitor)
}