use std::collections::{HashMap, hash_map::DefaultHasher}; use std::hash::{Hasher, Hash}; use serde::{Deserialize, Serialize, Serializer, Deserializer}; const SHORTTERM_CAP: usize = 20; const LONGTERM_CAP: usize = 100; // Holds a long term storage that tracks how often a search // result was activated, and a short term storage that stores // the order of recently activated search results (higher // vales are more recent). // Keys for both mappings are hashes of the acvtivated result's // command string. #[derive(Debug, Default)] pub struct RecentUseStorage { long_term: HashMap, short_term: HashMap, } fn hash_key(key: K) -> usize { let mut hasher = DefaultHasher::new(); key.hash(&mut hasher); hasher.finish() as usize } impl RecentUseStorage { pub fn add(&mut self, exec: &K) { let key = hash_key(exec); *self.long_term.entry(key).or_insert(0) += 1; let short_term_idx = self.short_term.values().max().unwrap_or( &0)+1; self.short_term.insert(key, short_term_idx); self.trim() } fn trim(&mut self) { while self.short_term.len() > SHORTTERM_CAP { let key = *self.short_term.iter().min_by_key(|kv| kv.1).unwrap().0; self.short_term.remove(&key); } while self.long_term.values().sum::() > LONGTERM_CAP { let mut delete_keys = Vec::new(); for (k, v) in self.long_term.iter_mut() { *v /= 2; if *v == 0 { delete_keys.push(*k); } } for k in delete_keys { self.long_term.remove(&k); } } } pub fn get_recent(&self, exec: &K) -> usize { self.short_term.get(&hash_key(exec)).copied().unwrap_or(0) } pub fn get_freq(&self, exec: &K) -> usize { self.long_term.get(&hash_key(exec)).copied().unwrap_or(0) } } impl Serialize for RecentUseStorage { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let mut stvec: Vec<_> = self.short_term.keys().copied().collect(); stvec.sort_by_key(|k| self.short_term[k]); (&self.long_term, stvec).serialize(serializer) } } impl<'de> Deserialize<'de> for RecentUseStorage { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { type SerType = (HashMap, Vec); let (long_term, stv) = SerType::deserialize(deserializer)?; let short_term: HashMap<_, _> = stv.into_iter().enumerate().map(|(v,k)| (k,v)).collect(); Ok(RecentUseStorage { long_term, short_term, }) } }