Skip to main content

directory_indexer/
error.rs

1use thiserror::Error;
2
3pub type Result<T> = std::result::Result<T, IndexerError>;
4
5#[derive(Error, Debug)]
6pub enum IndexerError {
7    #[error("IO error: {0}")]
8    Io(#[from] std::io::Error),
9
10    #[error("Database error: {0}")]
11    Database(#[from] rusqlite::Error),
12
13    #[error("HTTP request error: {0}")]
14    Http(#[from] reqwest::Error),
15
16    #[error("JSON serialization error: {0}")]
17    Json(#[from] serde_json::Error),
18
19    #[error("Configuration error: {0}")]
20    Config(#[from] config::ConfigError),
21
22    #[error("Embedding provider error: {message}")]
23    Embedding { message: String },
24
25    #[error("Vector store error: {message}")]
26    VectorStore { message: String },
27
28    #[error("File processing error: {message}")]
29    FileProcessing { message: String },
30
31    #[error("Invalid input: {message}")]
32    InvalidInput { message: String },
33
34    #[error("Resource not found: {message}")]
35    NotFound { message: String },
36
37    #[error("MCP protocol error: {message}")]
38    Mcp { message: String },
39
40    #[error("Environment setup required: {message}")]
41    EnvironmentSetup { message: String },
42}
43
44impl IndexerError {
45    pub fn embedding<S: Into<String>>(message: S) -> Self {
46        Self::Embedding {
47            message: message.into(),
48        }
49    }
50
51    pub fn vector_store<S: Into<String>>(message: S) -> Self {
52        Self::VectorStore {
53            message: message.into(),
54        }
55    }
56
57    pub fn file_processing<S: Into<String>>(message: S) -> Self {
58        Self::FileProcessing {
59            message: message.into(),
60        }
61    }
62
63    pub fn invalid_input<S: Into<String>>(message: S) -> Self {
64        Self::InvalidInput {
65            message: message.into(),
66        }
67    }
68
69    pub fn not_found<S: Into<String>>(message: S) -> Self {
70        Self::NotFound {
71            message: message.into(),
72        }
73    }
74
75    pub fn mcp<S: Into<String>>(message: S) -> Self {
76        Self::Mcp {
77            message: message.into(),
78        }
79    }
80
81    pub fn environment_setup<S: Into<String>>(message: S) -> Self {
82        Self::EnvironmentSetup {
83            message: message.into(),
84        }
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91    use std::io;
92
93    #[test]
94    fn test_error_display_messages() {
95        let io_error = IndexerError::Io(io::Error::new(io::ErrorKind::NotFound, "test io error"));
96        assert!(io_error.to_string().contains("IO error"));
97        assert!(io_error.to_string().contains("test io error"));
98
99        let embedding_error = IndexerError::embedding("embedding failed");
100        assert_eq!(
101            embedding_error.to_string(),
102            "Embedding provider error: embedding failed"
103        );
104
105        let vector_error = IndexerError::vector_store("qdrant unavailable");
106        assert_eq!(
107            vector_error.to_string(),
108            "Vector store error: qdrant unavailable"
109        );
110
111        let file_error = IndexerError::file_processing("cannot read file");
112        assert_eq!(
113            file_error.to_string(),
114            "File processing error: cannot read file"
115        );
116
117        let input_error = IndexerError::invalid_input("invalid path");
118        assert_eq!(input_error.to_string(), "Invalid input: invalid path");
119
120        let not_found_error = IndexerError::not_found("file not found");
121        assert_eq!(
122            not_found_error.to_string(),
123            "Resource not found: file not found"
124        );
125
126        let mcp_error = IndexerError::mcp("protocol error");
127        assert_eq!(mcp_error.to_string(), "MCP protocol error: protocol error");
128
129        let env_error = IndexerError::environment_setup("qdrant not running");
130        assert_eq!(
131            env_error.to_string(),
132            "Environment setup required: qdrant not running"
133        );
134    }
135
136    #[test]
137    fn test_error_constructor_functions() {
138        let embedding_error = IndexerError::embedding("test message");
139        match embedding_error {
140            IndexerError::Embedding { message } => assert_eq!(message, "test message"),
141            _ => panic!("Expected Embedding error"),
142        }
143
144        let vector_error = IndexerError::vector_store("qdrant error");
145        match vector_error {
146            IndexerError::VectorStore { message } => assert_eq!(message, "qdrant error"),
147            _ => panic!("Expected VectorStore error"),
148        }
149
150        let file_error = IndexerError::file_processing("read failed");
151        match file_error {
152            IndexerError::FileProcessing { message } => assert_eq!(message, "read failed"),
153            _ => panic!("Expected FileProcessing error"),
154        }
155
156        let input_error = IndexerError::invalid_input("bad input");
157        match input_error {
158            IndexerError::InvalidInput { message } => assert_eq!(message, "bad input"),
159            _ => panic!("Expected InvalidInput error"),
160        }
161
162        let not_found_error = IndexerError::not_found("missing");
163        match not_found_error {
164            IndexerError::NotFound { message } => assert_eq!(message, "missing"),
165            _ => panic!("Expected NotFound error"),
166        }
167
168        let mcp_error = IndexerError::mcp("rpc error");
169        match mcp_error {
170            IndexerError::Mcp { message } => assert_eq!(message, "rpc error"),
171            _ => panic!("Expected Mcp error"),
172        }
173
174        let env_error = IndexerError::environment_setup("setup needed");
175        match env_error {
176            IndexerError::EnvironmentSetup { message } => assert_eq!(message, "setup needed"),
177            _ => panic!("Expected EnvironmentSetup error"),
178        }
179    }
180
181    #[test]
182    fn test_error_from_conversions() {
183        let io_error = io::Error::new(io::ErrorKind::PermissionDenied, "access denied");
184        let indexer_error: IndexerError = io_error.into();
185        match indexer_error {
186            IndexerError::Io(_) => {}
187            _ => panic!("Expected Io error from conversion"),
188        }
189
190        let json_error = serde_json::from_str::<i32>("invalid json").unwrap_err();
191        let indexer_error: IndexerError = json_error.into();
192        match indexer_error {
193            IndexerError::Json(_) => {}
194            _ => panic!("Expected Json error from conversion"),
195        }
196    }
197
198    #[test]
199    fn test_error_debug_format() {
200        let error = IndexerError::embedding("debug test");
201        let debug_str = format!("{:?}", error);
202        assert!(debug_str.contains("Embedding"));
203        assert!(debug_str.contains("debug test"));
204    }
205
206    #[test]
207    fn test_error_string_conversion() {
208        let error1 = IndexerError::embedding(String::from("owned string"));
209        let error2 = IndexerError::embedding("borrowed string");
210
211        match (&error1, &error2) {
212            (
213                IndexerError::Embedding { message: msg1 },
214                IndexerError::Embedding { message: msg2 },
215            ) => {
216                assert_eq!(msg1, "owned string");
217                assert_eq!(msg2, "borrowed string");
218            }
219            _ => panic!("Expected Embedding errors"),
220        }
221    }
222
223    #[test]
224    fn test_result_type_alias() {
225        fn returns_result() -> Result<i32> {
226            Ok(42)
227        }
228
229        fn returns_error() -> Result<i32> {
230            Err(IndexerError::invalid_input("test error"))
231        }
232
233        assert_eq!(returns_result().unwrap(), 42);
234        assert!(returns_error().is_err());
235    }
236}