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