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}