scarab_plugin_api/events/
args.rs1use super::EventType;
7use crate::object_model::ObjectHandle;
8use std::time::Instant;
9
10#[derive(Clone, Debug)]
18pub struct EventArgs {
19 pub event_type: EventType,
21
22 pub window: Option<ObjectHandle>,
24
25 pub pane: Option<ObjectHandle>,
27
28 pub tab: Option<ObjectHandle>,
30
31 pub data: EventData,
33
34 pub timestamp: Instant,
36}
37
38impl EventArgs {
39 pub fn new(event_type: EventType) -> Self {
41 Self {
42 event_type,
43 window: None,
44 pane: None,
45 tab: None,
46 data: EventData::None,
47 timestamp: Instant::now(),
48 }
49 }
50
51 pub fn with_window(mut self, handle: ObjectHandle) -> Self {
53 self.window = Some(handle);
54 self
55 }
56
57 pub fn with_pane(mut self, handle: ObjectHandle) -> Self {
59 self.pane = Some(handle);
60 self
61 }
62
63 pub fn with_tab(mut self, handle: ObjectHandle) -> Self {
65 self.tab = Some(handle);
66 self
67 }
68
69 pub fn with_data(mut self, data: EventData) -> Self {
71 self.data = data;
72 self
73 }
74
75 pub fn data(&self) -> &EventData {
77 &self.data
78 }
79
80 pub fn has_window(&self) -> bool {
82 self.window.is_some()
83 }
84
85 pub fn has_pane(&self) -> bool {
87 self.pane.is_some()
88 }
89
90 pub fn has_tab(&self) -> bool {
92 self.tab.is_some()
93 }
94
95 pub fn age(&self) -> std::time::Duration {
97 self.timestamp.elapsed()
98 }
99}
100
101#[derive(Clone, Debug, Default)]
106pub enum EventData {
107 #[default]
109 None,
110
111 Text(String),
113
114 Uri(String),
116
117 FocusState {
119 is_focused: bool,
121 },
122
123 Dimensions {
125 cols: u16,
127 rows: u16,
129 },
130
131 TitleChange {
133 old: String,
135 new: String,
137 },
138
139 UserVar {
141 name: String,
143 value: String,
145 },
146
147 Selection {
149 text: String,
151 start: (u16, u16),
153 end: (u16, u16),
155 },
156
157 ExitCode(i32),
159
160 Binary(Vec<u8>),
162}
163
164impl EventData {
165 pub fn as_text(&self) -> Option<&str> {
167 match self {
168 EventData::Text(s) => Some(s),
169 _ => None,
170 }
171 }
172
173 pub fn as_uri(&self) -> Option<&str> {
175 match self {
176 EventData::Uri(s) => Some(s),
177 _ => None,
178 }
179 }
180
181 pub fn as_focus_state(&self) -> Option<bool> {
183 match self {
184 EventData::FocusState { is_focused } => Some(*is_focused),
185 _ => None,
186 }
187 }
188
189 pub fn as_dimensions(&self) -> Option<(u16, u16)> {
191 match self {
192 EventData::Dimensions { cols, rows } => Some((*cols, *rows)),
193 _ => None,
194 }
195 }
196
197 pub fn as_title_change(&self) -> Option<(&str, &str)> {
199 match self {
200 EventData::TitleChange { old, new } => Some((old, new)),
201 _ => None,
202 }
203 }
204
205 pub fn as_user_var(&self) -> Option<(&str, &str)> {
207 match self {
208 EventData::UserVar { name, value } => Some((name, value)),
209 _ => None,
210 }
211 }
212
213 pub fn as_selection(&self) -> Option<(&str, (u16, u16), (u16, u16))> {
215 match self {
216 EventData::Selection { text, start, end } => Some((text, *start, *end)),
217 _ => None,
218 }
219 }
220
221 pub fn as_exit_code(&self) -> Option<i32> {
223 match self {
224 EventData::ExitCode(code) => Some(*code),
225 _ => None,
226 }
227 }
228
229 pub fn as_binary(&self) -> Option<&[u8]> {
231 match self {
232 EventData::Binary(data) => Some(data),
233 _ => None,
234 }
235 }
236
237 pub fn is_none(&self) -> bool {
239 matches!(self, EventData::None)
240 }
241}
242
243#[cfg(test)]
244mod tests {
245 use super::*;
246 use crate::object_model::ObjectType;
247
248 #[test]
249 fn test_event_args_builder() {
250 let window = ObjectHandle::new(ObjectType::Window, 1, 0);
251 let pane = ObjectHandle::new(ObjectType::Pane, 2, 0);
252
253 let args = EventArgs::new(EventType::PaneFocused)
254 .with_window(window)
255 .with_pane(pane)
256 .with_data(EventData::FocusState { is_focused: true });
257
258 assert_eq!(args.event_type, EventType::PaneFocused);
259 assert_eq!(args.window, Some(window));
260 assert_eq!(args.pane, Some(pane));
261 assert!(args.has_window());
262 assert!(args.has_pane());
263 assert!(!args.has_tab());
264 assert!(args.data.as_focus_state().unwrap());
265 }
266
267 #[test]
268 fn test_event_data_text() {
269 let data = EventData::Text("hello".to_string());
270 assert_eq!(data.as_text(), Some("hello"));
271 assert!(data.as_uri().is_none());
272 assert!(!data.is_none());
273 }
274
275 #[test]
276 fn test_event_data_dimensions() {
277 let data = EventData::Dimensions { cols: 80, rows: 24 };
278 assert_eq!(data.as_dimensions(), Some((80, 24)));
279 assert!(data.as_text().is_none());
280 }
281
282 #[test]
283 fn test_event_data_selection() {
284 let data = EventData::Selection {
285 text: "selected".to_string(),
286 start: (0, 0),
287 end: (8, 0),
288 };
289
290 let (text, start, end) = data.as_selection().unwrap();
291 assert_eq!(text, "selected");
292 assert_eq!(start, (0, 0));
293 assert_eq!(end, (8, 0));
294 }
295
296 #[test]
297 fn test_event_data_user_var() {
298 let data = EventData::UserVar {
299 name: "CWD".to_string(),
300 value: "/home/user".to_string(),
301 };
302
303 let (name, value) = data.as_user_var().unwrap();
304 assert_eq!(name, "CWD");
305 assert_eq!(value, "/home/user");
306 }
307
308 #[test]
309 fn test_event_data_title_change() {
310 let data = EventData::TitleChange {
311 old: "old title".to_string(),
312 new: "new title".to_string(),
313 };
314
315 let (old, new) = data.as_title_change().unwrap();
316 assert_eq!(old, "old title");
317 assert_eq!(new, "new title");
318 }
319
320 #[test]
321 fn test_event_data_exit_code() {
322 let data = EventData::ExitCode(42);
323 assert_eq!(data.as_exit_code(), Some(42));
324 }
325
326 #[test]
327 fn test_event_data_binary() {
328 let bytes = vec![1, 2, 3, 4];
329 let data = EventData::Binary(bytes.clone());
330 assert_eq!(data.as_binary(), Some(bytes.as_slice()));
331 }
332
333 #[test]
334 fn test_event_args_age() {
335 let args = EventArgs::new(EventType::Bell);
336 std::thread::sleep(std::time::Duration::from_millis(10));
337 assert!(args.age().as_millis() >= 10);
338 }
339}