use std::path::PathBuf;
use std::time::Duration;
#[derive(Debug, Clone)]
pub struct BootstrapCacheConfig {
pub cache_dir: PathBuf,
pub max_peers: usize,
pub epsilon: f64,
pub stale_threshold: Duration,
pub reachability_ttl: Duration,
pub save_interval: Duration,
pub quality_update_interval: Duration,
pub cleanup_interval: Duration,
pub min_peers_to_save: usize,
pub enable_file_locking: bool,
pub weights: QualityWeights,
}
#[derive(Debug, Clone)]
pub struct QualityWeights {
pub success_rate: f64,
pub rtt: f64,
pub freshness: f64,
pub capabilities: f64,
}
impl Default for BootstrapCacheConfig {
fn default() -> Self {
Self {
cache_dir: default_cache_dir(),
max_peers: 30_000,
epsilon: 0.1,
stale_threshold: Duration::from_secs(7 * 24 * 3600), reachability_ttl: crate::reachability::DIRECT_REACHABILITY_TTL,
save_interval: Duration::from_secs(5 * 60), quality_update_interval: Duration::from_secs(3600), cleanup_interval: Duration::from_secs(6 * 3600), min_peers_to_save: 10,
enable_file_locking: true,
weights: QualityWeights::default(),
}
}
}
impl Default for QualityWeights {
fn default() -> Self {
Self {
success_rate: 0.4,
rtt: 0.25,
freshness: 0.15,
capabilities: 0.2,
}
}
}
impl BootstrapCacheConfig {
pub fn builder() -> BootstrapCacheConfigBuilder {
BootstrapCacheConfigBuilder::default()
}
}
#[derive(Default)]
pub struct BootstrapCacheConfigBuilder {
config: BootstrapCacheConfig,
}
impl BootstrapCacheConfigBuilder {
pub fn cache_dir(mut self, dir: impl Into<PathBuf>) -> Self {
self.config.cache_dir = dir.into();
self
}
pub fn max_peers(mut self, max: usize) -> Self {
self.config.max_peers = max;
self
}
pub fn epsilon(mut self, epsilon: f64) -> Self {
self.config.epsilon = epsilon.clamp(0.0, 1.0);
self
}
pub fn reachability_ttl(mut self, ttl: Duration) -> Self {
self.config.reachability_ttl = ttl;
self
}
pub fn stale_threshold(mut self, duration: Duration) -> Self {
self.config.stale_threshold = duration;
self
}
pub fn save_interval(mut self, duration: Duration) -> Self {
self.config.save_interval = duration;
self
}
pub fn quality_update_interval(mut self, duration: Duration) -> Self {
self.config.quality_update_interval = duration;
self
}
pub fn cleanup_interval(mut self, duration: Duration) -> Self {
self.config.cleanup_interval = duration;
self
}
pub fn min_peers_to_save(mut self, min: usize) -> Self {
self.config.min_peers_to_save = min;
self
}
pub fn enable_file_locking(mut self, enable: bool) -> Self {
self.config.enable_file_locking = enable;
self
}
pub fn weights(mut self, weights: QualityWeights) -> Self {
self.config.weights = weights;
self
}
pub fn build(self) -> BootstrapCacheConfig {
self.config
}
}
fn default_cache_dir() -> PathBuf {
if let Ok(tmpdir) = std::env::var("TMPDIR") {
return PathBuf::from(tmpdir).join("ant-quic-cache");
}
if let Some(cache_dir) = dirs::cache_dir() {
cache_dir.join("ant-quic")
} else if let Some(home) = dirs::home_dir() {
home.join(".cache").join("ant-quic")
} else {
PathBuf::from(".ant-quic-cache")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = BootstrapCacheConfig::default();
assert_eq!(config.max_peers, 30_000);
assert!((config.epsilon - 0.1).abs() < f64::EPSILON);
assert_eq!(config.stale_threshold, Duration::from_secs(7 * 24 * 3600));
}
#[test]
fn test_builder() {
let config = BootstrapCacheConfig::builder()
.max_peers(10_000)
.epsilon(0.2)
.cache_dir("/tmp/test")
.build();
assert_eq!(config.max_peers, 10_000);
assert!((config.epsilon - 0.2).abs() < f64::EPSILON);
assert_eq!(config.cache_dir, PathBuf::from("/tmp/test"));
}
#[test]
fn test_epsilon_clamping() {
let config = BootstrapCacheConfig::builder().epsilon(1.5).build();
assert!((config.epsilon - 1.0).abs() < f64::EPSILON);
let config = BootstrapCacheConfig::builder().epsilon(-0.5).build();
assert!(config.epsilon.abs() < f64::EPSILON);
}
}