halldyll_core/fetch/
conditional.rs1use std::collections::HashMap;
4use std::sync::RwLock;
5use url::Url;
6
7pub struct ConditionalCache {
9 entries: RwLock<HashMap<String, CacheEntry>>,
11 max_size: usize,
13}
14
15#[derive(Debug, Clone)]
17pub struct CacheEntry {
18 pub etag: Option<String>,
20 pub last_modified: Option<String>,
22 pub cached_at: std::time::Instant,
24}
25
26impl Default for ConditionalCache {
27 fn default() -> Self {
28 Self::new(10000)
29 }
30}
31
32impl ConditionalCache {
33 pub fn new(max_size: usize) -> Self {
35 Self {
36 entries: RwLock::new(HashMap::new()),
37 max_size,
38 }
39 }
40
41 fn cache_key(url: &Url) -> String {
43 let mut url = url.clone();
45 url.set_fragment(None);
46 url.to_string()
47 }
48
49 pub fn get(&self, url: &Url) -> Option<CacheEntry> {
51 let key = Self::cache_key(url);
52 self.entries.read().ok()?.get(&key).cloned()
53 }
54
55 pub fn set(&self, url: &Url, etag: Option<String>, last_modified: Option<String>) {
57 let key = Self::cache_key(url);
58 let entry = CacheEntry {
59 etag,
60 last_modified,
61 cached_at: std::time::Instant::now(),
62 };
63
64 if let Ok(mut entries) = self.entries.write() {
65 if entries.len() >= self.max_size {
67 let to_remove: Vec<_> = entries
69 .iter()
70 .take(self.max_size / 10)
71 .map(|(k, _)| k.clone())
72 .collect();
73 for k in to_remove {
74 entries.remove(&k);
75 }
76 }
77 entries.insert(key, entry);
78 }
79 }
80
81 pub fn remove(&self, url: &Url) {
83 let key = Self::cache_key(url);
84 if let Ok(mut entries) = self.entries.write() {
85 entries.remove(&key);
86 }
87 }
88
89 pub fn clear(&self) {
91 if let Ok(mut entries) = self.entries.write() {
92 entries.clear();
93 }
94 }
95
96 pub fn len(&self) -> usize {
98 self.entries.read().map(|e| e.len()).unwrap_or(0)
99 }
100
101 pub fn is_empty(&self) -> bool {
103 self.len() == 0
104 }
105}
106
107pub struct ConditionalRequest {
109 pub etag: Option<String>,
111 pub last_modified: Option<String>,
113}
114
115impl ConditionalRequest {
116 pub fn new() -> Self {
118 Self {
119 etag: None,
120 last_modified: None,
121 }
122 }
123
124 pub fn from_cache(entry: &CacheEntry) -> Self {
126 Self {
127 etag: entry.etag.clone(),
128 last_modified: entry.last_modified.clone(),
129 }
130 }
131
132 pub fn has_conditions(&self) -> bool {
134 self.etag.is_some() || self.last_modified.is_some()
135 }
136}
137
138impl Default for ConditionalRequest {
139 fn default() -> Self {
140 Self::new()
141 }
142}