use std::collections::HashMap;
use std::error::Error;
use crate::config::UpdateExpirationValue;
use crate::UpdateExpirationMode;
pub struct Update {
content: Vec<u8>,
digest: String,
}
impl Update {
pub fn new(content: Vec<u8>) -> Self {
let digest = blake3::hash(&content).to_hex().to_string();
Update {
content,
digest,
}
}
pub fn content(&self) -> &Vec<u8> {
&self.content
}
pub fn digest(&self) -> &String {
&self.digest
}
}
pub trait UpdateHandler {
fn on_update(&self, update: Update);
}
pub struct UpdateDecorator {
active_updates: HashMap<String, (Update, UpdateExpirationValue)>,
removed_updates: Vec<String>,
expiration_mode: UpdateExpirationMode,
max_expired_size: usize,
max_expired_margin: f64,
}
impl UpdateDecorator {
pub fn new(expiration_mode: UpdateExpirationMode) -> Self {
Self{
active_updates: HashMap::new(),
removed_updates: Vec::new(),
expiration_mode,
max_expired_size: 10000,
max_expired_margin: 0.5
}
}
pub fn active_count(&self) -> usize {
self.active_updates.len()
}
pub fn active_headers(&self) -> Vec<String> {
self.active_updates.iter().map(|(header, _)| header.to_owned()).collect()
}
pub fn is_new(&self, digest: &String) -> bool {
!self.active_updates.contains_key(digest) && !self.removed_updates.contains(&digest)
}
pub fn is_expired(&self, digest: &String) -> bool {
self.removed_updates.contains(&digest)
}
pub fn is_active(&self, digest: &String) -> bool {
self.active_updates.contains_key(digest)
}
pub fn get_update(&self, digest: &str) -> Option<&Update> {
self.active_updates.get(digest).map_or(None, |(update, _)| Some(update))
}
pub fn insert_update(&mut self, update: Update) -> Result<(), Box<dyn Error>> {
if let None = self.active_updates.insert(update.digest().to_owned(), (update, UpdateExpirationValue::new(self.expiration_mode.clone()))) {
Ok(())
}
else {
Err("Update already existed")?
}
}
pub fn clear(&mut self) {
self.active_updates.clear();
self.removed_updates.clear();
}
pub fn active_headers_for_push(&mut self) -> Vec<String> {
let mut headers = Vec::new();
self.active_updates.iter_mut()
.for_each(|(digest, (_, expiration))| {
expiration.increase_push_count();
headers.push(digest.clone());
});
headers
}
pub fn clear_expired(&mut self) {
match self.expiration_mode {
UpdateExpirationMode::None => (),
UpdateExpirationMode::MostRecent(size, margin) => {
let max_size = size + (size as f64 * margin) as usize;
if self.active_updates.len() > max_size {
let removal_count = self.active_updates.len() - max_size;
let mut removal_keys: Vec<(String, std::time::Instant)> = Vec::new();
for(digest, (_, expiration_value)) in &self.active_updates {
match expiration_value {
UpdateExpirationValue::MostRecent(created) => removal_keys.push((digest.to_owned(), created.clone())),
_ => (),
}
}
removal_keys.sort_by_key(|(_, created)| *created);
for i in 0..removal_count {
self.active_updates.remove(&removal_keys[i].0);
self.removed_updates.push(removal_keys[i].0.clone());
}
}
},
UpdateExpirationMode::PushCount(_) | UpdateExpirationMode::DurationMillis(_) => {
let expired_keys: Vec<String> = self.active_updates.iter()
.filter(|(_, (_, expiration_value))| expiration_value.has_expired())
.map(|(digest, (_, _))| digest.to_owned())
.collect();
for key in expired_keys {
self.active_updates.remove(&key);
self.removed_updates.push(key.clone());
}
}
}
let margin_size = (self.max_expired_size as f64 * self.max_expired_margin) as usize;
let max_expired = self.max_expired_size + margin_size;
if self.removed_updates.len() > max_expired && margin_size > 0 {
self.removed_updates.drain(0..margin_size);
}
}
}