prompts_cli/
storage.rs

1use anyhow::Result;
2use dirs;
3use sha2::{Sha256, Digest};
4use serde::{Serialize, Deserialize};
5use std::path::PathBuf;
6
7#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
8pub struct Prompt {
9    pub content: String,
10    pub tags: Option<Vec<String>>,
11    pub categories: Option<Vec<String>>,
12    pub hash: String,
13}
14
15impl Prompt {
16    pub fn new(content: &str, tags: Option<Vec<String>>, categories: Option<Vec<String>>) -> Self {
17        let hash = Sha256::digest(content.as_bytes());
18        Self {
19            content: content.to_string(),
20            tags,
21            categories,
22            hash: format!("{:x}", hash),
23        }
24    }
25}
26
27pub trait Storage {
28    fn save_prompt(&self, prompt: &mut Prompt) -> Result<()>;
29    fn load_prompts(&self) -> Result<Vec<Prompt>>;
30    fn delete_prompt(&self, hash: &str) -> Result<()>;
31}
32
33pub struct JsonStorage {
34    storage_path: PathBuf,
35}
36
37impl JsonStorage {
38    pub fn new(storage_path: Option<PathBuf>) -> Result<Self> {
39        let path = match storage_path {
40            Some(path) => path,
41            None => {
42                let mut default_path = dirs::config_dir().ok_or_else(|| anyhow::anyhow!("Could not find config directory"))?;
43                default_path.push("prompts-cli");
44                default_path.push("prompts");
45                std::fs::create_dir_all(&default_path)?;
46                default_path
47            }
48        };
49        Ok(Self { storage_path: path })
50    }
51}
52
53impl Storage for JsonStorage {
54    fn save_prompt(&self, prompt: &mut Prompt) -> Result<()> {
55        let file_path = self.storage_path.join(format!("{}.json", prompt.hash));
56        let json = serde_json::to_string_pretty(prompt)?;
57        std::fs::write(file_path, json)?;
58        Ok(())
59    }
60
61    fn load_prompts(&self) -> Result<Vec<Prompt>> {
62        let mut prompts = Vec::new();
63        for entry in std::fs::read_dir(&self.storage_path)? {
64            let entry = entry?;
65            let path = entry.path();
66            if path.is_file() && path.extension().map_or(false, |ext| ext == "json") {
67                let json = std::fs::read_to_string(&path)?;
68                let prompt: Prompt = serde_json::from_str(&json)?;
69                prompts.push(prompt);
70            }
71        }
72        Ok(prompts)
73    }
74
75    fn delete_prompt(&self, hash: &str) -> Result<()> {
76        let file_path = self.storage_path.join(format!("{}.json", hash));
77        if file_path.exists() {
78            std::fs::remove_file(file_path)?;
79        }
80        Ok(())
81    }
82}