1use std::collections::HashMap;
7use std::sync::{Arc, Mutex};
8
9#[derive(Clone, Debug, PartialEq, Default)]
11pub struct DescriptorEntry {
12 pub mw: Option<f64>,
14 pub logp: Option<f64>,
16 pub tpsa: Option<f64>,
18 pub hba: Option<usize>,
20 pub hbd: Option<usize>,
22 pub rotb: Option<usize>,
24}
25
26#[derive(Clone, Debug)]
28pub struct DescriptorCache {
29 cache: Arc<Mutex<HashMap<String, DescriptorEntry>>>,
30 max_size: usize,
31}
32
33impl DescriptorCache {
34 pub fn new(max_size: usize) -> Self {
36 Self {
37 cache: Arc::new(Mutex::new(HashMap::new())),
38 max_size,
39 }
40 }
41
42 pub fn get(&self, smiles: &str) -> Option<DescriptorEntry> {
44 self.cache.lock().ok().and_then(|c| c.get(smiles).cloned())
45 }
46
47 pub fn put(&self, smiles: String, entry: DescriptorEntry) {
49 if let Ok(mut cache) = self.cache.lock() {
50 if !cache.contains_key(&smiles) && cache.len() >= self.max_size {
52 if let Some(key) = cache.keys().next().cloned() {
54 cache.remove(&key);
55 }
56 }
57 cache.insert(smiles, entry);
58 }
59 }
60
61 pub fn clear(&self) {
63 if let Ok(mut cache) = self.cache.lock() {
64 cache.clear();
65 }
66 }
67
68 pub fn len(&self) -> usize {
70 self.cache.lock().map(|c| c.len()).unwrap_or(0)
71 }
72
73 pub fn is_empty(&self) -> bool {
75 self.len() == 0
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn cache_put_get() {
85 let cache = DescriptorCache::new(100);
86 let entry = DescriptorEntry {
87 mw: Some(46.0),
88 ..DescriptorEntry::default()
89 };
90
91 cache.put("CC".to_string(), entry.clone());
92 let retrieved = cache.get("CC").unwrap();
93 assert_eq!(retrieved.mw, Some(46.0));
94 }
95
96 #[test]
97 fn cache_miss() {
98 let cache = DescriptorCache::new(100);
99 assert_eq!(cache.get("CC"), None);
100 }
101
102 #[test]
103 fn cache_eviction() {
104 let cache = DescriptorCache::new(2);
105 let entry = DescriptorEntry::default();
106
107 cache.put("C1".to_string(), entry.clone());
108 cache.put("C2".to_string(), entry.clone());
109 cache.put("C3".to_string(), entry.clone());
110
111 assert!(cache.len() <= 2);
113 }
114
115 #[test]
116 fn cache_clear() {
117 let cache = DescriptorCache::new(100);
118 let entry = DescriptorEntry::default();
119
120 cache.put("CC".to_string(), entry);
121 assert!(!cache.is_empty());
122
123 cache.clear();
124 assert!(cache.is_empty());
125 }
126}