Skip to main content

notedthat_core/search/
error.rs

1use thiserror::Error;
2
3/// Errors returned by the search subsystem.
4///
5/// Maps to HTTP status codes via `From<SearchError> for Error` in `error.rs`:
6/// - `InvalidInput` → 400
7/// - `UnknownKb` → 404
8/// - `BackendUnavailable` → 503
9/// - `Internal` → 500
10#[derive(Debug, Error)]
11pub enum SearchError {
12    /// Search request input is malformed or semantically invalid.
13    #[error("invalid input: {message}")]
14    InvalidInput {
15        /// Human-readable description of the invalid input.
16        message: String,
17    },
18
19    /// The requested knowledge base is unknown to the search subsystem.
20    #[error("knowledge base not found: {slug}")]
21    UnknownKb {
22        /// The missing knowledge base slug.
23        slug: String,
24    },
25
26    /// The search backend is unavailable or temporarily unable to serve requests.
27    #[error("search backend unavailable: {message}")]
28    BackendUnavailable {
29        /// Human-readable backend failure detail.
30        message: String,
31    },
32
33    /// An unexpected search subsystem error occurred.
34    #[error("internal error: {message}")]
35    Internal {
36        /// Human-readable internal failure detail.
37        message: String,
38    },
39}
40
41impl SearchError {
42    /// Convenience constructor.
43    pub fn invalid_input(message: impl Into<String>) -> Self {
44        Self::InvalidInput {
45            message: message.into(),
46        }
47    }
48
49    /// Convenience constructor.
50    pub fn unknown_kb(slug: impl Into<String>) -> Self {
51        Self::UnknownKb { slug: slug.into() }
52    }
53
54    /// Convenience constructor.
55    pub fn backend_unavailable(message: impl Into<String>) -> Self {
56        Self::BackendUnavailable {
57            message: message.into(),
58        }
59    }
60
61    /// Convenience constructor.
62    pub fn internal(message: impl Into<String>) -> Self {
63        Self::Internal {
64            message: message.into(),
65        }
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72    use crate::error::Error;
73
74    #[test]
75    fn invalid_input_display() {
76        let e = SearchError::invalid_input("query must not be empty");
77        assert!(e.to_string().contains("query must not be empty"));
78    }
79
80    #[test]
81    fn unknown_kb_display() {
82        let e = SearchError::unknown_kb("notes");
83        assert!(e.to_string().contains("notes"));
84    }
85
86    #[test]
87    fn backend_unavailable_display() {
88        let e = SearchError::backend_unavailable("connection refused");
89        assert!(e.to_string().contains("connection refused"));
90    }
91
92    #[test]
93    fn internal_display() {
94        let e = SearchError::internal("unexpected state");
95        assert!(e.to_string().contains("unexpected state"));
96    }
97
98    #[test]
99    fn maps_invalid_input_to_error_invalid_input() {
100        let e = SearchError::invalid_input("bad query");
101        let mapped: Error = e.into();
102        assert!(matches!(mapped, Error::InvalidInput { .. }));
103    }
104
105    #[test]
106    fn maps_unknown_kb_to_error_not_found() {
107        let e = SearchError::unknown_kb("my-kb");
108        let mapped: Error = e.into();
109        assert!(matches!(mapped, Error::NotFound { .. }));
110    }
111
112    #[test]
113    fn maps_backend_unavailable_to_error_storage() {
114        let e = SearchError::backend_unavailable("qdrant down");
115        let mapped: Error = e.into();
116        // Should map to StorageError::BackendUnavailable → Error::Storage(...)
117        assert!(matches!(mapped, Error::Storage(_)));
118    }
119
120    #[test]
121    fn maps_internal_to_error_config() {
122        let e = SearchError::internal("unexpected state");
123        let mapped: Error = e.into();
124        // Maps to Error::Config (existing 500-mapped variant)
125        assert!(matches!(mapped, Error::Config { .. }));
126    }
127
128    #[test]
129    fn send_sync_bounds() {
130        fn assert_send_sync<T: Send + Sync + std::error::Error>() {}
131        assert_send_sync::<SearchError>();
132    }
133}