scarab_plugin_api/object_model/
tab.rs1use super::{
4 ObjectError, ObjectHandle, ObjectRegistry, ObjectType, PaneProxy, Result, WindowProxy,
5};
6
7#[derive(Debug, Clone)]
9pub struct TabProxy {
10 handle: ObjectHandle,
11}
12
13impl TabProxy {
14 pub fn new(handle: ObjectHandle) -> Result<Self> {
15 if handle.object_type() != ObjectType::Tab {
16 return Err(ObjectError::type_mismatch(
17 handle,
18 ObjectType::Tab,
19 handle.object_type(),
20 ));
21 }
22 Ok(Self { handle })
23 }
24
25 pub fn handle(&self) -> ObjectHandle {
26 self.handle
27 }
28
29 pub fn id(&self) -> u64 {
30 self.handle.id()
31 }
32
33 pub fn get_title(&self) -> Result<String> {
35 Err(ObjectError::method_not_found(self.handle, "get_title"))
36 }
37
38 pub fn set_title(&self, _title: &str) -> Result<()> {
40 Err(ObjectError::method_not_found(self.handle, "set_title"))
41 }
42
43 pub fn panes<T>(&self, registry: &impl ObjectRegistry<T>) -> Result<Vec<PaneProxy>> {
54 let child_handles = registry.get_children(&self.handle)?;
55
56 child_handles
57 .into_iter()
58 .map(|handle| {
59 if handle.object_type() != ObjectType::Pane {
60 return Err(ObjectError::type_mismatch(
61 handle,
62 ObjectType::Pane,
63 handle.object_type(),
64 ));
65 }
66 PaneProxy::new(handle)
67 })
68 .collect()
69 }
70
71 pub fn active_pane<T>(&self, registry: &impl ObjectRegistry<T>) -> Result<PaneProxy> {
85 let panes = self.panes(registry)?;
86 panes
87 .into_iter()
88 .next()
89 .ok_or_else(|| ObjectError::method_not_found(self.handle, "active_pane: no panes"))
90 }
91
92 pub fn activate(&self) -> Result<()> {
94 Err(ObjectError::method_not_found(self.handle, "activate"))
95 }
96
97 pub fn is_active(&self) -> Result<bool> {
99 Err(ObjectError::method_not_found(self.handle, "is_active"))
100 }
101
102 pub fn window<T>(&self, registry: &impl ObjectRegistry<T>) -> Result<WindowProxy> {
113 let parent_handle = registry
114 .get_parent(&self.handle)?
115 .ok_or_else(|| ObjectError::method_not_found(self.handle, "window: no parent"))?;
116
117 if parent_handle.object_type() != ObjectType::Window {
118 return Err(ObjectError::type_mismatch(
119 parent_handle,
120 ObjectType::Window,
121 parent_handle.object_type(),
122 ));
123 }
124
125 WindowProxy::new(parent_handle)
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn test_tab_proxy_creation() {
135 let handle = ObjectHandle::new(ObjectType::Tab, 1, 0);
136 let proxy = TabProxy::new(handle);
137 assert!(proxy.is_ok());
138
139 let proxy = proxy.unwrap();
140 assert_eq!(proxy.id(), 1);
141 assert_eq!(proxy.handle(), handle);
142 }
143
144 #[test]
145 fn test_tab_proxy_type_validation() {
146 let pane_handle = ObjectHandle::new(ObjectType::Pane, 1, 0);
147 let result = TabProxy::new(pane_handle);
148
149 assert!(result.is_err());
150 match result {
151 Err(ObjectError::TypeMismatch {
152 expected, actual, ..
153 }) => {
154 assert_eq!(expected, ObjectType::Tab);
155 assert_eq!(actual, ObjectType::Pane);
156 }
157 _ => panic!("Expected TypeMismatch error"),
158 }
159 }
160
161 #[test]
162 fn test_tab_proxy_methods_not_implemented() {
163 let handle = ObjectHandle::new(ObjectType::Tab, 1, 0);
164 let proxy = TabProxy::new(handle).unwrap();
165
166 assert!(matches!(
168 proxy.get_title(),
169 Err(ObjectError::MethodNotFound { .. })
170 ));
171 assert!(matches!(
172 proxy.set_title("test"),
173 Err(ObjectError::MethodNotFound { .. })
174 ));
175 assert!(matches!(
176 proxy.activate(),
177 Err(ObjectError::MethodNotFound { .. })
178 ));
179 assert!(matches!(
180 proxy.is_active(),
181 Err(ObjectError::MethodNotFound { .. })
182 ));
183 }
184
185 #[test]
186 fn test_tab_proxy_clone() {
187 let handle = ObjectHandle::new(ObjectType::Tab, 1, 0);
188 let proxy1 = TabProxy::new(handle).unwrap();
189 let proxy2 = proxy1.clone();
190
191 assert_eq!(proxy1.handle(), proxy2.handle());
192 assert_eq!(proxy1.id(), proxy2.id());
193 }
194
195 use super::super::registry::{ObjectRegistry, RegistryEntry};
197 use std::collections::HashMap;
198
199 #[derive(Clone)]
200 struct TestObject;
201
202 struct TestRegistry {
203 objects: HashMap<u64, RegistryEntry<TestObject>>,
204 parents: HashMap<u64, ObjectHandle>,
205 children: HashMap<u64, Vec<ObjectHandle>>,
206 generations: HashMap<u64, u32>,
207 }
208
209 impl TestRegistry {
210 fn new() -> Self {
211 Self {
212 objects: HashMap::new(),
213 parents: HashMap::new(),
214 children: HashMap::new(),
215 generations: HashMap::new(),
216 }
217 }
218
219 fn set_parent(&mut self, child: ObjectHandle, parent: ObjectHandle) {
220 self.parents.insert(child.id(), parent);
221 self.children
222 .entry(parent.id())
223 .or_insert_with(Vec::new)
224 .push(child);
225 }
226 }
227
228 impl ObjectRegistry<TestObject> for TestRegistry {
229 fn register(&mut self, object: TestObject) -> ObjectHandle {
230 let id = self.objects.len() as u64 + 1;
231 let generation = self.current_generation(id);
232 let handle = ObjectHandle::new(ObjectType::Tab, id, generation);
233 self.objects.insert(id, RegistryEntry::new(handle, object));
234 handle
235 }
236
237 fn unregister(&mut self, handle: ObjectHandle) -> Result<TestObject> {
238 self.increment_generation(handle.id());
239 self.objects
240 .remove(&handle.id())
241 .map(|entry| entry.into_object())
242 .ok_or_else(|| ObjectError::not_found(handle))
243 }
244
245 fn get(&self, handle: ObjectHandle) -> Result<&TestObject> {
246 let current_gen = self.current_generation(handle.id());
247 if !handle.is_valid(current_gen) {
248 return Err(ObjectError::stale_handle(handle, current_gen));
249 }
250 self.objects
251 .get(&handle.id())
252 .map(|entry| entry.object())
253 .ok_or_else(|| ObjectError::not_found(handle))
254 }
255
256 fn get_mut(&mut self, handle: ObjectHandle) -> Result<&mut TestObject> {
257 let current_gen = self.current_generation(handle.id());
258 if !handle.is_valid(current_gen) {
259 return Err(ObjectError::stale_handle(handle, current_gen));
260 }
261 self.objects
262 .get_mut(&handle.id())
263 .map(|entry| entry.object_mut())
264 .ok_or_else(|| ObjectError::not_found(handle))
265 }
266
267 fn get_by_id(&self, id: u64) -> Option<&TestObject> {
268 self.objects.get(&id).map(|entry| entry.object())
269 }
270
271 fn next_id(&mut self) -> u64 {
272 self.objects.len() as u64 + 1
273 }
274
275 fn increment_generation(&mut self, id: u64) {
276 let gen = self.generations.entry(id).or_insert(0);
277 *gen = gen.wrapping_add(1);
278 }
279
280 fn current_generation(&self, id: u64) -> u32 {
281 self.generations.get(&id).copied().unwrap_or(0)
282 }
283
284 fn all_ids(&self) -> Vec<u64> {
285 self.objects.keys().copied().collect()
286 }
287
288 fn len(&self) -> usize {
289 self.objects.len()
290 }
291
292 fn get_parent(&self, handle: &ObjectHandle) -> Result<Option<ObjectHandle>> {
293 let current_gen = self.current_generation(handle.id());
294 if !handle.is_valid(current_gen) {
295 return Err(ObjectError::stale_handle(*handle, current_gen));
296 }
297 if !self.objects.contains_key(&handle.id()) {
298 return Err(ObjectError::not_found(*handle));
299 }
300 Ok(self.parents.get(&handle.id()).copied())
301 }
302
303 fn get_children(&self, handle: &ObjectHandle) -> Result<Vec<ObjectHandle>> {
304 let current_gen = self.current_generation(handle.id());
305 if !handle.is_valid(current_gen) {
306 return Err(ObjectError::stale_handle(*handle, current_gen));
307 }
308 if !self.objects.contains_key(&handle.id()) {
309 return Err(ObjectError::not_found(*handle));
310 }
311 Ok(self
312 .children
313 .get(&handle.id())
314 .cloned()
315 .unwrap_or_else(Vec::new))
316 }
317 }
318
319 #[test]
320 fn test_tab_navigation_to_window() {
321 let mut registry = TestRegistry::new();
322
323 let tab_handle = ObjectHandle::new(ObjectType::Tab, 1, 0);
325 let window_handle = ObjectHandle::new(ObjectType::Window, 2, 0);
326
327 registry
328 .objects
329 .insert(1, RegistryEntry::new(tab_handle, TestObject));
330 registry
331 .objects
332 .insert(2, RegistryEntry::new(window_handle, TestObject));
333
334 registry.set_parent(tab_handle, window_handle);
336
337 let tab_proxy = TabProxy::new(tab_handle).unwrap();
338 let window_proxy = tab_proxy.window(®istry).unwrap();
339
340 assert_eq!(window_proxy.handle(), window_handle);
341 assert_eq!(window_proxy.id(), 2);
342 }
343
344 #[test]
345 fn test_tab_navigation_to_panes() {
346 let mut registry = TestRegistry::new();
347
348 let tab_handle = ObjectHandle::new(ObjectType::Tab, 1, 0);
350 let pane1_handle = ObjectHandle::new(ObjectType::Pane, 2, 0);
351 let pane2_handle = ObjectHandle::new(ObjectType::Pane, 3, 0);
352
353 registry
354 .objects
355 .insert(1, RegistryEntry::new(tab_handle, TestObject));
356 registry
357 .objects
358 .insert(2, RegistryEntry::new(pane1_handle, TestObject));
359 registry
360 .objects
361 .insert(3, RegistryEntry::new(pane2_handle, TestObject));
362
363 registry.set_parent(pane1_handle, tab_handle);
365 registry.set_parent(pane2_handle, tab_handle);
366
367 let tab_proxy = TabProxy::new(tab_handle).unwrap();
368 let panes = tab_proxy.panes(®istry).unwrap();
369
370 assert_eq!(panes.len(), 2);
371 assert!(panes.iter().any(|p| p.id() == 2));
372 assert!(panes.iter().any(|p| p.id() == 3));
373 }
374
375 #[test]
376 fn test_tab_navigation_active_pane() {
377 let mut registry = TestRegistry::new();
378
379 let tab_handle = ObjectHandle::new(ObjectType::Tab, 1, 0);
381 let pane1_handle = ObjectHandle::new(ObjectType::Pane, 2, 0);
382 let pane2_handle = ObjectHandle::new(ObjectType::Pane, 3, 0);
383
384 registry
385 .objects
386 .insert(1, RegistryEntry::new(tab_handle, TestObject));
387 registry
388 .objects
389 .insert(2, RegistryEntry::new(pane1_handle, TestObject));
390 registry
391 .objects
392 .insert(3, RegistryEntry::new(pane2_handle, TestObject));
393
394 registry.set_parent(pane1_handle, tab_handle);
396 registry.set_parent(pane2_handle, tab_handle);
397
398 let tab_proxy = TabProxy::new(tab_handle).unwrap();
399 let active_pane = tab_proxy.active_pane(®istry).unwrap();
400
401 assert!(active_pane.id() == 2 || active_pane.id() == 3);
403 }
404
405 #[test]
406 fn test_tab_navigation_no_panes() {
407 let mut registry = TestRegistry::new();
408
409 let tab_handle = ObjectHandle::new(ObjectType::Tab, 1, 0);
410 registry
411 .objects
412 .insert(1, RegistryEntry::new(tab_handle, TestObject));
413
414 let tab_proxy = TabProxy::new(tab_handle).unwrap();
415
416 let panes = tab_proxy.panes(®istry).unwrap();
418 assert!(panes.is_empty());
419
420 let result = tab_proxy.active_pane(®istry);
422 assert!(result.is_err());
423 }
424
425 #[test]
426 fn test_tab_navigation_no_parent() {
427 let mut registry = TestRegistry::new();
428
429 let tab_handle = ObjectHandle::new(ObjectType::Tab, 1, 0);
430 registry
431 .objects
432 .insert(1, RegistryEntry::new(tab_handle, TestObject));
433
434 let tab_proxy = TabProxy::new(tab_handle).unwrap();
435
436 let result = tab_proxy.window(®istry);
438 assert!(result.is_err());
439 }
440
441 #[test]
442 fn test_tab_navigation_wrong_child_type() {
443 let mut registry = TestRegistry::new();
444
445 let tab_handle = ObjectHandle::new(ObjectType::Tab, 1, 0);
447 let window_handle = ObjectHandle::new(ObjectType::Window, 2, 0);
448
449 registry
450 .objects
451 .insert(1, RegistryEntry::new(tab_handle, TestObject));
452 registry
453 .objects
454 .insert(2, RegistryEntry::new(window_handle, TestObject));
455
456 registry.set_parent(window_handle, tab_handle);
458
459 let tab_proxy = TabProxy::new(tab_handle).unwrap();
460 let result = tab_proxy.panes(®istry);
461
462 assert!(matches!(result, Err(ObjectError::TypeMismatch { .. })));
464 }
465}