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
47    fn make_saved_search(name: &str, query: &str) -> SavedSearch {
48        SavedSearch {
49            id: SavedSearchId::new(),
50            account_id: None,
51            name: name.to_string(),
52            query: query.to_string(),
53            sort: SortOrder::DateDesc,
54            icon: None,
55            position: 0,
56            created_at: chrono::Utc::now(),
57        }
58    }
59
60    #[tokio::test]
61    async fn saved_create_list_delete() {
62        let store = Arc::new(Store::in_memory().await.unwrap());
63        let svc = SavedSearchService::new(store);
64
65        let search = make_saved_search("Unread from Alice", "from:alice is:unread");
66        svc.create(&search).await.unwrap();
67
68        let list = svc.list().await.unwrap();
69        assert_eq!(list.len(), 1);
70        assert_eq!(list[0].name, "Unread from Alice");
71
72        svc.delete(&search.id).await.unwrap();
73        let list = svc.list().await.unwrap();
74        assert_eq!(list.len(), 0);
75    }
76
77    #[tokio::test]
78    async fn saved_get_by_name() {
79        let store = Arc::new(Store::in_memory().await.unwrap());
80        let svc = SavedSearchService::new(store);
81
82        let search = make_saved_search("Important", "is:starred");
83        svc.create(&search).await.unwrap();
84
85        let found = svc.get_by_name("Important").await.unwrap();
86        assert!(found.is_some());
87        assert_eq!(found.unwrap().query, "is:starred");
88
89        let not_found = svc.get_by_name("Nonexistent").await.unwrap();
90        assert!(not_found.is_none());
91    }
92
93    #[tokio::test]
94    async fn saved_duplicate_names_allowed() {
95        let store = Arc::new(Store::in_memory().await.unwrap());
96        let svc = SavedSearchService::new(store);
97
98        let s1 = make_saved_search("Inbox", "label:inbox");
99        let s2 = make_saved_search("Inbox", "label:inbox is:unread");
100        svc.create(&s1).await.unwrap();
101        svc.create(&s2).await.unwrap();
102
103        let list = svc.list().await.unwrap();
104        assert_eq!(list.len(), 2);
105    }
106}