zellij_utils/plugin_api/
event.rs

1pub use super::generated_api::api::{
2    action::{Action as ProtobufAction, Position as ProtobufPosition},
3    event::{
4        event::Payload as ProtobufEventPayload, ClientInfo as ProtobufClientInfo,
5        CopyDestination as ProtobufCopyDestination, Event as ProtobufEvent,
6        EventNameList as ProtobufEventNameList, EventType as ProtobufEventType,
7        FileMetadata as ProtobufFileMetadata, InputModeKeybinds as ProtobufInputModeKeybinds,
8        KeyBind as ProtobufKeyBind, LayoutInfo as ProtobufLayoutInfo,
9        ModeUpdatePayload as ProtobufModeUpdatePayload, PaneId as ProtobufPaneId,
10        PaneInfo as ProtobufPaneInfo, PaneManifest as ProtobufPaneManifest,
11        PaneType as ProtobufPaneType, PluginInfo as ProtobufPluginInfo,
12        ResurrectableSession as ProtobufResurrectableSession,
13        SessionManifest as ProtobufSessionManifest, TabInfo as ProtobufTabInfo, *,
14    },
15    input_mode::InputMode as ProtobufInputMode,
16    key::Key as ProtobufKey,
17    style::Style as ProtobufStyle,
18};
19#[allow(hidden_glob_reexports)]
20use crate::data::{
21    ClientInfo, CopyDestination, Event, EventType, FileMetadata, InputMode, KeyWithModifier,
22    LayoutInfo, ModeInfo, Mouse, PaneId, PaneInfo, PaneManifest, PermissionStatus,
23    PluginCapabilities, PluginInfo, SessionInfo, Style, TabInfo,
24};
25
26use crate::errors::prelude::*;
27use crate::input::actions::Action;
28
29use std::collections::{BTreeMap, HashMap, HashSet};
30use std::convert::TryFrom;
31use std::path::PathBuf;
32use std::time::Duration;
33
34impl TryFrom<ProtobufEvent> for Event {
35    type Error = &'static str;
36    fn try_from(protobuf_event: ProtobufEvent) -> Result<Self, &'static str> {
37        match ProtobufEventType::from_i32(protobuf_event.name) {
38            Some(ProtobufEventType::ModeUpdate) => match protobuf_event.payload {
39                Some(ProtobufEventPayload::ModeUpdatePayload(protobuf_mode_update_payload)) => {
40                    let mode_info: ModeInfo = protobuf_mode_update_payload.try_into()?;
41                    Ok(Event::ModeUpdate(mode_info))
42                },
43                _ => Err("Malformed payload for the ModeUpdate Event"),
44            },
45            Some(ProtobufEventType::TabUpdate) => match protobuf_event.payload {
46                Some(ProtobufEventPayload::TabUpdatePayload(protobuf_tab_info_payload)) => {
47                    let mut tab_infos: Vec<TabInfo> = vec![];
48                    for protobuf_tab_info in protobuf_tab_info_payload.tab_info {
49                        tab_infos.push(TabInfo::try_from(protobuf_tab_info)?);
50                    }
51                    Ok(Event::TabUpdate(tab_infos))
52                },
53                _ => Err("Malformed payload for the TabUpdate Event"),
54            },
55            Some(ProtobufEventType::PaneUpdate) => match protobuf_event.payload {
56                Some(ProtobufEventPayload::PaneUpdatePayload(protobuf_pane_update_payload)) => {
57                    let mut pane_manifest: HashMap<usize, Vec<PaneInfo>> = HashMap::new();
58                    for protobuf_pane_manifest in protobuf_pane_update_payload.pane_manifest {
59                        let tab_index = protobuf_pane_manifest.tab_index as usize;
60                        let mut panes = vec![];
61                        for protobuf_pane_info in protobuf_pane_manifest.panes {
62                            panes.push(protobuf_pane_info.try_into()?);
63                        }
64                        if pane_manifest.contains_key(&tab_index) {
65                            return Err("Duplicate tab definition in pane manifest");
66                        }
67                        pane_manifest.insert(tab_index, panes);
68                    }
69                    Ok(Event::PaneUpdate(PaneManifest {
70                        panes: pane_manifest,
71                    }))
72                },
73                _ => Err("Malformed payload for the PaneUpdate Event"),
74            },
75            Some(ProtobufEventType::Key) => match protobuf_event.payload {
76                Some(ProtobufEventPayload::KeyPayload(protobuf_key)) => {
77                    Ok(Event::Key(protobuf_key.try_into()?))
78                },
79                _ => Err("Malformed payload for the Key Event"),
80            },
81            Some(ProtobufEventType::Mouse) => match protobuf_event.payload {
82                Some(ProtobufEventPayload::MouseEventPayload(protobuf_mouse)) => {
83                    Ok(Event::Mouse(protobuf_mouse.try_into()?))
84                },
85                _ => Err("Malformed payload for the Mouse Event"),
86            },
87            Some(ProtobufEventType::Timer) => match protobuf_event.payload {
88                Some(ProtobufEventPayload::TimerPayload(seconds)) => {
89                    Ok(Event::Timer(seconds as f64))
90                },
91                _ => Err("Malformed payload for the Timer Event"),
92            },
93            Some(ProtobufEventType::CopyToClipboard) => match protobuf_event.payload {
94                Some(ProtobufEventPayload::CopyToClipboardPayload(copy_to_clipboard)) => {
95                    let protobuf_copy_to_clipboard =
96                        ProtobufCopyDestination::from_i32(copy_to_clipboard)
97                            .ok_or("Malformed copy to clipboard payload")?;
98                    Ok(Event::CopyToClipboard(
99                        protobuf_copy_to_clipboard.try_into()?,
100                    ))
101                },
102                _ => Err("Malformed payload for the Copy To Clipboard Event"),
103            },
104            Some(ProtobufEventType::SystemClipboardFailure) => match protobuf_event.payload {
105                None => Ok(Event::SystemClipboardFailure),
106                _ => Err("Malformed payload for the system clipboard failure Event"),
107            },
108            Some(ProtobufEventType::InputReceived) => match protobuf_event.payload {
109                None => Ok(Event::InputReceived),
110                _ => Err("Malformed payload for the input received Event"),
111            },
112            Some(ProtobufEventType::Visible) => match protobuf_event.payload {
113                Some(ProtobufEventPayload::VisiblePayload(is_visible)) => {
114                    Ok(Event::Visible(is_visible))
115                },
116                _ => Err("Malformed payload for the visible Event"),
117            },
118            Some(ProtobufEventType::CustomMessage) => match protobuf_event.payload {
119                Some(ProtobufEventPayload::CustomMessagePayload(custom_message_payload)) => {
120                    Ok(Event::CustomMessage(
121                        custom_message_payload.message_name,
122                        custom_message_payload.payload,
123                    ))
124                },
125                _ => Err("Malformed payload for the custom message Event"),
126            },
127            Some(ProtobufEventType::FileSystemCreate) => match protobuf_event.payload {
128                Some(ProtobufEventPayload::FileListPayload(file_list_payload)) => {
129                    let file_paths = file_list_payload
130                        .paths
131                        .iter()
132                        .zip(file_list_payload.paths_metadata.iter())
133                        .map(|(p, m)| (PathBuf::from(p), m.into()))
134                        .collect();
135                    Ok(Event::FileSystemCreate(file_paths))
136                },
137                _ => Err("Malformed payload for the file system create Event"),
138            },
139            Some(ProtobufEventType::FileSystemRead) => match protobuf_event.payload {
140                Some(ProtobufEventPayload::FileListPayload(file_list_payload)) => {
141                    let file_paths = file_list_payload
142                        .paths
143                        .iter()
144                        .zip(file_list_payload.paths_metadata.iter())
145                        .map(|(p, m)| (PathBuf::from(p), m.into()))
146                        .collect();
147                    Ok(Event::FileSystemRead(file_paths))
148                },
149                _ => Err("Malformed payload for the file system read Event"),
150            },
151            Some(ProtobufEventType::FileSystemUpdate) => match protobuf_event.payload {
152                Some(ProtobufEventPayload::FileListPayload(file_list_payload)) => {
153                    let file_paths = file_list_payload
154                        .paths
155                        .iter()
156                        .zip(file_list_payload.paths_metadata.iter())
157                        .map(|(p, m)| (PathBuf::from(p), m.into()))
158                        .collect();
159                    Ok(Event::FileSystemUpdate(file_paths))
160                },
161                _ => Err("Malformed payload for the file system update Event"),
162            },
163            Some(ProtobufEventType::FileSystemDelete) => match protobuf_event.payload {
164                Some(ProtobufEventPayload::FileListPayload(file_list_payload)) => {
165                    let file_paths = file_list_payload
166                        .paths
167                        .iter()
168                        .zip(file_list_payload.paths_metadata.iter())
169                        .map(|(p, m)| (PathBuf::from(p), m.into()))
170                        .collect();
171                    Ok(Event::FileSystemDelete(file_paths))
172                },
173                _ => Err("Malformed payload for the file system delete Event"),
174            },
175            Some(ProtobufEventType::PermissionRequestResult) => match protobuf_event.payload {
176                Some(ProtobufEventPayload::PermissionRequestResultPayload(payload)) => {
177                    if payload.granted {
178                        Ok(Event::PermissionRequestResult(PermissionStatus::Granted))
179                    } else {
180                        Ok(Event::PermissionRequestResult(PermissionStatus::Denied))
181                    }
182                },
183                _ => Err("Malformed payload for the file system delete Event"),
184            },
185            Some(ProtobufEventType::SessionUpdate) => match protobuf_event.payload {
186                Some(ProtobufEventPayload::SessionUpdatePayload(
187                    protobuf_session_update_payload,
188                )) => {
189                    let mut session_infos: Vec<SessionInfo> = vec![];
190                    let mut resurrectable_sessions: Vec<(String, Duration)> = vec![];
191                    for protobuf_session_info in protobuf_session_update_payload.session_manifests {
192                        session_infos.push(SessionInfo::try_from(protobuf_session_info)?);
193                    }
194                    for protobuf_resurrectable_session in
195                        protobuf_session_update_payload.resurrectable_sessions
196                    {
197                        resurrectable_sessions.push(protobuf_resurrectable_session.into());
198                    }
199                    Ok(Event::SessionUpdate(
200                        session_infos,
201                        resurrectable_sessions.into(),
202                    ))
203                },
204                _ => Err("Malformed payload for the SessionUpdate Event"),
205            },
206            Some(ProtobufEventType::RunCommandResult) => match protobuf_event.payload {
207                Some(ProtobufEventPayload::RunCommandResultPayload(run_command_result_payload)) => {
208                    Ok(Event::RunCommandResult(
209                        run_command_result_payload.exit_code,
210                        run_command_result_payload.stdout,
211                        run_command_result_payload.stderr,
212                        run_command_result_payload
213                            .context
214                            .into_iter()
215                            .map(|c_i| (c_i.name, c_i.value))
216                            .collect(),
217                    ))
218                },
219                _ => Err("Malformed payload for the RunCommandResult Event"),
220            },
221            Some(ProtobufEventType::WebRequestResult) => match protobuf_event.payload {
222                Some(ProtobufEventPayload::WebRequestResultPayload(web_request_result_payload)) => {
223                    Ok(Event::WebRequestResult(
224                        web_request_result_payload.status as u16,
225                        web_request_result_payload
226                            .headers
227                            .into_iter()
228                            .map(|h| (h.name, h.value))
229                            .collect(),
230                        web_request_result_payload.body,
231                        web_request_result_payload
232                            .context
233                            .into_iter()
234                            .map(|c_i| (c_i.name, c_i.value))
235                            .collect(),
236                    ))
237                },
238                _ => Err("Malformed payload for the WebRequestResult Event"),
239            },
240            Some(ProtobufEventType::CommandPaneOpened) => match protobuf_event.payload {
241                Some(ProtobufEventPayload::CommandPaneOpenedPayload(
242                    command_pane_opened_payload,
243                )) => Ok(Event::CommandPaneOpened(
244                    command_pane_opened_payload.terminal_pane_id,
245                    command_pane_opened_payload
246                        .context
247                        .into_iter()
248                        .map(|c_i| (c_i.name, c_i.value))
249                        .collect(),
250                )),
251                _ => Err("Malformed payload for the CommandPaneOpened Event"),
252            },
253            Some(ProtobufEventType::CommandPaneExited) => match protobuf_event.payload {
254                Some(ProtobufEventPayload::CommandPaneExitedPayload(
255                    command_pane_exited_payload,
256                )) => Ok(Event::CommandPaneExited(
257                    command_pane_exited_payload.terminal_pane_id,
258                    command_pane_exited_payload.exit_code,
259                    command_pane_exited_payload
260                        .context
261                        .into_iter()
262                        .map(|c_i| (c_i.name, c_i.value))
263                        .collect(),
264                )),
265                _ => Err("Malformed payload for the CommandPaneExited Event"),
266            },
267            Some(ProtobufEventType::PaneClosed) => match protobuf_event.payload {
268                Some(ProtobufEventPayload::PaneClosedPayload(pane_closed_payload)) => {
269                    let pane_id = pane_closed_payload
270                        .pane_id
271                        .ok_or("Malformed payload for the PaneClosed Event")?;
272                    Ok(Event::PaneClosed(PaneId::try_from(pane_id)?))
273                },
274                _ => Err("Malformed payload for the PaneClosed Event"),
275            },
276            Some(ProtobufEventType::EditPaneOpened) => match protobuf_event.payload {
277                Some(ProtobufEventPayload::EditPaneOpenedPayload(command_pane_opened_payload)) => {
278                    Ok(Event::EditPaneOpened(
279                        command_pane_opened_payload.terminal_pane_id,
280                        command_pane_opened_payload
281                            .context
282                            .into_iter()
283                            .map(|c_i| (c_i.name, c_i.value))
284                            .collect(),
285                    ))
286                },
287                _ => Err("Malformed payload for the EditPaneOpened Event"),
288            },
289            Some(ProtobufEventType::EditPaneExited) => match protobuf_event.payload {
290                Some(ProtobufEventPayload::EditPaneExitedPayload(command_pane_exited_payload)) => {
291                    Ok(Event::EditPaneExited(
292                        command_pane_exited_payload.terminal_pane_id,
293                        command_pane_exited_payload.exit_code,
294                        command_pane_exited_payload
295                            .context
296                            .into_iter()
297                            .map(|c_i| (c_i.name, c_i.value))
298                            .collect(),
299                    ))
300                },
301                _ => Err("Malformed payload for the EditPaneExited Event"),
302            },
303            Some(ProtobufEventType::CommandPaneReRun) => match protobuf_event.payload {
304                Some(ProtobufEventPayload::CommandPaneRerunPayload(command_pane_rerun_payload)) => {
305                    Ok(Event::CommandPaneReRun(
306                        command_pane_rerun_payload.terminal_pane_id,
307                        command_pane_rerun_payload
308                            .context
309                            .into_iter()
310                            .map(|c_i| (c_i.name, c_i.value))
311                            .collect(),
312                    ))
313                },
314                _ => Err("Malformed payload for the CommandPaneReRun Event"),
315            },
316            Some(ProtobufEventType::FailedToWriteConfigToDisk) => match protobuf_event.payload {
317                Some(ProtobufEventPayload::FailedToWriteConfigToDiskPayload(
318                    failed_to_write_configuration_payload,
319                )) => Ok(Event::FailedToWriteConfigToDisk(
320                    failed_to_write_configuration_payload.file_path,
321                )),
322                _ => Err("Malformed payload for the FailedToWriteConfigToDisk Event"),
323            },
324            Some(ProtobufEventType::ListClients) => match protobuf_event.payload {
325                Some(ProtobufEventPayload::ListClientsPayload(mut list_clients_payload)) => {
326                    Ok(Event::ListClients(
327                        list_clients_payload
328                            .client_info
329                            .drain(..)
330                            .filter_map(|c| c.try_into().ok())
331                            .collect(),
332                    ))
333                },
334                _ => Err("Malformed payload for the FailedToWriteConfigToDisk Event"),
335            },
336            None => Err("Unknown Protobuf Event"),
337        }
338    }
339}
340
341impl TryFrom<ProtobufClientInfo> for ClientInfo {
342    type Error = &'static str;
343    fn try_from(protobuf_client_info: ProtobufClientInfo) -> Result<Self, &'static str> {
344        Ok(ClientInfo::new(
345            protobuf_client_info.client_id as u16,
346            protobuf_client_info
347                .pane_id
348                .ok_or("No pane id found")?
349                .try_into()?,
350            protobuf_client_info.running_command,
351            protobuf_client_info.is_current_client,
352        ))
353    }
354}
355
356impl TryFrom<ClientInfo> for ProtobufClientInfo {
357    type Error = &'static str;
358    fn try_from(client_info: ClientInfo) -> Result<Self, &'static str> {
359        Ok(ProtobufClientInfo {
360            client_id: client_info.client_id as u32,
361            pane_id: Some(client_info.pane_id.try_into()?),
362            running_command: client_info.running_command,
363            is_current_client: client_info.is_current_client,
364        })
365    }
366}
367
368impl TryFrom<Event> for ProtobufEvent {
369    type Error = &'static str;
370    fn try_from(event: Event) -> Result<Self, &'static str> {
371        match event {
372            Event::ModeUpdate(mode_info) => {
373                let protobuf_mode_update_payload = mode_info.try_into()?;
374                Ok(ProtobufEvent {
375                    name: ProtobufEventType::ModeUpdate as i32,
376                    payload: Some(event::Payload::ModeUpdatePayload(
377                        protobuf_mode_update_payload,
378                    )),
379                })
380            },
381            Event::TabUpdate(tab_infos) => {
382                let mut protobuf_tab_infos = vec![];
383                for tab_info in tab_infos {
384                    protobuf_tab_infos.push(tab_info.try_into()?);
385                }
386                let tab_update_payload = TabUpdatePayload {
387                    tab_info: protobuf_tab_infos,
388                };
389                Ok(ProtobufEvent {
390                    name: ProtobufEventType::TabUpdate as i32,
391                    payload: Some(event::Payload::TabUpdatePayload(tab_update_payload)),
392                })
393            },
394            Event::PaneUpdate(pane_manifest) => {
395                let mut protobuf_pane_manifests = vec![];
396                for (tab_index, pane_infos) in pane_manifest.panes {
397                    let mut protobuf_pane_infos = vec![];
398                    for pane_info in pane_infos {
399                        protobuf_pane_infos.push(pane_info.try_into()?);
400                    }
401                    protobuf_pane_manifests.push(ProtobufPaneManifest {
402                        tab_index: tab_index as u32,
403                        panes: protobuf_pane_infos,
404                    });
405                }
406                Ok(ProtobufEvent {
407                    name: ProtobufEventType::PaneUpdate as i32,
408                    payload: Some(event::Payload::PaneUpdatePayload(PaneUpdatePayload {
409                        pane_manifest: protobuf_pane_manifests,
410                    })),
411                })
412            },
413            Event::Key(key) => Ok(ProtobufEvent {
414                name: ProtobufEventType::Key as i32,
415                payload: Some(event::Payload::KeyPayload(key.try_into()?)),
416            }),
417            Event::Mouse(mouse_event) => {
418                let protobuf_mouse_payload = mouse_event.try_into()?;
419                Ok(ProtobufEvent {
420                    name: ProtobufEventType::Mouse as i32,
421                    payload: Some(event::Payload::MouseEventPayload(protobuf_mouse_payload)),
422                })
423            },
424            Event::Timer(seconds) => Ok(ProtobufEvent {
425                name: ProtobufEventType::Timer as i32,
426                payload: Some(event::Payload::TimerPayload(seconds as f32)),
427            }),
428            Event::CopyToClipboard(clipboard_destination) => {
429                let protobuf_copy_destination: ProtobufCopyDestination =
430                    clipboard_destination.try_into()?;
431                Ok(ProtobufEvent {
432                    name: ProtobufEventType::CopyToClipboard as i32,
433                    payload: Some(event::Payload::CopyToClipboardPayload(
434                        protobuf_copy_destination as i32,
435                    )),
436                })
437            },
438            Event::SystemClipboardFailure => Ok(ProtobufEvent {
439                name: ProtobufEventType::SystemClipboardFailure as i32,
440                payload: None,
441            }),
442            Event::InputReceived => Ok(ProtobufEvent {
443                name: ProtobufEventType::InputReceived as i32,
444                payload: None,
445            }),
446            Event::Visible(is_visible) => Ok(ProtobufEvent {
447                name: ProtobufEventType::Visible as i32,
448                payload: Some(event::Payload::VisiblePayload(is_visible)),
449            }),
450            Event::CustomMessage(message, payload) => Ok(ProtobufEvent {
451                name: ProtobufEventType::CustomMessage as i32,
452                payload: Some(event::Payload::CustomMessagePayload(CustomMessagePayload {
453                    message_name: message,
454                    payload,
455                })),
456            }),
457            Event::FileSystemCreate(event_paths) => {
458                let mut paths = vec![];
459                let mut paths_metadata = vec![];
460                for (path, path_metadata) in event_paths {
461                    paths.push(path.display().to_string());
462                    paths_metadata.push(path_metadata.into());
463                }
464                let file_list_payload = FileListPayload {
465                    paths,
466                    paths_metadata,
467                };
468                Ok(ProtobufEvent {
469                    name: ProtobufEventType::FileSystemCreate as i32,
470                    payload: Some(event::Payload::FileListPayload(file_list_payload)),
471                })
472            },
473            Event::FileSystemRead(event_paths) => {
474                let mut paths = vec![];
475                let mut paths_metadata = vec![];
476                for (path, path_metadata) in event_paths {
477                    paths.push(path.display().to_string());
478                    paths_metadata.push(path_metadata.into());
479                }
480                let file_list_payload = FileListPayload {
481                    paths,
482                    paths_metadata,
483                };
484                Ok(ProtobufEvent {
485                    name: ProtobufEventType::FileSystemRead as i32,
486                    payload: Some(event::Payload::FileListPayload(file_list_payload)),
487                })
488            },
489            Event::FileSystemUpdate(event_paths) => {
490                let mut paths = vec![];
491                let mut paths_metadata = vec![];
492                for (path, path_metadata) in event_paths {
493                    paths.push(path.display().to_string());
494                    paths_metadata.push(path_metadata.into());
495                }
496                let file_list_payload = FileListPayload {
497                    paths,
498                    paths_metadata,
499                };
500                Ok(ProtobufEvent {
501                    name: ProtobufEventType::FileSystemUpdate as i32,
502                    payload: Some(event::Payload::FileListPayload(file_list_payload)),
503                })
504            },
505            Event::FileSystemDelete(event_paths) => {
506                let mut paths = vec![];
507                let mut paths_metadata = vec![];
508                for (path, path_metadata) in event_paths {
509                    paths.push(path.display().to_string());
510                    paths_metadata.push(path_metadata.into());
511                }
512                let file_list_payload = FileListPayload {
513                    paths,
514                    paths_metadata,
515                };
516                Ok(ProtobufEvent {
517                    name: ProtobufEventType::FileSystemDelete as i32,
518                    payload: Some(event::Payload::FileListPayload(file_list_payload)),
519                })
520            },
521            Event::PermissionRequestResult(permission_status) => {
522                let granted = match permission_status {
523                    PermissionStatus::Granted => true,
524                    PermissionStatus::Denied => false,
525                };
526                Ok(ProtobufEvent {
527                    name: ProtobufEventType::PermissionRequestResult as i32,
528                    payload: Some(event::Payload::PermissionRequestResultPayload(
529                        PermissionRequestResultPayload { granted },
530                    )),
531                })
532            },
533            Event::SessionUpdate(session_infos, resurrectable_sessions) => {
534                let mut protobuf_session_manifests = vec![];
535                for session_info in session_infos {
536                    protobuf_session_manifests.push(session_info.try_into()?);
537                }
538                let mut protobuf_resurrectable_sessions = vec![];
539                for resurrectable_session in resurrectable_sessions {
540                    protobuf_resurrectable_sessions.push(resurrectable_session.into());
541                }
542                let session_update_payload = SessionUpdatePayload {
543                    session_manifests: protobuf_session_manifests,
544                    resurrectable_sessions: protobuf_resurrectable_sessions,
545                };
546                Ok(ProtobufEvent {
547                    name: ProtobufEventType::SessionUpdate as i32,
548                    payload: Some(event::Payload::SessionUpdatePayload(session_update_payload)),
549                })
550            },
551            Event::RunCommandResult(exit_code, stdout, stderr, context) => {
552                let run_command_result_payload = RunCommandResultPayload {
553                    exit_code,
554                    stdout,
555                    stderr,
556                    context: context
557                        .into_iter()
558                        .map(|(name, value)| ContextItem { name, value })
559                        .collect(),
560                };
561                Ok(ProtobufEvent {
562                    name: ProtobufEventType::RunCommandResult as i32,
563                    payload: Some(event::Payload::RunCommandResultPayload(
564                        run_command_result_payload,
565                    )),
566                })
567            },
568            Event::WebRequestResult(status, headers, body, context) => {
569                let web_request_result_payload = WebRequestResultPayload {
570                    status: status as i32,
571                    headers: headers
572                        .into_iter()
573                        .map(|(name, value)| Header { name, value })
574                        .collect(),
575                    body,
576                    context: context
577                        .into_iter()
578                        .map(|(name, value)| ContextItem { name, value })
579                        .collect(),
580                };
581                Ok(ProtobufEvent {
582                    name: ProtobufEventType::WebRequestResult as i32,
583                    payload: Some(event::Payload::WebRequestResultPayload(
584                        web_request_result_payload,
585                    )),
586                })
587            },
588            Event::CommandPaneOpened(terminal_pane_id, context) => {
589                let command_pane_opened_payload = CommandPaneOpenedPayload {
590                    terminal_pane_id,
591                    context: context
592                        .into_iter()
593                        .map(|(name, value)| ContextItem { name, value })
594                        .collect(),
595                };
596                Ok(ProtobufEvent {
597                    name: ProtobufEventType::CommandPaneOpened as i32,
598                    payload: Some(event::Payload::CommandPaneOpenedPayload(
599                        command_pane_opened_payload,
600                    )),
601                })
602            },
603            Event::CommandPaneExited(terminal_pane_id, exit_code, context) => {
604                let command_pane_exited_payload = CommandPaneExitedPayload {
605                    terminal_pane_id,
606                    exit_code,
607                    context: context
608                        .into_iter()
609                        .map(|(name, value)| ContextItem { name, value })
610                        .collect(),
611                };
612                Ok(ProtobufEvent {
613                    name: ProtobufEventType::CommandPaneExited as i32,
614                    payload: Some(event::Payload::CommandPaneExitedPayload(
615                        command_pane_exited_payload,
616                    )),
617                })
618            },
619            Event::PaneClosed(pane_id) => Ok(ProtobufEvent {
620                name: ProtobufEventType::PaneClosed as i32,
621                payload: Some(event::Payload::PaneClosedPayload(PaneClosedPayload {
622                    pane_id: Some(pane_id.try_into()?),
623                })),
624            }),
625            Event::EditPaneOpened(terminal_pane_id, context) => {
626                let command_pane_opened_payload = EditPaneOpenedPayload {
627                    terminal_pane_id,
628                    context: context
629                        .into_iter()
630                        .map(|(name, value)| ContextItem { name, value })
631                        .collect(),
632                };
633                Ok(ProtobufEvent {
634                    name: ProtobufEventType::EditPaneOpened as i32,
635                    payload: Some(event::Payload::EditPaneOpenedPayload(
636                        command_pane_opened_payload,
637                    )),
638                })
639            },
640            Event::EditPaneExited(terminal_pane_id, exit_code, context) => {
641                let command_pane_exited_payload = EditPaneExitedPayload {
642                    terminal_pane_id,
643                    exit_code,
644                    context: context
645                        .into_iter()
646                        .map(|(name, value)| ContextItem { name, value })
647                        .collect(),
648                };
649                Ok(ProtobufEvent {
650                    name: ProtobufEventType::EditPaneExited as i32,
651                    payload: Some(event::Payload::EditPaneExitedPayload(
652                        command_pane_exited_payload,
653                    )),
654                })
655            },
656            Event::CommandPaneReRun(terminal_pane_id, context) => {
657                let command_pane_rerun_payload = CommandPaneReRunPayload {
658                    terminal_pane_id,
659                    context: context
660                        .into_iter()
661                        .map(|(name, value)| ContextItem { name, value })
662                        .collect(),
663                };
664                Ok(ProtobufEvent {
665                    name: ProtobufEventType::CommandPaneReRun as i32,
666                    payload: Some(event::Payload::CommandPaneRerunPayload(
667                        command_pane_rerun_payload,
668                    )),
669                })
670            },
671            Event::FailedToWriteConfigToDisk(file_path) => Ok(ProtobufEvent {
672                name: ProtobufEventType::FailedToWriteConfigToDisk as i32,
673                payload: Some(event::Payload::FailedToWriteConfigToDiskPayload(
674                    FailedToWriteConfigToDiskPayload { file_path },
675                )),
676            }),
677            Event::ListClients(mut client_info_list) => Ok(ProtobufEvent {
678                name: ProtobufEventType::ListClients as i32,
679                payload: Some(event::Payload::ListClientsPayload(ListClientsPayload {
680                    client_info: client_info_list
681                        .drain(..)
682                        .filter_map(|c| c.try_into().ok())
683                        .collect(),
684                })),
685            }),
686        }
687    }
688}
689
690impl TryFrom<SessionInfo> for ProtobufSessionManifest {
691    type Error = &'static str;
692    fn try_from(session_info: SessionInfo) -> Result<Self, &'static str> {
693        let mut protobuf_pane_manifests = vec![];
694        for (tab_index, pane_infos) in session_info.panes.panes {
695            let mut protobuf_pane_infos = vec![];
696            for pane_info in pane_infos {
697                protobuf_pane_infos.push(pane_info.try_into()?);
698            }
699            protobuf_pane_manifests.push(ProtobufPaneManifest {
700                tab_index: tab_index as u32,
701                panes: protobuf_pane_infos,
702            });
703        }
704        Ok(ProtobufSessionManifest {
705            name: session_info.name,
706            panes: protobuf_pane_manifests,
707            tabs: session_info
708                .tabs
709                .iter()
710                .filter_map(|t| t.clone().try_into().ok())
711                .collect(),
712            connected_clients: session_info.connected_clients as u32,
713            is_current_session: session_info.is_current_session,
714            available_layouts: session_info
715                .available_layouts
716                .into_iter()
717                .filter_map(|l| ProtobufLayoutInfo::try_from(l).ok())
718                .collect(),
719            plugins: session_info
720                .plugins
721                .into_iter()
722                .map(|p| ProtobufPluginInfo::from(p))
723                .collect(),
724        })
725    }
726}
727
728impl From<(u32, PluginInfo)> for ProtobufPluginInfo {
729    fn from((plugin_id, plugin_info): (u32, PluginInfo)) -> ProtobufPluginInfo {
730        ProtobufPluginInfo {
731            plugin_id,
732            plugin_url: plugin_info.location,
733            plugin_config: plugin_info
734                .configuration
735                .into_iter()
736                .map(|(name, value)| ContextItem { name, value })
737                .collect(),
738        }
739    }
740}
741
742impl TryFrom<ProtobufSessionManifest> for SessionInfo {
743    type Error = &'static str;
744    fn try_from(protobuf_session_manifest: ProtobufSessionManifest) -> Result<Self, &'static str> {
745        let mut pane_manifest: HashMap<usize, Vec<PaneInfo>> = HashMap::new();
746        for protobuf_pane_manifest in protobuf_session_manifest.panes {
747            let tab_index = protobuf_pane_manifest.tab_index as usize;
748            let mut panes = vec![];
749            for protobuf_pane_info in protobuf_pane_manifest.panes {
750                panes.push(protobuf_pane_info.try_into()?);
751            }
752            if pane_manifest.contains_key(&tab_index) {
753                return Err("Duplicate tab definition in pane manifest");
754            }
755            pane_manifest.insert(tab_index, panes);
756        }
757        let panes = PaneManifest {
758            panes: pane_manifest,
759        };
760        let mut plugins = BTreeMap::new();
761        for plugin_info in protobuf_session_manifest.plugins.into_iter() {
762            let mut configuration = BTreeMap::new();
763            for context_item in plugin_info.plugin_config.into_iter() {
764                configuration.insert(context_item.name, context_item.value);
765            }
766            plugins.insert(
767                plugin_info.plugin_id,
768                PluginInfo {
769                    location: plugin_info.plugin_url,
770                    configuration,
771                },
772            );
773        }
774        Ok(SessionInfo {
775            name: protobuf_session_manifest.name,
776            tabs: protobuf_session_manifest
777                .tabs
778                .iter()
779                .filter_map(|t| t.clone().try_into().ok())
780                .collect(),
781            panes,
782            connected_clients: protobuf_session_manifest.connected_clients as usize,
783            is_current_session: protobuf_session_manifest.is_current_session,
784            available_layouts: protobuf_session_manifest
785                .available_layouts
786                .into_iter()
787                .filter_map(|l| LayoutInfo::try_from(l).ok())
788                .collect(),
789            plugins,
790        })
791    }
792}
793
794impl TryFrom<LayoutInfo> for ProtobufLayoutInfo {
795    type Error = &'static str;
796    fn try_from(layout_info: LayoutInfo) -> Result<Self, &'static str> {
797        match layout_info {
798            LayoutInfo::File(name) => Ok(ProtobufLayoutInfo {
799                source: "file".to_owned(),
800                name,
801            }),
802            LayoutInfo::BuiltIn(name) => Ok(ProtobufLayoutInfo {
803                source: "built-in".to_owned(),
804                name,
805            }),
806            LayoutInfo::Url(name) => Ok(ProtobufLayoutInfo {
807                source: "url".to_owned(),
808                name,
809            }),
810            LayoutInfo::Stringified(stringified_layout) => Ok(ProtobufLayoutInfo {
811                source: "stringified".to_owned(),
812                name: stringified_layout.clone(),
813            }),
814        }
815    }
816}
817
818impl TryFrom<ProtobufLayoutInfo> for LayoutInfo {
819    type Error = &'static str;
820    fn try_from(protobuf_layout_info: ProtobufLayoutInfo) -> Result<Self, &'static str> {
821        match protobuf_layout_info.source.as_str() {
822            "file" => Ok(LayoutInfo::File(protobuf_layout_info.name)),
823            "built-in" => Ok(LayoutInfo::BuiltIn(protobuf_layout_info.name)),
824            "url" => Ok(LayoutInfo::Url(protobuf_layout_info.name)),
825            "stringified" => Ok(LayoutInfo::Stringified(protobuf_layout_info.name)),
826            _ => Err("Unknown source for layout"),
827        }
828    }
829}
830
831impl TryFrom<CopyDestination> for ProtobufCopyDestination {
832    type Error = &'static str;
833    fn try_from(copy_destination: CopyDestination) -> Result<Self, &'static str> {
834        match copy_destination {
835            CopyDestination::Command => Ok(ProtobufCopyDestination::Command),
836            CopyDestination::Primary => Ok(ProtobufCopyDestination::Primary),
837            CopyDestination::System => Ok(ProtobufCopyDestination::System),
838        }
839    }
840}
841
842impl TryFrom<ProtobufCopyDestination> for CopyDestination {
843    type Error = &'static str;
844    fn try_from(protobuf_copy_destination: ProtobufCopyDestination) -> Result<Self, &'static str> {
845        match protobuf_copy_destination {
846            ProtobufCopyDestination::Command => Ok(CopyDestination::Command),
847            ProtobufCopyDestination::Primary => Ok(CopyDestination::Primary),
848            ProtobufCopyDestination::System => Ok(CopyDestination::System),
849        }
850    }
851}
852
853impl TryFrom<MouseEventPayload> for Mouse {
854    type Error = &'static str;
855    fn try_from(mouse_event_payload: MouseEventPayload) -> Result<Self, &'static str> {
856        match MouseEventName::from_i32(mouse_event_payload.mouse_event_name) {
857            Some(MouseEventName::MouseScrollUp) => match mouse_event_payload.mouse_event_payload {
858                Some(mouse_event_payload::MouseEventPayload::LineCount(line_count)) => {
859                    Ok(Mouse::ScrollUp(line_count as usize))
860                },
861                _ => Err("Malformed payload for mouse scroll up"),
862            },
863            Some(MouseEventName::MouseScrollDown) => {
864                match mouse_event_payload.mouse_event_payload {
865                    Some(mouse_event_payload::MouseEventPayload::LineCount(line_count)) => {
866                        Ok(Mouse::ScrollDown(line_count as usize))
867                    },
868                    _ => Err("Malformed payload for mouse scroll down"),
869                }
870            },
871            Some(MouseEventName::MouseLeftClick) => match mouse_event_payload.mouse_event_payload {
872                Some(mouse_event_payload::MouseEventPayload::Position(position)) => Ok(
873                    Mouse::LeftClick(position.line as isize, position.column as usize),
874                ),
875                _ => Err("Malformed payload for mouse left click"),
876            },
877            Some(MouseEventName::MouseRightClick) => {
878                match mouse_event_payload.mouse_event_payload {
879                    Some(mouse_event_payload::MouseEventPayload::Position(position)) => Ok(
880                        Mouse::RightClick(position.line as isize, position.column as usize),
881                    ),
882                    _ => Err("Malformed payload for mouse right click"),
883                }
884            },
885            Some(MouseEventName::MouseHold) => match mouse_event_payload.mouse_event_payload {
886                Some(mouse_event_payload::MouseEventPayload::Position(position)) => Ok(
887                    Mouse::Hold(position.line as isize, position.column as usize),
888                ),
889                _ => Err("Malformed payload for mouse hold"),
890            },
891            Some(MouseEventName::MouseRelease) => match mouse_event_payload.mouse_event_payload {
892                Some(mouse_event_payload::MouseEventPayload::Position(position)) => Ok(
893                    Mouse::Release(position.line as isize, position.column as usize),
894                ),
895                _ => Err("Malformed payload for mouse release"),
896            },
897            None => Err("Malformed payload for MouseEventName"),
898        }
899    }
900}
901
902impl TryFrom<Mouse> for MouseEventPayload {
903    type Error = &'static str;
904    fn try_from(mouse: Mouse) -> Result<Self, &'static str> {
905        match mouse {
906            Mouse::ScrollUp(number_of_lines) => Ok(MouseEventPayload {
907                mouse_event_name: MouseEventName::MouseScrollUp as i32,
908                mouse_event_payload: Some(mouse_event_payload::MouseEventPayload::LineCount(
909                    number_of_lines as u32,
910                )),
911            }),
912            Mouse::ScrollDown(number_of_lines) => Ok(MouseEventPayload {
913                mouse_event_name: MouseEventName::MouseScrollDown as i32,
914                mouse_event_payload: Some(mouse_event_payload::MouseEventPayload::LineCount(
915                    number_of_lines as u32,
916                )),
917            }),
918            Mouse::LeftClick(line, column) => Ok(MouseEventPayload {
919                mouse_event_name: MouseEventName::MouseLeftClick as i32,
920                mouse_event_payload: Some(mouse_event_payload::MouseEventPayload::Position(
921                    ProtobufPosition {
922                        line: line as i64,
923                        column: column as i64,
924                    },
925                )),
926            }),
927            Mouse::RightClick(line, column) => Ok(MouseEventPayload {
928                mouse_event_name: MouseEventName::MouseRightClick as i32,
929                mouse_event_payload: Some(mouse_event_payload::MouseEventPayload::Position(
930                    ProtobufPosition {
931                        line: line as i64,
932                        column: column as i64,
933                    },
934                )),
935            }),
936            Mouse::Hold(line, column) => Ok(MouseEventPayload {
937                mouse_event_name: MouseEventName::MouseHold as i32,
938                mouse_event_payload: Some(mouse_event_payload::MouseEventPayload::Position(
939                    ProtobufPosition {
940                        line: line as i64,
941                        column: column as i64,
942                    },
943                )),
944            }),
945            Mouse::Release(line, column) => Ok(MouseEventPayload {
946                mouse_event_name: MouseEventName::MouseRelease as i32,
947                mouse_event_payload: Some(mouse_event_payload::MouseEventPayload::Position(
948                    ProtobufPosition {
949                        line: line as i64,
950                        column: column as i64,
951                    },
952                )),
953            }),
954        }
955    }
956}
957
958impl TryFrom<ProtobufPaneInfo> for PaneInfo {
959    type Error = &'static str;
960    fn try_from(protobuf_pane_info: ProtobufPaneInfo) -> Result<Self, &'static str> {
961        Ok(PaneInfo {
962            id: protobuf_pane_info.id,
963            is_plugin: protobuf_pane_info.is_plugin,
964            is_focused: protobuf_pane_info.is_focused,
965            is_fullscreen: protobuf_pane_info.is_fullscreen,
966            is_floating: protobuf_pane_info.is_floating,
967            is_suppressed: protobuf_pane_info.is_suppressed,
968            title: protobuf_pane_info.title,
969            exited: protobuf_pane_info.exited,
970            exit_status: protobuf_pane_info.exit_status,
971            is_held: protobuf_pane_info.is_held,
972            pane_x: protobuf_pane_info.pane_x as usize,
973            pane_content_x: protobuf_pane_info.pane_content_x as usize,
974            pane_y: protobuf_pane_info.pane_y as usize,
975            pane_content_y: protobuf_pane_info.pane_content_y as usize,
976            pane_rows: protobuf_pane_info.pane_rows as usize,
977            pane_content_rows: protobuf_pane_info.pane_content_rows as usize,
978            pane_columns: protobuf_pane_info.pane_columns as usize,
979            pane_content_columns: protobuf_pane_info.pane_content_columns as usize,
980            cursor_coordinates_in_pane: protobuf_pane_info
981                .cursor_coordinates_in_pane
982                .map(|position| (position.column as usize, position.line as usize)),
983            terminal_command: protobuf_pane_info.terminal_command,
984            plugin_url: protobuf_pane_info.plugin_url,
985            is_selectable: protobuf_pane_info.is_selectable,
986        })
987    }
988}
989
990impl TryFrom<PaneInfo> for ProtobufPaneInfo {
991    type Error = &'static str;
992    fn try_from(pane_info: PaneInfo) -> Result<Self, &'static str> {
993        Ok(ProtobufPaneInfo {
994            id: pane_info.id,
995            is_plugin: pane_info.is_plugin,
996            is_focused: pane_info.is_focused,
997            is_fullscreen: pane_info.is_fullscreen,
998            is_floating: pane_info.is_floating,
999            is_suppressed: pane_info.is_suppressed,
1000            title: pane_info.title,
1001            exited: pane_info.exited,
1002            exit_status: pane_info.exit_status,
1003            is_held: pane_info.is_held,
1004            pane_x: pane_info.pane_x as u32,
1005            pane_content_x: pane_info.pane_content_x as u32,
1006            pane_y: pane_info.pane_y as u32,
1007            pane_content_y: pane_info.pane_content_y as u32,
1008            pane_rows: pane_info.pane_rows as u32,
1009            pane_content_rows: pane_info.pane_content_rows as u32,
1010            pane_columns: pane_info.pane_columns as u32,
1011            pane_content_columns: pane_info.pane_content_columns as u32,
1012            cursor_coordinates_in_pane: pane_info.cursor_coordinates_in_pane.map(|(x, y)| {
1013                ProtobufPosition {
1014                    column: x as i64,
1015                    line: y as i64,
1016                }
1017            }),
1018            terminal_command: pane_info.terminal_command,
1019            plugin_url: pane_info.plugin_url,
1020            is_selectable: pane_info.is_selectable,
1021        })
1022    }
1023}
1024
1025impl TryFrom<ProtobufTabInfo> for TabInfo {
1026    type Error = &'static str;
1027    fn try_from(protobuf_tab_info: ProtobufTabInfo) -> Result<Self, &'static str> {
1028        Ok(TabInfo {
1029            position: protobuf_tab_info.position as usize,
1030            name: protobuf_tab_info.name,
1031            active: protobuf_tab_info.active,
1032            panes_to_hide: protobuf_tab_info.panes_to_hide as usize,
1033            is_fullscreen_active: protobuf_tab_info.is_fullscreen_active,
1034            is_sync_panes_active: protobuf_tab_info.is_sync_panes_active,
1035            are_floating_panes_visible: protobuf_tab_info.are_floating_panes_visible,
1036            other_focused_clients: protobuf_tab_info
1037                .other_focused_clients
1038                .iter()
1039                .map(|c| *c as u16)
1040                .collect(),
1041            active_swap_layout_name: protobuf_tab_info.active_swap_layout_name,
1042            is_swap_layout_dirty: protobuf_tab_info.is_swap_layout_dirty,
1043        })
1044    }
1045}
1046
1047impl TryFrom<TabInfo> for ProtobufTabInfo {
1048    type Error = &'static str;
1049    fn try_from(tab_info: TabInfo) -> Result<Self, &'static str> {
1050        Ok(ProtobufTabInfo {
1051            position: tab_info.position as u32,
1052            name: tab_info.name,
1053            active: tab_info.active,
1054            panes_to_hide: tab_info.panes_to_hide as u32,
1055            is_fullscreen_active: tab_info.is_fullscreen_active,
1056            is_sync_panes_active: tab_info.is_sync_panes_active,
1057            are_floating_panes_visible: tab_info.are_floating_panes_visible,
1058            other_focused_clients: tab_info
1059                .other_focused_clients
1060                .iter()
1061                .map(|c| *c as u32)
1062                .collect(),
1063            active_swap_layout_name: tab_info.active_swap_layout_name,
1064            is_swap_layout_dirty: tab_info.is_swap_layout_dirty,
1065        })
1066    }
1067}
1068
1069impl TryFrom<ProtobufModeUpdatePayload> for ModeInfo {
1070    type Error = &'static str;
1071    fn try_from(
1072        mut protobuf_mode_update_payload: ProtobufModeUpdatePayload,
1073    ) -> Result<Self, &'static str> {
1074        let current_mode: InputMode =
1075            ProtobufInputMode::from_i32(protobuf_mode_update_payload.current_mode)
1076                .ok_or("Malformed InputMode in the ModeUpdate Event")?
1077                .try_into()?;
1078        let base_mode: Option<InputMode> = protobuf_mode_update_payload
1079            .base_mode
1080            .and_then(|b_m| ProtobufInputMode::from_i32(b_m)?.try_into().ok());
1081        let keybinds: Vec<(InputMode, Vec<(KeyWithModifier, Vec<Action>)>)> =
1082            protobuf_mode_update_payload
1083                .keybinds
1084                .iter_mut()
1085                .filter_map(|k| {
1086                    let input_mode: InputMode = ProtobufInputMode::from_i32(k.mode)
1087                        .ok_or("Malformed InputMode in the ModeUpdate Event")
1088                        .ok()?
1089                        .try_into()
1090                        .ok()?;
1091                    let mut keybinds: Vec<(KeyWithModifier, Vec<Action>)> = vec![];
1092                    for mut protobuf_keybind in k.key_bind.drain(..) {
1093                        let key: KeyWithModifier = protobuf_keybind.key.unwrap().try_into().ok()?;
1094                        let mut actions: Vec<Action> = vec![];
1095                        for action in protobuf_keybind.action.drain(..) {
1096                            if let Ok(action) = action.try_into() {
1097                                actions.push(action);
1098                            }
1099                        }
1100                        keybinds.push((key, actions));
1101                    }
1102                    Some((input_mode, keybinds))
1103                })
1104                .collect();
1105        let style: Style = protobuf_mode_update_payload
1106            .style
1107            .and_then(|m| m.try_into().ok())
1108            .ok_or("malformed payload for mode_info")?;
1109        let session_name = protobuf_mode_update_payload.session_name;
1110        let capabilities = PluginCapabilities {
1111            arrow_fonts: protobuf_mode_update_payload.arrow_fonts_support,
1112        };
1113        let mode_info = ModeInfo {
1114            mode: current_mode,
1115            keybinds,
1116            style,
1117            capabilities,
1118            session_name,
1119            base_mode,
1120        };
1121        Ok(mode_info)
1122    }
1123}
1124
1125impl TryFrom<ModeInfo> for ProtobufModeUpdatePayload {
1126    type Error = &'static str;
1127    fn try_from(mode_info: ModeInfo) -> Result<Self, &'static str> {
1128        let current_mode: ProtobufInputMode = mode_info.mode.try_into()?;
1129        let base_mode: Option<ProtobufInputMode> = mode_info
1130            .base_mode
1131            .and_then(|mode| ProtobufInputMode::try_from(mode).ok());
1132        let style: ProtobufStyle = mode_info.style.try_into()?;
1133        let arrow_fonts_support: bool = mode_info.capabilities.arrow_fonts;
1134        let session_name = mode_info.session_name;
1135        let mut protobuf_input_mode_keybinds: Vec<ProtobufInputModeKeybinds> = vec![];
1136        for (input_mode, input_mode_keybinds) in mode_info.keybinds {
1137            let mode: ProtobufInputMode = input_mode.try_into()?;
1138            let mut keybinds: Vec<ProtobufKeyBind> = vec![];
1139            for (key, actions) in input_mode_keybinds {
1140                let protobuf_key: ProtobufKey = key.try_into()?;
1141                let mut protobuf_actions: Vec<ProtobufAction> = vec![];
1142                for action in actions {
1143                    if let Ok(protobuf_action) = action.try_into() {
1144                        protobuf_actions.push(protobuf_action);
1145                    }
1146                }
1147                let key_bind = ProtobufKeyBind {
1148                    key: Some(protobuf_key),
1149                    action: protobuf_actions,
1150                };
1151                keybinds.push(key_bind);
1152            }
1153            let input_mode_keybind = ProtobufInputModeKeybinds {
1154                mode: mode as i32,
1155                key_bind: keybinds,
1156            };
1157            protobuf_input_mode_keybinds.push(input_mode_keybind);
1158        }
1159        Ok(ProtobufModeUpdatePayload {
1160            current_mode: current_mode as i32,
1161            style: Some(style),
1162            keybinds: protobuf_input_mode_keybinds,
1163            arrow_fonts_support,
1164            session_name,
1165            base_mode: base_mode.map(|b_m| b_m as i32),
1166        })
1167    }
1168}
1169
1170impl TryFrom<ProtobufEventNameList> for HashSet<EventType> {
1171    type Error = &'static str;
1172    fn try_from(protobuf_event_name_list: ProtobufEventNameList) -> Result<Self, &'static str> {
1173        let event_types: Vec<ProtobufEventType> = protobuf_event_name_list
1174            .event_types
1175            .iter()
1176            .filter_map(|i| ProtobufEventType::from_i32(*i))
1177            .collect();
1178        let event_types: Vec<EventType> = event_types
1179            .iter()
1180            .filter_map(|e| EventType::try_from(*e).ok())
1181            .collect();
1182        Ok(event_types.into_iter().collect())
1183    }
1184}
1185
1186impl TryFrom<HashSet<EventType>> for ProtobufEventNameList {
1187    type Error = &'static str;
1188    fn try_from(event_types: HashSet<EventType>) -> Result<Self, &'static str> {
1189        let protobuf_event_name_list = ProtobufEventNameList {
1190            event_types: event_types
1191                .iter()
1192                .filter_map(|e| ProtobufEventType::try_from(*e).ok())
1193                .map(|e| e as i32)
1194                .collect(),
1195        };
1196        Ok(protobuf_event_name_list)
1197    }
1198}
1199
1200impl TryFrom<ProtobufEventType> for EventType {
1201    type Error = &'static str;
1202    fn try_from(protobuf_event_type: ProtobufEventType) -> Result<Self, &'static str> {
1203        Ok(match protobuf_event_type {
1204            ProtobufEventType::ModeUpdate => EventType::ModeUpdate,
1205            ProtobufEventType::TabUpdate => EventType::TabUpdate,
1206            ProtobufEventType::PaneUpdate => EventType::PaneUpdate,
1207            ProtobufEventType::Key => EventType::Key,
1208            ProtobufEventType::Mouse => EventType::Mouse,
1209            ProtobufEventType::Timer => EventType::Timer,
1210            ProtobufEventType::CopyToClipboard => EventType::CopyToClipboard,
1211            ProtobufEventType::SystemClipboardFailure => EventType::SystemClipboardFailure,
1212            ProtobufEventType::InputReceived => EventType::InputReceived,
1213            ProtobufEventType::Visible => EventType::Visible,
1214            ProtobufEventType::CustomMessage => EventType::CustomMessage,
1215            ProtobufEventType::FileSystemCreate => EventType::FileSystemCreate,
1216            ProtobufEventType::FileSystemRead => EventType::FileSystemRead,
1217            ProtobufEventType::FileSystemUpdate => EventType::FileSystemUpdate,
1218            ProtobufEventType::FileSystemDelete => EventType::FileSystemDelete,
1219            ProtobufEventType::PermissionRequestResult => EventType::PermissionRequestResult,
1220            ProtobufEventType::SessionUpdate => EventType::SessionUpdate,
1221            ProtobufEventType::RunCommandResult => EventType::RunCommandResult,
1222            ProtobufEventType::WebRequestResult => EventType::WebRequestResult,
1223            ProtobufEventType::CommandPaneOpened => EventType::CommandPaneOpened,
1224            ProtobufEventType::CommandPaneExited => EventType::CommandPaneExited,
1225            ProtobufEventType::PaneClosed => EventType::PaneClosed,
1226            ProtobufEventType::EditPaneOpened => EventType::EditPaneOpened,
1227            ProtobufEventType::EditPaneExited => EventType::EditPaneExited,
1228            ProtobufEventType::CommandPaneReRun => EventType::CommandPaneReRun,
1229            ProtobufEventType::FailedToWriteConfigToDisk => EventType::FailedToWriteConfigToDisk,
1230            ProtobufEventType::ListClients => EventType::ListClients,
1231        })
1232    }
1233}
1234
1235impl TryFrom<EventType> for ProtobufEventType {
1236    type Error = &'static str;
1237    fn try_from(event_type: EventType) -> Result<Self, &'static str> {
1238        Ok(match event_type {
1239            EventType::ModeUpdate => ProtobufEventType::ModeUpdate,
1240            EventType::TabUpdate => ProtobufEventType::TabUpdate,
1241            EventType::PaneUpdate => ProtobufEventType::PaneUpdate,
1242            EventType::Key => ProtobufEventType::Key,
1243            EventType::Mouse => ProtobufEventType::Mouse,
1244            EventType::Timer => ProtobufEventType::Timer,
1245            EventType::CopyToClipboard => ProtobufEventType::CopyToClipboard,
1246            EventType::SystemClipboardFailure => ProtobufEventType::SystemClipboardFailure,
1247            EventType::InputReceived => ProtobufEventType::InputReceived,
1248            EventType::Visible => ProtobufEventType::Visible,
1249            EventType::CustomMessage => ProtobufEventType::CustomMessage,
1250            EventType::FileSystemCreate => ProtobufEventType::FileSystemCreate,
1251            EventType::FileSystemRead => ProtobufEventType::FileSystemRead,
1252            EventType::FileSystemUpdate => ProtobufEventType::FileSystemUpdate,
1253            EventType::FileSystemDelete => ProtobufEventType::FileSystemDelete,
1254            EventType::PermissionRequestResult => ProtobufEventType::PermissionRequestResult,
1255            EventType::SessionUpdate => ProtobufEventType::SessionUpdate,
1256            EventType::RunCommandResult => ProtobufEventType::RunCommandResult,
1257            EventType::WebRequestResult => ProtobufEventType::WebRequestResult,
1258            EventType::CommandPaneOpened => ProtobufEventType::CommandPaneOpened,
1259            EventType::CommandPaneExited => ProtobufEventType::CommandPaneExited,
1260            EventType::PaneClosed => ProtobufEventType::PaneClosed,
1261            EventType::EditPaneOpened => ProtobufEventType::EditPaneOpened,
1262            EventType::EditPaneExited => ProtobufEventType::EditPaneExited,
1263            EventType::CommandPaneReRun => ProtobufEventType::CommandPaneReRun,
1264            EventType::FailedToWriteConfigToDisk => ProtobufEventType::FailedToWriteConfigToDisk,
1265            EventType::ListClients => ProtobufEventType::ListClients,
1266        })
1267    }
1268}
1269
1270impl From<ProtobufResurrectableSession> for (String, Duration) {
1271    fn from(protobuf_resurrectable_session: ProtobufResurrectableSession) -> (String, Duration) {
1272        (
1273            protobuf_resurrectable_session.name,
1274            Duration::from_secs(protobuf_resurrectable_session.creation_time),
1275        )
1276    }
1277}
1278
1279impl From<(String, Duration)> for ProtobufResurrectableSession {
1280    fn from(session_name_and_creation_time: (String, Duration)) -> ProtobufResurrectableSession {
1281        ProtobufResurrectableSession {
1282            name: session_name_and_creation_time.0,
1283            creation_time: session_name_and_creation_time.1.as_secs(),
1284        }
1285    }
1286}
1287
1288impl From<&ProtobufFileMetadata> for Option<FileMetadata> {
1289    fn from(protobuf_file_metadata: &ProtobufFileMetadata) -> Option<FileMetadata> {
1290        if protobuf_file_metadata.metadata_is_set {
1291            Some(FileMetadata {
1292                is_file: protobuf_file_metadata.is_file,
1293                is_dir: protobuf_file_metadata.is_dir,
1294                is_symlink: protobuf_file_metadata.is_symlink,
1295                len: protobuf_file_metadata.len,
1296            })
1297        } else {
1298            None
1299        }
1300    }
1301}
1302
1303impl From<Option<FileMetadata>> for ProtobufFileMetadata {
1304    fn from(file_metadata: Option<FileMetadata>) -> ProtobufFileMetadata {
1305        match file_metadata {
1306            Some(file_metadata) => ProtobufFileMetadata {
1307                metadata_is_set: true,
1308                is_file: file_metadata.is_file,
1309                is_dir: file_metadata.is_dir,
1310                is_symlink: file_metadata.is_symlink,
1311                len: file_metadata.len,
1312            },
1313            None => ProtobufFileMetadata {
1314                metadata_is_set: false,
1315                ..Default::default()
1316            },
1317        }
1318    }
1319}
1320
1321#[test]
1322fn serialize_mode_update_event() {
1323    use prost::Message;
1324    let mode_update_event = Event::ModeUpdate(Default::default());
1325    let protobuf_event: ProtobufEvent = mode_update_event.clone().try_into().unwrap();
1326    let serialized_protobuf_event = protobuf_event.encode_to_vec();
1327    let deserialized_protobuf_event: ProtobufEvent =
1328        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
1329    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
1330    assert_eq!(
1331        mode_update_event, deserialized_event,
1332        "Event properly serialized/deserialized without change"
1333    );
1334}
1335
1336#[test]
1337fn serialize_mode_update_event_with_non_default_values() {
1338    use crate::data::{BareKey, Palette, PaletteColor, ThemeHue};
1339    use prost::Message;
1340    let mode_update_event = Event::ModeUpdate(ModeInfo {
1341        mode: InputMode::Locked,
1342        keybinds: vec![
1343            (
1344                InputMode::Locked,
1345                vec![(
1346                    KeyWithModifier::new(BareKey::Char('b')).with_alt_modifier(),
1347                    vec![Action::SwitchToMode(InputMode::Normal)],
1348                )],
1349            ),
1350            (
1351                InputMode::Tab,
1352                vec![(
1353                    KeyWithModifier::new(BareKey::Up).with_alt_modifier(),
1354                    vec![Action::SwitchToMode(InputMode::Pane)],
1355                )],
1356            ),
1357            (
1358                InputMode::Pane,
1359                vec![
1360                    (
1361                        KeyWithModifier::new(BareKey::Char('b')).with_ctrl_modifier(),
1362                        vec![
1363                            Action::SwitchToMode(InputMode::Tmux),
1364                            Action::Write(None, vec![10], false),
1365                        ],
1366                    ),
1367                    (
1368                        KeyWithModifier::new(BareKey::Char('a')),
1369                        vec![Action::WriteChars("foo".to_owned())],
1370                    ),
1371                ],
1372            ),
1373        ],
1374        style: Style {
1375            colors: Palette {
1376                source: crate::data::PaletteSource::Default,
1377                theme_hue: ThemeHue::Light,
1378                fg: PaletteColor::Rgb((1, 1, 1)),
1379                bg: PaletteColor::Rgb((200, 200, 200)),
1380                black: PaletteColor::EightBit(1),
1381                red: PaletteColor::EightBit(2),
1382                green: PaletteColor::EightBit(2),
1383                yellow: PaletteColor::EightBit(2),
1384                blue: PaletteColor::EightBit(2),
1385                magenta: PaletteColor::EightBit(2),
1386                cyan: PaletteColor::EightBit(2),
1387                white: PaletteColor::EightBit(2),
1388                orange: PaletteColor::EightBit(2),
1389                gray: PaletteColor::EightBit(2),
1390                purple: PaletteColor::EightBit(2),
1391                gold: PaletteColor::EightBit(2),
1392                silver: PaletteColor::EightBit(2),
1393                pink: PaletteColor::EightBit(2),
1394                brown: PaletteColor::Rgb((222, 221, 220)),
1395            },
1396            rounded_corners: true,
1397            hide_session_name: false,
1398        },
1399        capabilities: PluginCapabilities { arrow_fonts: false },
1400        session_name: Some("my awesome test session".to_owned()),
1401        base_mode: Some(InputMode::Locked),
1402    });
1403    let protobuf_event: ProtobufEvent = mode_update_event.clone().try_into().unwrap();
1404    let serialized_protobuf_event = protobuf_event.encode_to_vec();
1405    let deserialized_protobuf_event: ProtobufEvent =
1406        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
1407    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
1408    assert_eq!(
1409        mode_update_event, deserialized_event,
1410        "Event properly serialized/deserialized without change"
1411    );
1412}
1413
1414#[test]
1415fn serialize_tab_update_event() {
1416    use prost::Message;
1417    let tab_update_event = Event::TabUpdate(Default::default());
1418    let protobuf_event: ProtobufEvent = tab_update_event.clone().try_into().unwrap();
1419    let serialized_protobuf_event = protobuf_event.encode_to_vec();
1420    let deserialized_protobuf_event: ProtobufEvent =
1421        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
1422    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
1423    assert_eq!(
1424        tab_update_event, deserialized_event,
1425        "Event properly serialized/deserialized without change"
1426    );
1427}
1428
1429#[test]
1430fn serialize_tab_update_event_with_non_default_values() {
1431    use prost::Message;
1432    let tab_update_event = Event::TabUpdate(vec![
1433        TabInfo {
1434            position: 0,
1435            name: "First tab".to_owned(),
1436            active: true,
1437            panes_to_hide: 2,
1438            is_fullscreen_active: true,
1439            is_sync_panes_active: false,
1440            are_floating_panes_visible: true,
1441            other_focused_clients: vec![2, 3, 4],
1442            active_swap_layout_name: Some("my cool swap layout".to_owned()),
1443            is_swap_layout_dirty: false,
1444        },
1445        TabInfo {
1446            position: 1,
1447            name: "Secondtab".to_owned(),
1448            active: false,
1449            panes_to_hide: 5,
1450            is_fullscreen_active: false,
1451            is_sync_panes_active: true,
1452            are_floating_panes_visible: true,
1453            other_focused_clients: vec![1, 5, 111],
1454            active_swap_layout_name: None,
1455            is_swap_layout_dirty: true,
1456        },
1457        TabInfo::default(),
1458    ]);
1459    let protobuf_event: ProtobufEvent = tab_update_event.clone().try_into().unwrap();
1460    let serialized_protobuf_event = protobuf_event.encode_to_vec();
1461    let deserialized_protobuf_event: ProtobufEvent =
1462        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
1463    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
1464    assert_eq!(
1465        tab_update_event, deserialized_event,
1466        "Event properly serialized/deserialized without change"
1467    );
1468}
1469
1470#[test]
1471fn serialize_pane_update_event() {
1472    use prost::Message;
1473    let pane_update_event = Event::PaneUpdate(Default::default());
1474    let protobuf_event: ProtobufEvent = pane_update_event.clone().try_into().unwrap();
1475    let serialized_protobuf_event = protobuf_event.encode_to_vec();
1476    let deserialized_protobuf_event: ProtobufEvent =
1477        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
1478    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
1479    assert_eq!(
1480        pane_update_event, deserialized_event,
1481        "Event properly serialized/deserialized without change"
1482    );
1483}
1484
1485#[test]
1486fn serialize_key_event() {
1487    use crate::data::BareKey;
1488    use prost::Message;
1489    let key_event = Event::Key(KeyWithModifier::new(BareKey::Char('a')).with_ctrl_modifier());
1490    let protobuf_event: ProtobufEvent = key_event.clone().try_into().unwrap();
1491    let serialized_protobuf_event = protobuf_event.encode_to_vec();
1492    let deserialized_protobuf_event: ProtobufEvent =
1493        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
1494    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
1495    assert_eq!(
1496        key_event, deserialized_event,
1497        "Event properly serialized/deserialized without change"
1498    );
1499}
1500
1501#[test]
1502fn serialize_mouse_event() {
1503    use prost::Message;
1504    let mouse_event = Event::Mouse(Mouse::LeftClick(1, 1));
1505    let protobuf_event: ProtobufEvent = mouse_event.clone().try_into().unwrap();
1506    let serialized_protobuf_event = protobuf_event.encode_to_vec();
1507    let deserialized_protobuf_event: ProtobufEvent =
1508        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
1509    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
1510    assert_eq!(
1511        mouse_event, deserialized_event,
1512        "Event properly serialized/deserialized without change"
1513    );
1514}
1515
1516#[test]
1517fn serialize_mouse_event_without_position() {
1518    use prost::Message;
1519    let mouse_event = Event::Mouse(Mouse::ScrollUp(17));
1520    let protobuf_event: ProtobufEvent = mouse_event.clone().try_into().unwrap();
1521    let serialized_protobuf_event = protobuf_event.encode_to_vec();
1522    let deserialized_protobuf_event: ProtobufEvent =
1523        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
1524    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
1525    assert_eq!(
1526        mouse_event, deserialized_event,
1527        "Event properly serialized/deserialized without change"
1528    );
1529}
1530
1531#[test]
1532fn serialize_timer_event() {
1533    use prost::Message;
1534    let timer_event = Event::Timer(1.5);
1535    let protobuf_event: ProtobufEvent = timer_event.clone().try_into().unwrap();
1536    let serialized_protobuf_event = protobuf_event.encode_to_vec();
1537    let deserialized_protobuf_event: ProtobufEvent =
1538        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
1539    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
1540    assert_eq!(
1541        timer_event, deserialized_event,
1542        "Event properly serialized/deserialized without change"
1543    );
1544}
1545
1546#[test]
1547fn serialize_copy_to_clipboard_event() {
1548    use prost::Message;
1549    let copy_event = Event::CopyToClipboard(CopyDestination::Primary);
1550    let protobuf_event: ProtobufEvent = copy_event.clone().try_into().unwrap();
1551    let serialized_protobuf_event = protobuf_event.encode_to_vec();
1552    let deserialized_protobuf_event: ProtobufEvent =
1553        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
1554    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
1555    assert_eq!(
1556        copy_event, deserialized_event,
1557        "Event properly serialized/deserialized without change"
1558    );
1559}
1560
1561#[test]
1562fn serialize_clipboard_failure_event() {
1563    use prost::Message;
1564    let copy_event = Event::SystemClipboardFailure;
1565    let protobuf_event: ProtobufEvent = copy_event.clone().try_into().unwrap();
1566    let serialized_protobuf_event = protobuf_event.encode_to_vec();
1567    let deserialized_protobuf_event: ProtobufEvent =
1568        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
1569    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
1570    assert_eq!(
1571        copy_event, deserialized_event,
1572        "Event properly serialized/deserialized without change"
1573    );
1574}
1575
1576#[test]
1577fn serialize_input_received_event() {
1578    use prost::Message;
1579    let input_received_event = Event::InputReceived;
1580    let protobuf_event: ProtobufEvent = input_received_event.clone().try_into().unwrap();
1581    let serialized_protobuf_event = protobuf_event.encode_to_vec();
1582    let deserialized_protobuf_event: ProtobufEvent =
1583        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
1584    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
1585    assert_eq!(
1586        input_received_event, deserialized_event,
1587        "Event properly serialized/deserialized without change"
1588    );
1589}
1590
1591#[test]
1592fn serialize_visible_event() {
1593    use prost::Message;
1594    let visible_event = Event::Visible(true);
1595    let protobuf_event: ProtobufEvent = visible_event.clone().try_into().unwrap();
1596    let serialized_protobuf_event = protobuf_event.encode_to_vec();
1597    let deserialized_protobuf_event: ProtobufEvent =
1598        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
1599    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
1600    assert_eq!(
1601        visible_event, deserialized_event,
1602        "Event properly serialized/deserialized without change"
1603    );
1604}
1605
1606#[test]
1607fn serialize_custom_message_event() {
1608    use prost::Message;
1609    let custom_message_event = Event::CustomMessage("foo".to_owned(), "bar".to_owned());
1610    let protobuf_event: ProtobufEvent = custom_message_event.clone().try_into().unwrap();
1611    let serialized_protobuf_event = protobuf_event.encode_to_vec();
1612    let deserialized_protobuf_event: ProtobufEvent =
1613        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
1614    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
1615    assert_eq!(
1616        custom_message_event, deserialized_event,
1617        "Event properly serialized/deserialized without change"
1618    );
1619}
1620
1621#[test]
1622fn serialize_file_system_create_event() {
1623    use prost::Message;
1624    let file_system_event = Event::FileSystemCreate(vec![
1625        ("/absolute/path".into(), None),
1626        ("./relative_path".into(), Default::default()),
1627    ]);
1628    let protobuf_event: ProtobufEvent = file_system_event.clone().try_into().unwrap();
1629    let serialized_protobuf_event = protobuf_event.encode_to_vec();
1630    let deserialized_protobuf_event: ProtobufEvent =
1631        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
1632    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
1633    assert_eq!(
1634        file_system_event, deserialized_event,
1635        "Event properly serialized/deserialized without change"
1636    );
1637}
1638
1639#[test]
1640fn serialize_file_system_read_event() {
1641    use prost::Message;
1642    let file_system_event = Event::FileSystemRead(vec![
1643        ("/absolute/path".into(), None),
1644        ("./relative_path".into(), Default::default()),
1645    ]);
1646    let protobuf_event: ProtobufEvent = file_system_event.clone().try_into().unwrap();
1647    let serialized_protobuf_event = protobuf_event.encode_to_vec();
1648    let deserialized_protobuf_event: ProtobufEvent =
1649        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
1650    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
1651    assert_eq!(
1652        file_system_event, deserialized_event,
1653        "Event properly serialized/deserialized without change"
1654    );
1655}
1656
1657#[test]
1658fn serialize_file_system_update_event() {
1659    use prost::Message;
1660    let file_system_event = Event::FileSystemUpdate(vec![
1661        ("/absolute/path".into(), None),
1662        ("./relative_path".into(), Some(Default::default())),
1663    ]);
1664    let protobuf_event: ProtobufEvent = file_system_event.clone().try_into().unwrap();
1665    let serialized_protobuf_event = protobuf_event.encode_to_vec();
1666    let deserialized_protobuf_event: ProtobufEvent =
1667        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
1668    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
1669    assert_eq!(
1670        file_system_event, deserialized_event,
1671        "Event properly serialized/deserialized without change"
1672    );
1673}
1674
1675#[test]
1676fn serialize_file_system_delete_event() {
1677    use prost::Message;
1678    let file_system_event = Event::FileSystemDelete(vec![
1679        ("/absolute/path".into(), None),
1680        ("./relative_path".into(), Default::default()),
1681    ]);
1682    let protobuf_event: ProtobufEvent = file_system_event.clone().try_into().unwrap();
1683    let serialized_protobuf_event = protobuf_event.encode_to_vec();
1684    let deserialized_protobuf_event: ProtobufEvent =
1685        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
1686    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
1687    assert_eq!(
1688        file_system_event, deserialized_event,
1689        "Event properly serialized/deserialized without change"
1690    );
1691}
1692
1693#[test]
1694fn serialize_session_update_event() {
1695    use prost::Message;
1696    let session_update_event = Event::SessionUpdate(Default::default(), Default::default());
1697    let protobuf_event: ProtobufEvent = session_update_event.clone().try_into().unwrap();
1698    let serialized_protobuf_event = protobuf_event.encode_to_vec();
1699    let deserialized_protobuf_event: ProtobufEvent =
1700        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
1701    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
1702    assert_eq!(
1703        session_update_event, deserialized_event,
1704        "Event properly serialized/deserialized without change"
1705    );
1706}
1707
1708#[test]
1709fn serialize_session_update_event_with_non_default_values() {
1710    use prost::Message;
1711    let tab_infos = vec![
1712        TabInfo {
1713            position: 0,
1714            name: "First tab".to_owned(),
1715            active: true,
1716            panes_to_hide: 2,
1717            is_fullscreen_active: true,
1718            is_sync_panes_active: false,
1719            are_floating_panes_visible: true,
1720            other_focused_clients: vec![2, 3, 4],
1721            active_swap_layout_name: Some("my cool swap layout".to_owned()),
1722            is_swap_layout_dirty: false,
1723        },
1724        TabInfo {
1725            position: 1,
1726            name: "Secondtab".to_owned(),
1727            active: false,
1728            panes_to_hide: 5,
1729            is_fullscreen_active: false,
1730            is_sync_panes_active: true,
1731            are_floating_panes_visible: true,
1732            other_focused_clients: vec![1, 5, 111],
1733            active_swap_layout_name: None,
1734            is_swap_layout_dirty: true,
1735        },
1736        TabInfo::default(),
1737    ];
1738    let mut panes = HashMap::new();
1739    let panes_list = vec![
1740        PaneInfo {
1741            id: 1,
1742            is_plugin: false,
1743            is_focused: true,
1744            is_fullscreen: true,
1745            is_floating: false,
1746            is_suppressed: false,
1747            title: "pane 1".to_owned(),
1748            exited: false,
1749            exit_status: None,
1750            is_held: false,
1751            pane_x: 0,
1752            pane_content_x: 1,
1753            pane_y: 0,
1754            pane_content_y: 1,
1755            pane_rows: 5,
1756            pane_content_rows: 4,
1757            pane_columns: 22,
1758            pane_content_columns: 21,
1759            cursor_coordinates_in_pane: Some((0, 0)),
1760            terminal_command: Some("foo".to_owned()),
1761            plugin_url: None,
1762            is_selectable: true,
1763        },
1764        PaneInfo {
1765            id: 1,
1766            is_plugin: true,
1767            is_focused: true,
1768            is_fullscreen: true,
1769            is_floating: false,
1770            is_suppressed: false,
1771            title: "pane 1".to_owned(),
1772            exited: false,
1773            exit_status: None,
1774            is_held: false,
1775            pane_x: 0,
1776            pane_content_x: 1,
1777            pane_y: 0,
1778            pane_content_y: 1,
1779            pane_rows: 5,
1780            pane_content_rows: 4,
1781            pane_columns: 22,
1782            pane_content_columns: 21,
1783            cursor_coordinates_in_pane: Some((0, 0)),
1784            terminal_command: None,
1785            plugin_url: Some("i_am_a_fake_plugin".to_owned()),
1786            is_selectable: true,
1787        },
1788    ];
1789    panes.insert(0, panes_list);
1790    let mut plugins = BTreeMap::new();
1791    let mut plugin_configuration = BTreeMap::new();
1792    plugin_configuration.insert("config_key".to_owned(), "config_value".to_owned());
1793    plugins.insert(
1794        1,
1795        PluginInfo {
1796            location: "https://example.com/my-plugin.wasm".to_owned(),
1797            configuration: plugin_configuration,
1798        },
1799    );
1800    let session_info_1 = SessionInfo {
1801        name: "session 1".to_owned(),
1802        tabs: tab_infos,
1803        panes: PaneManifest { panes },
1804        connected_clients: 2,
1805        is_current_session: true,
1806        available_layouts: vec![
1807            LayoutInfo::File("layout 1".to_owned()),
1808            LayoutInfo::BuiltIn("layout2".to_owned()),
1809            LayoutInfo::File("layout3".to_owned()),
1810        ],
1811        plugins,
1812    };
1813    let session_info_2 = SessionInfo {
1814        name: "session 2".to_owned(),
1815        tabs: vec![],
1816        panes: PaneManifest {
1817            panes: HashMap::new(),
1818        },
1819        connected_clients: 0,
1820        is_current_session: false,
1821        available_layouts: vec![
1822            LayoutInfo::File("layout 1".to_owned()),
1823            LayoutInfo::BuiltIn("layout2".to_owned()),
1824            LayoutInfo::File("layout3".to_owned()),
1825        ],
1826        plugins: Default::default(),
1827    };
1828    let session_infos = vec![session_info_1, session_info_2];
1829    let resurrectable_sessions = vec![];
1830
1831    let session_update_event = Event::SessionUpdate(session_infos, resurrectable_sessions);
1832    let protobuf_event: ProtobufEvent = session_update_event.clone().try_into().unwrap();
1833    let serialized_protobuf_event = protobuf_event.encode_to_vec();
1834    let deserialized_protobuf_event: ProtobufEvent =
1835        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
1836    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
1837    assert_eq!(
1838        session_update_event, deserialized_event,
1839        "Event properly serialized/deserialized without change"
1840    );
1841}
1842
1843// note: ProtobufPaneId and ProtobufPaneType are not the same as the ones defined in plugin_command.rs
1844// this is a duplicate type - we are forced to do this because protobuffs do not support recursive
1845// imports
1846impl TryFrom<ProtobufPaneId> for PaneId {
1847    type Error = &'static str;
1848    fn try_from(protobuf_pane_id: ProtobufPaneId) -> Result<Self, &'static str> {
1849        match ProtobufPaneType::from_i32(protobuf_pane_id.pane_type) {
1850            Some(ProtobufPaneType::Terminal) => Ok(PaneId::Terminal(protobuf_pane_id.id)),
1851            Some(ProtobufPaneType::Plugin) => Ok(PaneId::Plugin(protobuf_pane_id.id)),
1852            None => Err("Failed to convert PaneId"),
1853        }
1854    }
1855}
1856
1857// note: ProtobufPaneId and ProtobufPaneType are not the same as the ones defined in plugin_command.rs
1858// this is a duplicate type - we are forced to do this because protobuffs do not support recursive
1859// imports
1860impl TryFrom<PaneId> for ProtobufPaneId {
1861    type Error = &'static str;
1862    fn try_from(pane_id: PaneId) -> Result<Self, &'static str> {
1863        match pane_id {
1864            PaneId::Terminal(id) => Ok(ProtobufPaneId {
1865                pane_type: ProtobufPaneType::Terminal as i32,
1866                id,
1867            }),
1868            PaneId::Plugin(id) => Ok(ProtobufPaneId {
1869                pane_type: ProtobufPaneType::Plugin as i32,
1870                id,
1871            }),
1872        }
1873    }
1874}