use anyhow::{Context, Result};
use std::path::PathBuf;
use super::types::Recipe;
pub struct RecipeStore {
dir: PathBuf,
}
impl RecipeStore {
pub fn open() -> Result<Self> {
let dir = dirs::home_dir()
.ok_or_else(|| anyhow::anyhow!("Cannot determine home directory"))?
.join(".ghost")
.join("recipes");
std::fs::create_dir_all(&dir)
.with_context(|| format!("Failed to create recipe dir: {}", dir.display()))?;
Ok(Self { dir })
}
pub fn open_at(dir: PathBuf) -> Result<Self> {
std::fs::create_dir_all(&dir)?;
Ok(Self { dir })
}
pub fn list(&self) -> Result<Vec<Recipe>> {
let mut recipes = vec![];
for entry in std::fs::read_dir(&self.dir)? {
let entry = entry?;
let path = entry.path();
if path.extension().and_then(|e| e.to_str()) == Some("json") {
if let Ok(r) = self.load_file(&path) {
recipes.push(r);
}
}
}
recipes.sort_by(|a, b| a.name.cmp(&b.name));
Ok(recipes)
}
pub fn get(&self, name: &str) -> Result<Recipe> {
let path = self.recipe_path(name);
self.load_file(&path)
.with_context(|| format!("Recipe '{}' not found", name))
}
pub fn save(&self, recipe: &Recipe) -> Result<()> {
let path = self.recipe_path(&recipe.name);
let json = serde_json::to_string_pretty(recipe)?;
std::fs::write(&path, json)
.with_context(|| format!("Failed to write recipe to {}", path.display()))
}
pub fn save_json(&self, json: &str) -> Result<Recipe> {
let recipe: Recipe = serde_json::from_str(json)
.context("Invalid recipe JSON")?;
self.save(&recipe)?;
Ok(recipe)
}
pub fn delete(&self, name: &str) -> Result<()> {
let path = self.recipe_path(name);
if path.exists() {
std::fs::remove_file(&path)
.with_context(|| format!("Failed to delete recipe '{}'", name))
} else {
Err(anyhow::anyhow!("Recipe '{}' not found", name))
}
}
fn recipe_path(&self, name: &str) -> PathBuf {
let safe = name.replace(['/', '\\', ':', '*', '?', '"', '<', '>', '|'], "_");
self.dir.join(format!("{safe}.json"))
}
fn load_file(&self, path: &PathBuf) -> Result<Recipe> {
let data = std::fs::read_to_string(path)?;
serde_json::from_str(&data).with_context(|| format!("Invalid JSON in {}", path.display()))
}
}