/ src / source / memory.rs
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  }