scarab_plugin_api/object_model/
error.rs1use std::fmt;
4
5use crate::object_model::{ObjectHandle, ObjectType};
6
7#[derive(Debug, Clone, PartialEq)]
9pub enum ObjectError {
10 NotFound { handle: ObjectHandle },
12
13 StaleHandle {
15 handle: ObjectHandle,
16 current_generation: u32,
17 },
18
19 TypeMismatch {
21 handle: ObjectHandle,
22 expected: ObjectType,
23 actual: ObjectType,
24 },
25
26 MethodNotFound {
28 handle: ObjectHandle,
29 method_name: String,
30 },
31
32 InvalidArgument {
34 handle: ObjectHandle,
35 method_name: String,
36 argument: String,
37 reason: String,
38 },
39}
40
41impl ObjectError {
42 pub fn not_found(handle: ObjectHandle) -> Self {
44 Self::NotFound { handle }
45 }
46
47 pub fn stale_handle(handle: ObjectHandle, current_generation: u32) -> Self {
49 Self::StaleHandle {
50 handle,
51 current_generation,
52 }
53 }
54
55 pub fn type_mismatch(handle: ObjectHandle, expected: ObjectType, actual: ObjectType) -> Self {
57 Self::TypeMismatch {
58 handle,
59 expected,
60 actual,
61 }
62 }
63
64 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 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
141pub 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}