Skip to main content

scarab_plugin_api/object_model/
error.rs

1//! Error types for object model operations
2
3use std::fmt;
4
5use crate::object_model::{ObjectHandle, ObjectType};
6
7/// Errors that can occur when working with the object model
8#[derive(Debug, Clone, PartialEq)]
9pub enum ObjectError {
10    /// The requested object was not found in the registry
11    NotFound { handle: ObjectHandle },
12
13    /// The handle refers to an object that has been deleted and recreated
14    StaleHandle {
15        handle: ObjectHandle,
16        current_generation: u32,
17    },
18
19    /// The object exists but is not of the expected type
20    TypeMismatch {
21        handle: ObjectHandle,
22        expected: ObjectType,
23        actual: ObjectType,
24    },
25
26    /// The requested method does not exist on this object type
27    MethodNotFound {
28        handle: ObjectHandle,
29        method_name: String,
30    },
31
32    /// Invalid argument passed to object method
33    InvalidArgument {
34        handle: ObjectHandle,
35        method_name: String,
36        argument: String,
37        reason: String,
38    },
39}
40
41impl ObjectError {
42    /// Create a NotFound error
43    pub fn not_found(handle: ObjectHandle) -> Self {
44        Self::NotFound { handle }
45    }
46
47    /// Create a StaleHandle error
48    pub fn stale_handle(handle: ObjectHandle, current_generation: u32) -> Self {
49        Self::StaleHandle {
50            handle,
51            current_generation,
52        }
53    }
54
55    /// Create a TypeMismatch error
56    pub fn type_mismatch(handle: ObjectHandle, expected: ObjectType, actual: ObjectType) -> Self {
57        Self::TypeMismatch {
58            handle,
59            expected,
60            actual,
61        }
62    }
63
64    /// Create a MethodNotFound error
65    pub fn method_not_found(handle: ObjectHandle, method_name: impl Into<String>) -> Self {
66        Self::MethodNotFound {
67            handle,
68            method_name: method_name.into(),
69        }
70    }
71
72    /// Create an InvalidArgument error
73    pub fn invalid_argument(
74        handle: ObjectHandle,
75        method_name: impl Into<String>,
76        argument: impl Into<String>,
77        reason: impl Into<String>,
78    ) -> Self {
79        Self::InvalidArgument {
80            handle,
81            method_name: method_name.into(),
82            argument: argument.into(),
83            reason: reason.into(),
84        }
85    }
86}
87
88impl fmt::Display for ObjectError {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        match self {
91            ObjectError::NotFound { handle } => {
92                write!(f, "Object not found: {}", handle)
93            }
94            ObjectError::StaleHandle {
95                handle,
96                current_generation,
97            } => {
98                write!(
99                    f,
100                    "Stale handle: {} (generation {} is now {})",
101                    handle,
102                    handle.generation(),
103                    current_generation
104                )
105            }
106            ObjectError::TypeMismatch {
107                handle,
108                expected,
109                actual,
110            } => {
111                write!(
112                    f,
113                    "Type mismatch for {}: expected {}, got {}",
114                    handle, expected, actual
115                )
116            }
117            ObjectError::MethodNotFound {
118                handle,
119                method_name,
120            } => {
121                write!(f, "Method '{}' not found on object {}", method_name, handle)
122            }
123            ObjectError::InvalidArgument {
124                handle,
125                method_name,
126                argument,
127                reason,
128            } => {
129                write!(
130                    f,
131                    "Invalid argument '{}' for method '{}' on {}: {}",
132                    argument, method_name, handle, reason
133                )
134            }
135        }
136    }
137}
138
139impl std::error::Error for ObjectError {}
140
141/// Result type alias for object model operations
142pub type Result<T> = std::result::Result<T, ObjectError>;
143
144#[cfg(test)]
145mod tests {
146    use super::*;
147
148    #[test]
149    fn test_not_found_error() {
150        let handle = ObjectHandle::new(ObjectType::Window, 42, 1);
151        let err = ObjectError::not_found(handle);
152
153        match err {
154            ObjectError::NotFound { handle: h } => {
155                assert_eq!(h.id(), 42);
156            }
157            _ => panic!("Wrong error variant"),
158        }
159
160        let msg = err.to_string();
161        assert!(msg.contains("not found"));
162        assert!(msg.contains("Window#42"));
163    }
164
165    #[test]
166    fn test_stale_handle_error() {
167        let handle = ObjectHandle::new(ObjectType::Tab, 10, 2);
168        let err = ObjectError::stale_handle(handle, 5);
169
170        match err {
171            ObjectError::StaleHandle {
172                handle: h,
173                current_generation,
174            } => {
175                assert_eq!(h.generation(), 2);
176                assert_eq!(current_generation, 5);
177            }
178            _ => panic!("Wrong error variant"),
179        }
180
181        let msg = err.to_string();
182        assert!(msg.contains("Stale"));
183        assert!(msg.contains("generation 2"));
184        assert!(msg.contains("now 5"));
185    }
186
187    #[test]
188    fn test_type_mismatch_error() {
189        let handle = ObjectHandle::new(ObjectType::Pane, 7, 1);
190        let err = ObjectError::type_mismatch(handle, ObjectType::Tab, ObjectType::Pane);
191
192        match err {
193            ObjectError::TypeMismatch {
194                expected, actual, ..
195            } => {
196                assert_eq!(expected, ObjectType::Tab);
197                assert_eq!(actual, ObjectType::Pane);
198            }
199            _ => panic!("Wrong error variant"),
200        }
201
202        let msg = err.to_string();
203        assert!(msg.contains("Type mismatch"));
204        assert!(msg.contains("Tab"));
205        assert!(msg.contains("Pane"));
206    }
207
208    #[test]
209    fn test_method_not_found_error() {
210        let handle = ObjectHandle::new(ObjectType::Window, 1, 1);
211        let err = ObjectError::method_not_found(handle, "activate");
212
213        match &err {
214            ObjectError::MethodNotFound { method_name, .. } => {
215                assert_eq!(method_name, "activate");
216            }
217            _ => panic!("Wrong error variant"),
218        }
219
220        let msg = err.to_string();
221        assert!(msg.contains("Method"));
222        assert!(msg.contains("activate"));
223        assert!(msg.contains("not found"));
224    }
225
226    #[test]
227    fn test_invalid_argument_error() {
228        let handle = ObjectHandle::new(ObjectType::Pane, 5, 1);
229        let err = ObjectError::invalid_argument(handle, "resize", "width", "must be positive");
230
231        match &err {
232            ObjectError::InvalidArgument {
233                method_name,
234                argument,
235                reason,
236                ..
237            } => {
238                assert_eq!(method_name, "resize");
239                assert_eq!(argument, "width");
240                assert_eq!(reason, "must be positive");
241            }
242            _ => panic!("Wrong error variant"),
243        }
244
245        let msg = err.to_string();
246        assert!(msg.contains("Invalid argument"));
247        assert!(msg.contains("width"));
248        assert!(msg.contains("resize"));
249        assert!(msg.contains("must be positive"));
250    }
251
252    #[test]
253    fn test_error_clone() {
254        let handle = ObjectHandle::new(ObjectType::Tab, 1, 1);
255        let err1 = ObjectError::not_found(handle);
256        let err2 = err1.clone();
257        assert_eq!(err1, err2);
258    }
259
260    #[test]
261    fn test_result_type() {
262        let handle = ObjectHandle::new(ObjectType::Window, 1, 1);
263
264        let ok_result: Result<i32> = Ok(42);
265        assert_eq!(ok_result.unwrap(), 42);
266
267        let err_result: Result<i32> = Err(ObjectError::not_found(handle));
268        assert!(err_result.is_err());
269    }
270}