anesis 0.9.1

CLI for scaffolding projects from remote templates and extending them with project addons
Documentation
use std::{fs, path::Path};

use anyhow::Result;
use serde::{Deserialize, Serialize};

const LOCK_FILE_NAME: &str = "anesis.lock";

#[derive(Debug, Default, Serialize, Deserialize)]
pub struct LockFile {
  pub addons: Vec<LockEntry>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct LockEntry {
  pub id: String,
  pub version: String,
  pub variant: String,
  pub commands_executed: Vec<String>,
}

impl LockFile {
  pub fn load(project_root: &Path) -> Result<Self> {
    let path = project_root.join(LOCK_FILE_NAME);
    if !path.exists() {
      return Ok(Self::default());
    }
    let contents = fs::read_to_string(&path)?;
    let lock: Self = serde_json::from_str(&contents)?;
    Ok(lock)
  }

  pub fn save(&self, project_root: &Path) -> Result<()> {
    let path = project_root.join(LOCK_FILE_NAME);
    let contents = serde_json::to_string_pretty(self)?;
    fs::write(path, contents)?;
    Ok(())
  }

  pub fn is_command_executed(&self, addon_id: &str, command: &str) -> bool {
    self
      .addons
      .iter()
      .find(|e| e.id == addon_id)
      .map(|e| e.commands_executed.iter().any(|c| c == command))
      .unwrap_or(false)
  }

  pub fn addon_version(&self, addon_id: &str) -> Option<&str> {
    self
      .addons
      .iter()
      .find(|e| e.id == addon_id)
      .map(|entry| entry.version.as_str())
  }

  pub fn mark_command_executed(&mut self, addon_id: &str, command: &str) {
    if let Some(entry) = self.addons.iter_mut().find(|e| e.id == addon_id)
      && !entry.commands_executed.iter().any(|c| c == command)
    {
      entry.commands_executed.push(command.to_string());
    }
  }

  pub fn upsert_entry(&mut self, entry: LockEntry) {
    if let Some(existing) = self.addons.iter_mut().find(|e| e.id == entry.id) {
      *existing = entry;
    } else {
      self.addons.push(entry);
    }
  }
}