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 addon_version(&self, addon_id: &str) -> Option<&str> {
49    self
50      .addons
51      .iter()
52      .find(|e| e.id == addon_id)
53      .map(|entry| entry.version.as_str())
54  }
55
56  pub fn mark_command_executed(&mut self, addon_id: &str, command: &str) {
57    if let Some(entry) = self.addons.iter_mut().find(|e| e.id == addon_id)
58      && !entry.commands_executed.iter().any(|c| c == command)
59    {
60      entry.commands_executed.push(command.to_string());
61    }
62  }
63
64  pub fn upsert_entry(&mut self, entry: LockEntry) {
65    if let Some(existing) = self.addons.iter_mut().find(|e| e.id == entry.id) {
66      *existing = entry;
67    } else {
68      self.addons.push(entry);
69    }
70  }
71}