Skip to main content

mxr_search/
saved.rs

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}