1use mxr_core::id::SavedSearchId;
2use mxr_core::types::SavedSearch;
3use mxr_store::Store;
4use std::sync::Arc;
5
6pub struct SavedSearchService {
7 store: Arc<Store>,
8}
9
10impl SavedSearchService {
11 pub fn new(store: Arc<Store>) -> Self {
12 Self { store }
13 }
14
15 pub async fn create(&self, search: &SavedSearch) -> Result<(), mxr_core::MxrError> {
16 self.store
17 .insert_saved_search(search)
18 .await
19 .map_err(|e| mxr_core::MxrError::Store(e.to_string()))
20 }
21
22 pub async fn list(&self) -> Result<Vec<SavedSearch>, mxr_core::MxrError> {
23 self.store
24 .list_saved_searches()
25 .await
26 .map_err(|e| mxr_core::MxrError::Store(e.to_string()))
27 }
28
29 pub async fn delete(&self, id: &SavedSearchId) -> Result<(), mxr_core::MxrError> {
30 self.store
31 .delete_saved_search(id)
32 .await
33 .map_err(|e| mxr_core::MxrError::Store(e.to_string()))
34 }
35
36 pub async fn get_by_name(&self, name: &str) -> Result<Option<SavedSearch>, mxr_core::MxrError> {
37 let searches = self.list().await?;
38 Ok(searches.into_iter().find(|s| s.name == name))
39 }
40}
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45 use mxr_core::types::SortOrder;
46 use mxr_core::SearchMode;
47
48 fn make_saved_search(name: &str, query: &str) -> SavedSearch {
49 SavedSearch {
50 id: SavedSearchId::new(),
51 account_id: None,
52 name: name.to_string(),
53 query: query.to_string(),
54 search_mode: SearchMode::Lexical,
55 sort: SortOrder::DateDesc,
56 icon: None,
57 position: 0,
58 created_at: chrono::Utc::now(),
59 }
60 }
61
62 #[tokio::test]
63 async fn saved_create_list_delete() {
64 let store = Arc::new(Store::in_memory().await.unwrap());
65 let svc = SavedSearchService::new(store);
66
67 let search = make_saved_search("Unread from Alice", "from:alice is:unread");
68 svc.create(&search).await.unwrap();
69
70 let list = svc.list().await.unwrap();
71 assert_eq!(list.len(), 1);
72 assert_eq!(list[0].name, "Unread from Alice");
73
74 svc.delete(&search.id).await.unwrap();
75 let list = svc.list().await.unwrap();
76 assert_eq!(list.len(), 0);
77 }
78
79 #[tokio::test]
80 async fn saved_get_by_name() {
81 let store = Arc::new(Store::in_memory().await.unwrap());
82 let svc = SavedSearchService::new(store);
83
84 let search = make_saved_search("Important", "is:starred");
85 svc.create(&search).await.unwrap();
86
87 let found = svc.get_by_name("Important").await.unwrap();
88 assert!(found.is_some());
89 assert_eq!(found.unwrap().query, "is:starred");
90
91 let not_found = svc.get_by_name("Nonexistent").await.unwrap();
92 assert!(not_found.is_none());
93 }
94
95 #[tokio::test]
96 async fn saved_duplicate_names_allowed() {
97 let store = Arc::new(Store::in_memory().await.unwrap());
98 let svc = SavedSearchService::new(store);
99
100 let s1 = make_saved_search("Inbox", "label:inbox");
101 let s2 = make_saved_search("Inbox", "label:inbox is:unread");
102 svc.create(&s1).await.unwrap();
103 svc.create(&s2).await.unwrap();
104
105 let list = svc.list().await.unwrap();
106 assert_eq!(list.len(), 2);
107 }
108}