1use anyhow::Result;
2use fuzzy_matcher::skim::SkimMatcherV2;
3use fuzzy_matcher::FuzzyMatcher;
4
5pub struct Prompts {
6 storage: Box<dyn crate::storage::Storage + Send + Sync>,
7}
8
9impl Prompts {
10 pub fn new(storage: Box<dyn crate::storage::Storage + Send + Sync>) -> Self {
11 Self { storage }
12 }
13
14 pub async fn add_prompt(&self, prompt: &mut crate::storage::Prompt) -> Result<()> {
15 self.storage.save_prompt(prompt)
16 }
17
18 pub async fn list_prompts(&self) -> Result<Vec<crate::storage::Prompt>> {
19 self.storage.load_prompts()
20 }
21
22 pub async fn show_prompt(&self, query: &str) -> Result<Vec<crate::storage::Prompt>> {
23 let prompts = self.storage.load_prompts()?;
24 let search_results = search_prompts(&prompts, query, &[], &[]);
25 Ok(search_results)
26 }
27
28 pub async fn edit_prompt(&self, hash: &str, new_prompt: &mut crate::storage::Prompt) -> Result<()> {
29 self.storage.delete_prompt(hash)?;
30 self.storage.save_prompt(new_prompt)
31 }
32
33 pub async fn delete_prompt(&self, hash: &str) -> Result<()> {
34 self.storage.delete_prompt(hash)
35 }
36}
37
38pub fn search_prompts(prompts: &[crate::storage::Prompt], query: &str, tags: &[String], categories: &[String]) -> Vec<crate::storage::Prompt> {
39 let matcher = SkimMatcherV2::default();
40 prompts.iter().filter(|p| {
41 let content_match = query.is_empty() || matcher.fuzzy_match(&p.content, query).is_some();
42 let tags_match = tags.is_empty() || p.tags.as_ref().map_or(false, |ptags| {
43 tags.iter().all(|tag| ptags.contains(tag))
44 });
45 let categories_match = categories.is_empty() || p.categories.as_ref().map_or(false, |pcats| {
46 categories.iter().all(|cat| pcats.contains(cat))
47 });
48 content_match && tags_match && categories_match
49 }).cloned().collect()
50}