memory.rs
1 use super::traits::*; 2 use super::variable::{ParsedVariable, VariableSource}; 3 use crate::error::SourceError; 4 use compact_str::CompactString; 5 use indexmap::IndexMap; 6 use parking_lot::Mutex; 7 8 pub struct MemorySource { 9 id: SourceId, 10 variables: Mutex<IndexMap<CompactString, ParsedVariable>>, 11 version: Mutex<u64>, 12 last_loaded_version: Mutex<Option<u64>>, 13 } 14 15 impl MemorySource { 16 pub fn new() -> Self { 17 Self { 18 id: SourceId::new("memory"), 19 variables: Mutex::new(IndexMap::new()), 20 version: Mutex::new(0), 21 last_loaded_version: Mutex::new(None), 22 } 23 } 24 25 pub fn set(&self, key: impl Into<CompactString>, value: impl Into<CompactString>) { 26 let key = key.into(); 27 let value = value.into(); 28 let mut vars = self.variables.lock(); 29 vars.insert( 30 key.clone(), 31 ParsedVariable { 32 key: key.clone(), 33 raw_value: value, 34 source: VariableSource::Memory, 35 description: None, 36 is_commented: false, 37 }, 38 ); 39 *self.version.lock() += 1; 40 } 41 42 pub fn set_with_description( 43 &self, 44 key: impl Into<CompactString>, 45 value: impl Into<CompactString>, 46 description: impl Into<CompactString>, 47 ) { 48 let key = key.into(); 49 let value = value.into(); 50 let description = description.into(); 51 let mut vars = self.variables.lock(); 52 vars.insert( 53 key.clone(), 54 ParsedVariable { 55 key: key.clone(), 56 raw_value: value, 57 source: VariableSource::Memory, 58 description: Some(description), 59 is_commented: false, 60 }, 61 ); 62 *self.version.lock() += 1; 63 } 64 65 pub fn remove(&self, key: &str) -> Option<ParsedVariable> { 66 let mut vars = self.variables.lock(); 67 let removed = vars.swap_remove(key); 68 if removed.is_some() { 69 *self.version.lock() += 1; 70 } 71 removed 72 } 73 74 pub fn clear(&self) { 75 let mut vars = self.variables.lock(); 76 vars.clear(); 77 *self.version.lock() += 1; 78 } 79 80 pub fn len(&self) -> usize { 81 self.variables.lock().len() 82 } 83 84 pub fn is_empty(&self) -> bool { 85 self.variables.lock().is_empty() 86 } 87 } 88 89 impl Default for MemorySource { 90 fn default() -> Self { 91 Self::new() 92 } 93 } 94 95 impl EnvSource for MemorySource { 96 fn id(&self) -> &SourceId { 97 &self.id 98 } 99 100 fn source_type(&self) -> SourceType { 101 SourceType::Memory 102 } 103 104 fn priority(&self) -> Priority { 105 Priority::MEMORY 106 } 107 108 fn capabilities(&self) -> SourceCapabilities { 109 SourceCapabilities::READ | SourceCapabilities::WRITE | SourceCapabilities::CACHEABLE 110 } 111 112 fn load(&self) -> Result<SourceSnapshot, SourceError> { 113 let vars: Vec<ParsedVariable> = self.variables.lock().values().cloned().collect(); 114 let current_version = *self.version.lock(); 115 116 // Update last loaded version 117 *self.last_loaded_version.lock() = Some(current_version); 118 119 Ok(SourceSnapshot { 120 source_id: self.id.clone(), 121 variables: vars.into(), 122 timestamp: std::time::Instant::now(), 123 version: Some(current_version), 124 }) 125 } 126 127 fn has_changed(&self) -> bool { 128 let current = *self.version.lock(); 129 *self.last_loaded_version.lock() != Some(current) 130 } 131 132 fn invalidate(&self) {} 133 } 134 135 #[cfg(test)] 136 mod tests { 137 use super::*; 138 139 #[test] 140 fn test_memory_source() { 141 let source = MemorySource::new(); 142 143 source.set("KEY1", "value1"); 144 source.set("KEY2", "value2"); 145 146 let snapshot = source.load().unwrap(); 147 assert_eq!(snapshot.variables.len(), 2); 148 assert_eq!(snapshot.variables[0].key.as_str(), "KEY1"); 149 assert_eq!(snapshot.variables[0].raw_value.as_str(), "value1"); 150 } 151 152 #[test] 153 fn test_remove() { 154 let source = MemorySource::new(); 155 156 source.set("KEY1", "value1"); 157 assert_eq!(source.len(), 1); 158 159 let removed = source.remove("KEY1"); 160 assert!(removed.is_some()); 161 assert_eq!(source.len(), 0); 162 } 163 164 #[test] 165 fn test_version() { 166 let source = MemorySource::new(); 167 168 source.set("KEY1", "value1"); 169 let v1 = source.load().unwrap().version.unwrap(); 170 171 source.set("KEY2", "value2"); 172 let v2 = source.load().unwrap().version.unwrap(); 173 174 assert!(v2 > v1); 175 } 176 }