Skip to main content

oxide_cli/addons/
lock.rs

1use std::{fs, path::Path};
2
3use anyhow::Result;
4use serde::{Deserialize, Serialize};
5
6const LOCK_FILE_NAME: &str = "oxide.lock";
7
8#[derive(Debug, Default, Serialize, Deserialize)]
9pub struct LockFile {
10  pub addons: Vec<LockEntry>,
11}
12
13#[derive(Debug, Serialize, Deserialize)]
14pub struct LockEntry {
15  pub id: String,
16  pub version: String,
17  pub variant: String,
18  pub commands_executed: Vec<String>,
19}
20
21impl LockFile {
22  pub fn load(project_root: &Path) -> Result<Self> {
23    let path = project_root.join(LOCK_FILE_NAME);
24    if !path.exists() {
25      return Ok(Self::default());
26    }
27    let contents = fs::read_to_string(&path)?;
28    let lock: Self = serde_json::from_str(&contents)?;
29    Ok(lock)
30  }
31
32  pub fn save(&self, project_root: &Path) -> Result<()> {
33    let path = project_root.join(LOCK_FILE_NAME);
34    let contents = serde_json::to_string_pretty(self)?;
35    fs::write(path, contents)?;
36    Ok(())
37  }
38
39  pub fn is_command_executed(&self, addon_id: &str, command: &str) -> bool {
40    self
41      .addons
42      .iter()
43      .find(|e| e.id == addon_id)
44      .map(|e| e.commands_executed.iter().any(|c| c == command))
45      .unwrap_or(false)
46  }
47
48  pub fn mark_command_executed(&mut self, addon_id: &str, command: &str) {
49    if let Some(entry) = self.addons.iter_mut().find(|e| e.id == addon_id)
50      && !entry.commands_executed.iter().any(|c| c == command)
51    {
52      entry.commands_executed.push(command.to_string());
53    }
54  }
55
56  pub fn upsert_entry(&mut self, entry: LockEntry) {
57    if let Some(existing) = self.addons.iter_mut().find(|e| e.id == entry.id) {
58      *existing = entry;
59    } else {
60      self.addons.push(entry);
61    }
62  }
63}