ant_quic/bootstrap_cache/
config.rs

1//! Bootstrap cache configuration.
2
3use std::path::PathBuf;
4use std::time::Duration;
5
6/// Configuration for the bootstrap cache
7#[derive(Debug, Clone)]
8pub struct BootstrapCacheConfig {
9    /// Directory for cache files
10    pub cache_dir: PathBuf,
11
12    /// Maximum number of peers to cache (default: 20,000)
13    pub max_peers: usize,
14
15    /// Epsilon for exploration rate (default: 0.1 = 10%)
16    /// Higher values = more exploration of unknown peers
17    pub epsilon: f64,
18
19    /// Time after which peers are considered stale (default: 7 days)
20    pub stale_threshold: Duration,
21
22    /// Interval between background save operations (default: 5 minutes)
23    pub save_interval: Duration,
24
25    /// Interval between quality score recalculations (default: 1 hour)
26    pub quality_update_interval: Duration,
27
28    /// Interval between stale peer cleanup (default: 6 hours)
29    pub cleanup_interval: Duration,
30
31    /// Minimum peers required before saving (prevents empty cache overwrite)
32    pub min_peers_to_save: usize,
33
34    /// Enable file locking for multi-process safety
35    pub enable_file_locking: bool,
36
37    /// Quality score weights
38    pub weights: QualityWeights,
39}
40
41/// Weights for quality score calculation
42#[derive(Debug, Clone)]
43pub struct QualityWeights {
44    /// Weight for success rate component (default: 0.4)
45    pub success_rate: f64,
46    /// Weight for RTT component (default: 0.25)
47    pub rtt: f64,
48    /// Weight for age/freshness component (default: 0.15)
49    pub freshness: f64,
50    /// Weight for capability bonuses (default: 0.2)
51    pub capabilities: f64,
52}
53
54impl Default for BootstrapCacheConfig {
55    fn default() -> Self {
56        Self {
57            cache_dir: default_cache_dir(),
58            max_peers: 20_000,
59            epsilon: 0.1,
60            stale_threshold: Duration::from_secs(7 * 24 * 3600), // 7 days
61            save_interval: Duration::from_secs(5 * 60),          // 5 minutes
62            quality_update_interval: Duration::from_secs(3600),  // 1 hour
63            cleanup_interval: Duration::from_secs(6 * 3600),     // 6 hours
64            min_peers_to_save: 10,
65            enable_file_locking: true,
66            weights: QualityWeights::default(),
67        }
68    }
69}
70
71impl Default for QualityWeights {
72    fn default() -> Self {
73        Self {
74            success_rate: 0.4,
75            rtt: 0.25,
76            freshness: 0.15,
77            capabilities: 0.2,
78        }
79    }
80}
81
82impl BootstrapCacheConfig {
83    /// Create a new configuration builder
84    pub fn builder() -> BootstrapCacheConfigBuilder {
85        BootstrapCacheConfigBuilder::default()
86    }
87}
88
89/// Builder for BootstrapCacheConfig
90#[derive(Default)]
91pub struct BootstrapCacheConfigBuilder {
92    config: BootstrapCacheConfig,
93}
94
95impl BootstrapCacheConfigBuilder {
96    /// Set the cache directory
97    pub fn cache_dir(mut self, dir: impl Into<PathBuf>) -> Self {
98        self.config.cache_dir = dir.into();
99        self
100    }
101
102    /// Set maximum number of peers
103    pub fn max_peers(mut self, max: usize) -> Self {
104        self.config.max_peers = max;
105        self
106    }
107
108    /// Set epsilon for exploration rate (clamped to 0.0-1.0)
109    pub fn epsilon(mut self, epsilon: f64) -> Self {
110        self.config.epsilon = epsilon.clamp(0.0, 1.0);
111        self
112    }
113
114    /// Set stale threshold duration
115    pub fn stale_threshold(mut self, duration: Duration) -> Self {
116        self.config.stale_threshold = duration;
117        self
118    }
119
120    /// Set save interval
121    pub fn save_interval(mut self, duration: Duration) -> Self {
122        self.config.save_interval = duration;
123        self
124    }
125
126    /// Set quality update interval
127    pub fn quality_update_interval(mut self, duration: Duration) -> Self {
128        self.config.quality_update_interval = duration;
129        self
130    }
131
132    /// Set cleanup interval
133    pub fn cleanup_interval(mut self, duration: Duration) -> Self {
134        self.config.cleanup_interval = duration;
135        self
136    }
137
138    /// Set minimum peers required to save
139    pub fn min_peers_to_save(mut self, min: usize) -> Self {
140        self.config.min_peers_to_save = min;
141        self
142    }
143
144    /// Enable or disable file locking
145    pub fn enable_file_locking(mut self, enable: bool) -> Self {
146        self.config.enable_file_locking = enable;
147        self
148    }
149
150    /// Set quality weights
151    pub fn weights(mut self, weights: QualityWeights) -> Self {
152        self.config.weights = weights;
153        self
154    }
155
156    /// Build the configuration
157    pub fn build(self) -> BootstrapCacheConfig {
158        self.config
159    }
160}
161
162fn default_cache_dir() -> PathBuf {
163    // Try platform-specific cache directory, fallback to current directory
164    if let Some(cache_dir) = dirs::cache_dir() {
165        cache_dir.join("ant-quic")
166    } else if let Some(home) = dirs::home_dir() {
167        home.join(".cache").join("ant-quic")
168    } else {
169        PathBuf::from(".ant-quic-cache")
170    }
171}
172
173#[cfg(test)]
174mod tests {
175    use super::*;
176
177    #[test]
178    fn test_default_config() {
179        let config = BootstrapCacheConfig::default();
180        assert_eq!(config.max_peers, 20_000);
181        assert!((config.epsilon - 0.1).abs() < f64::EPSILON);
182        assert_eq!(config.stale_threshold, Duration::from_secs(7 * 24 * 3600));
183    }
184
185    #[test]
186    fn test_builder() {
187        let config = BootstrapCacheConfig::builder()
188            .max_peers(10_000)
189            .epsilon(0.2)
190            .cache_dir("/tmp/test")
191            .build();
192
193        assert_eq!(config.max_peers, 10_000);
194        assert!((config.epsilon - 0.2).abs() < f64::EPSILON);
195        assert_eq!(config.cache_dir, PathBuf::from("/tmp/test"));
196    }
197
198    #[test]
199    fn test_epsilon_clamping() {
200        let config = BootstrapCacheConfig::builder().epsilon(1.5).build();
201        assert!((config.epsilon - 1.0).abs() < f64::EPSILON);
202
203        let config = BootstrapCacheConfig::builder().epsilon(-0.5).build();
204        assert!(config.epsilon.abs() < f64::EPSILON);
205    }
206}