Skip to main content

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,
5        layout_parsing_error::ErrorType as ProtobufLayoutParsingErrorType,
6        pane_scrollback_response, ActionCompletePayload as ProtobufActionCompletePayload,
7        ActivePaneScrollPayload as ProtobufActivePaneScrollPayload,
8        AvailableLayoutInfoPayload as ProtobufAvailableLayoutInfoPayload,
9        ClientInfo as ProtobufClientInfo, ClientPaneHistory as ProtobufClientPaneHistory,
10        ClientTabHistory as ProtobufClientTabHistory,
11        CommandChangedPayload as ProtobufCommandChangedPayload, ContextItem as ProtobufContextItem,
12        CopyDestination as ProtobufCopyDestination, CwdChangedPayload as ProtobufCwdChangedPayload,
13        Event as ProtobufEvent, EventNameList as ProtobufEventNameList,
14        EventType as ProtobufEventType, FileMetadata as ProtobufFileMetadata,
15        HintTextPayload as ProtobufHintTextPayload,
16        HostTerminalThemeChangedPayload as ProtobufHostTerminalThemeChangedPayload,
17        HostTerminalThemeIndication as ProtobufHostTerminalThemeIndication,
18        InputModeKeybinds as ProtobufInputModeKeybinds, KdlError as ProtobufKdlError,
19        KdlErrorVariant as ProtobufKdlErrorVariant, KeyBind as ProtobufKeyBind,
20        LayoutInfo as ProtobufLayoutInfo, LayoutMetadata as ProtobufLayoutMetadata,
21        LayoutParsingError as ProtobufLayoutParsingError,
22        LayoutWithError as ProtobufLayoutWithError, ModeUpdatePayload as ProtobufModeUpdatePayload,
23        PaneContents as ProtobufPaneContents, PaneContentsEntry as ProtobufPaneContentsEntry,
24        PaneFrameStyle as ProtobufPaneFrameStyle, PaneId as ProtobufPaneId,
25        PaneInfo as ProtobufPaneInfo, PaneManifest as ProtobufPaneManifest,
26        PaneMetadata as ProtobufPaneMetadata,
27        PaneRenderReportPayload as ProtobufPaneRenderReportPayload,
28        PaneScrollbackResponse as ProtobufPaneScrollbackResponse, PaneType as ProtobufPaneType,
29        PluginConfigurationChangedPayload as ProtobufPluginConfigurationChangedPayload,
30        PluginInfo as ProtobufPluginInfo, ResurrectableSession as ProtobufResurrectableSession,
31        SelectedText as ProtobufSelectedText, SessionManifest as ProtobufSessionManifest,
32        SoftKeyboardVisibilityChangedPayload as ProtobufSoftKeyboardVisibilityChangedPayload,
33        StyledText as ProtobufStyledText, StyledTextIndices as ProtobufStyledTextIndices,
34        SyntaxError as ProtobufSyntaxError, TabInfo as ProtobufTabInfo,
35        TabMetadata as ProtobufTabMetadata, UserActionPayload as ProtobufUserActionPayload,
36        WebServerStatusPayload as ProtobufWebServerStatusPayload, WebSharing as ProtobufWebSharing,
37        *,
38    },
39    input_mode::InputMode as ProtobufInputMode,
40    key::Key as ProtobufKey,
41    style::Style as ProtobufStyle,
42};
43#[allow(hidden_glob_reexports)]
44use crate::data::{
45    ClientId, ClientInfo, CopyDestination, Event, EventType, FileMetadata, HostTerminalThemeMode,
46    InputMode, KeyWithModifier, LayoutInfo, LayoutMetadata, ModeInfo, Mouse, PaneContents, PaneId,
47    PaneInfo, PaneManifest, PaneMetadata, PaneScrollbackResponse, PermissionStatus,
48    PluginCapabilities, PluginInfo, SelectedText, SessionInfo, Style, StyledText, TabInfo,
49    TabMetadata, WebServerStatus, WebSharing,
50};
51
52use crate::errors::prelude::*;
53use crate::input::actions::Action;
54
55use std::collections::{BTreeMap, HashMap, HashSet};
56use std::convert::TryFrom;
57use std::net::IpAddr;
58use std::path::PathBuf;
59use std::str::FromStr;
60use std::time::Duration;
61
62impl TryFrom<ProtobufEvent> for Event {
63    type Error = &'static str;
64    fn try_from(protobuf_event: ProtobufEvent) -> Result<Self, &'static str> {
65        match ProtobufEventType::try_from(protobuf_event.name).ok() {
66            Some(ProtobufEventType::ModeUpdate) => match protobuf_event.payload {
67                Some(ProtobufEventPayload::ModeUpdatePayload(protobuf_mode_update_payload)) => {
68                    let mode_info: ModeInfo = protobuf_mode_update_payload.try_into()?;
69                    Ok(Event::ModeUpdate(mode_info))
70                },
71                _ => Err("Malformed payload for the ModeUpdate Event"),
72            },
73            Some(ProtobufEventType::TabUpdate) => match protobuf_event.payload {
74                Some(ProtobufEventPayload::TabUpdatePayload(protobuf_tab_info_payload)) => {
75                    let mut tab_infos: Vec<TabInfo> = vec![];
76                    for protobuf_tab_info in protobuf_tab_info_payload.tab_info {
77                        tab_infos.push(TabInfo::try_from(protobuf_tab_info)?);
78                    }
79                    Ok(Event::TabUpdate(tab_infos))
80                },
81                _ => Err("Malformed payload for the TabUpdate Event"),
82            },
83            Some(ProtobufEventType::PaneUpdate) => match protobuf_event.payload {
84                Some(ProtobufEventPayload::PaneUpdatePayload(protobuf_pane_update_payload)) => {
85                    let mut pane_manifest: HashMap<usize, Vec<PaneInfo>> = HashMap::new();
86                    for protobuf_pane_manifest in protobuf_pane_update_payload.pane_manifest {
87                        let tab_index = protobuf_pane_manifest.tab_index as usize;
88                        let mut panes = vec![];
89                        for protobuf_pane_info in protobuf_pane_manifest.panes {
90                            panes.push(protobuf_pane_info.try_into()?);
91                        }
92                        if pane_manifest.contains_key(&tab_index) {
93                            return Err("Duplicate tab definition in pane manifest");
94                        }
95                        pane_manifest.insert(tab_index, panes);
96                    }
97                    Ok(Event::PaneUpdate(PaneManifest {
98                        panes: pane_manifest,
99                    }))
100                },
101                _ => Err("Malformed payload for the PaneUpdate Event"),
102            },
103            Some(ProtobufEventType::Key) => match protobuf_event.payload {
104                Some(ProtobufEventPayload::KeyPayload(protobuf_key)) => {
105                    Ok(Event::Key(protobuf_key.try_into()?))
106                },
107                _ => Err("Malformed payload for the Key Event"),
108            },
109            Some(ProtobufEventType::Mouse) => match protobuf_event.payload {
110                Some(ProtobufEventPayload::MouseEventPayload(protobuf_mouse)) => {
111                    Ok(Event::Mouse(protobuf_mouse.try_into()?))
112                },
113                _ => Err("Malformed payload for the Mouse Event"),
114            },
115            Some(ProtobufEventType::Timer) => match protobuf_event.payload {
116                Some(ProtobufEventPayload::TimerPayload(seconds)) => {
117                    Ok(Event::Timer(seconds as f64))
118                },
119                _ => Err("Malformed payload for the Timer Event"),
120            },
121            Some(ProtobufEventType::CopyToClipboard) => match protobuf_event.payload {
122                Some(ProtobufEventPayload::CopyToClipboardPayload(copy_to_clipboard)) => {
123                    let protobuf_copy_to_clipboard =
124                        ProtobufCopyDestination::try_from(copy_to_clipboard)
125                            .ok()
126                            .ok_or("Malformed copy to clipboard payload")?;
127                    Ok(Event::CopyToClipboard(
128                        protobuf_copy_to_clipboard.try_into()?,
129                    ))
130                },
131                _ => Err("Malformed payload for the Copy To Clipboard Event"),
132            },
133            Some(ProtobufEventType::SystemClipboardFailure) => match protobuf_event.payload {
134                None => Ok(Event::SystemClipboardFailure),
135                _ => Err("Malformed payload for the system clipboard failure Event"),
136            },
137            Some(ProtobufEventType::InputReceived) => match protobuf_event.payload {
138                None => Ok(Event::InputReceived),
139                _ => Err("Malformed payload for the input received Event"),
140            },
141            Some(ProtobufEventType::Visible) => match protobuf_event.payload {
142                Some(ProtobufEventPayload::VisiblePayload(is_visible)) => {
143                    Ok(Event::Visible(is_visible))
144                },
145                _ => Err("Malformed payload for the visible Event"),
146            },
147            Some(ProtobufEventType::CustomMessage) => match protobuf_event.payload {
148                Some(ProtobufEventPayload::CustomMessagePayload(custom_message_payload)) => {
149                    Ok(Event::CustomMessage(
150                        custom_message_payload.message_name,
151                        custom_message_payload.payload,
152                    ))
153                },
154                _ => Err("Malformed payload for the custom message Event"),
155            },
156            Some(ProtobufEventType::FileSystemCreate) => match protobuf_event.payload {
157                Some(ProtobufEventPayload::FileListPayload(file_list_payload)) => {
158                    let file_paths = file_list_payload
159                        .paths
160                        .iter()
161                        .zip(file_list_payload.paths_metadata.iter())
162                        .map(|(p, m)| (PathBuf::from(p), m.into()))
163                        .collect();
164                    Ok(Event::FileSystemCreate(file_paths))
165                },
166                _ => Err("Malformed payload for the file system create Event"),
167            },
168            Some(ProtobufEventType::FileSystemRead) => match protobuf_event.payload {
169                Some(ProtobufEventPayload::FileListPayload(file_list_payload)) => {
170                    let file_paths = file_list_payload
171                        .paths
172                        .iter()
173                        .zip(file_list_payload.paths_metadata.iter())
174                        .map(|(p, m)| (PathBuf::from(p), m.into()))
175                        .collect();
176                    Ok(Event::FileSystemRead(file_paths))
177                },
178                _ => Err("Malformed payload for the file system read Event"),
179            },
180            Some(ProtobufEventType::FileSystemUpdate) => match protobuf_event.payload {
181                Some(ProtobufEventPayload::FileListPayload(file_list_payload)) => {
182                    let file_paths = file_list_payload
183                        .paths
184                        .iter()
185                        .zip(file_list_payload.paths_metadata.iter())
186                        .map(|(p, m)| (PathBuf::from(p), m.into()))
187                        .collect();
188                    Ok(Event::FileSystemUpdate(file_paths))
189                },
190                _ => Err("Malformed payload for the file system update Event"),
191            },
192            Some(ProtobufEventType::FileSystemDelete) => match protobuf_event.payload {
193                Some(ProtobufEventPayload::FileListPayload(file_list_payload)) => {
194                    let file_paths = file_list_payload
195                        .paths
196                        .iter()
197                        .zip(file_list_payload.paths_metadata.iter())
198                        .map(|(p, m)| (PathBuf::from(p), m.into()))
199                        .collect();
200                    Ok(Event::FileSystemDelete(file_paths))
201                },
202                _ => Err("Malformed payload for the file system delete Event"),
203            },
204            Some(ProtobufEventType::PermissionRequestResult) => match protobuf_event.payload {
205                Some(ProtobufEventPayload::PermissionRequestResultPayload(payload)) => {
206                    if payload.granted {
207                        Ok(Event::PermissionRequestResult(PermissionStatus::Granted))
208                    } else {
209                        Ok(Event::PermissionRequestResult(PermissionStatus::Denied))
210                    }
211                },
212                _ => Err("Malformed payload for the file system delete Event"),
213            },
214            Some(ProtobufEventType::SessionUpdate) => match protobuf_event.payload {
215                Some(ProtobufEventPayload::SessionUpdatePayload(
216                    protobuf_session_update_payload,
217                )) => {
218                    let mut session_infos: Vec<SessionInfo> = vec![];
219                    let mut resurrectable_sessions: Vec<(String, Duration)> = vec![];
220                    for protobuf_session_info in protobuf_session_update_payload.session_manifests {
221                        session_infos.push(SessionInfo::try_from(protobuf_session_info)?);
222                    }
223                    for protobuf_resurrectable_session in
224                        protobuf_session_update_payload.resurrectable_sessions
225                    {
226                        resurrectable_sessions.push(protobuf_resurrectable_session.into());
227                    }
228                    Ok(Event::SessionUpdate(
229                        session_infos,
230                        resurrectable_sessions.into(),
231                    ))
232                },
233                _ => Err("Malformed payload for the SessionUpdate Event"),
234            },
235            Some(ProtobufEventType::RunCommandResult) => match protobuf_event.payload {
236                Some(ProtobufEventPayload::RunCommandResultPayload(run_command_result_payload)) => {
237                    Ok(Event::RunCommandResult(
238                        run_command_result_payload.exit_code,
239                        run_command_result_payload.stdout,
240                        run_command_result_payload.stderr,
241                        run_command_result_payload
242                            .context
243                            .into_iter()
244                            .map(|c_i| (c_i.name, c_i.value))
245                            .collect(),
246                    ))
247                },
248                _ => Err("Malformed payload for the RunCommandResult Event"),
249            },
250            Some(ProtobufEventType::WebRequestResult) => match protobuf_event.payload {
251                Some(ProtobufEventPayload::WebRequestResultPayload(web_request_result_payload)) => {
252                    Ok(Event::WebRequestResult(
253                        web_request_result_payload.status as u16,
254                        web_request_result_payload
255                            .headers
256                            .into_iter()
257                            .map(|h| (h.name, h.value))
258                            .collect(),
259                        web_request_result_payload.body,
260                        web_request_result_payload
261                            .context
262                            .into_iter()
263                            .map(|c_i| (c_i.name, c_i.value))
264                            .collect(),
265                    ))
266                },
267                _ => Err("Malformed payload for the WebRequestResult Event"),
268            },
269            Some(ProtobufEventType::CommandPaneOpened) => match protobuf_event.payload {
270                Some(ProtobufEventPayload::CommandPaneOpenedPayload(
271                    command_pane_opened_payload,
272                )) => Ok(Event::CommandPaneOpened(
273                    command_pane_opened_payload.terminal_pane_id,
274                    command_pane_opened_payload
275                        .context
276                        .into_iter()
277                        .map(|c_i| (c_i.name, c_i.value))
278                        .collect(),
279                )),
280                _ => Err("Malformed payload for the CommandPaneOpened Event"),
281            },
282            Some(ProtobufEventType::CommandPaneExited) => match protobuf_event.payload {
283                Some(ProtobufEventPayload::CommandPaneExitedPayload(
284                    command_pane_exited_payload,
285                )) => Ok(Event::CommandPaneExited(
286                    command_pane_exited_payload.terminal_pane_id,
287                    command_pane_exited_payload.exit_code,
288                    command_pane_exited_payload
289                        .context
290                        .into_iter()
291                        .map(|c_i| (c_i.name, c_i.value))
292                        .collect(),
293                )),
294                _ => Err("Malformed payload for the CommandPaneExited Event"),
295            },
296            Some(ProtobufEventType::PaneClosed) => match protobuf_event.payload {
297                Some(ProtobufEventPayload::PaneClosedPayload(pane_closed_payload)) => {
298                    let pane_id = pane_closed_payload
299                        .pane_id
300                        .ok_or("Malformed payload for the PaneClosed Event")?;
301                    Ok(Event::PaneClosed(PaneId::try_from(pane_id)?))
302                },
303                _ => Err("Malformed payload for the PaneClosed Event"),
304            },
305            Some(ProtobufEventType::EditPaneOpened) => match protobuf_event.payload {
306                Some(ProtobufEventPayload::EditPaneOpenedPayload(command_pane_opened_payload)) => {
307                    Ok(Event::EditPaneOpened(
308                        command_pane_opened_payload.terminal_pane_id,
309                        command_pane_opened_payload
310                            .context
311                            .into_iter()
312                            .map(|c_i| (c_i.name, c_i.value))
313                            .collect(),
314                    ))
315                },
316                _ => Err("Malformed payload for the EditPaneOpened Event"),
317            },
318            Some(ProtobufEventType::EditPaneExited) => match protobuf_event.payload {
319                Some(ProtobufEventPayload::EditPaneExitedPayload(command_pane_exited_payload)) => {
320                    Ok(Event::EditPaneExited(
321                        command_pane_exited_payload.terminal_pane_id,
322                        command_pane_exited_payload.exit_code,
323                        command_pane_exited_payload
324                            .context
325                            .into_iter()
326                            .map(|c_i| (c_i.name, c_i.value))
327                            .collect(),
328                    ))
329                },
330                _ => Err("Malformed payload for the EditPaneExited Event"),
331            },
332            Some(ProtobufEventType::CommandPaneReRun) => match protobuf_event.payload {
333                Some(ProtobufEventPayload::CommandPaneRerunPayload(command_pane_rerun_payload)) => {
334                    Ok(Event::CommandPaneReRun(
335                        command_pane_rerun_payload.terminal_pane_id,
336                        command_pane_rerun_payload
337                            .context
338                            .into_iter()
339                            .map(|c_i| (c_i.name, c_i.value))
340                            .collect(),
341                    ))
342                },
343                _ => Err("Malformed payload for the CommandPaneReRun Event"),
344            },
345            Some(ProtobufEventType::FailedToWriteConfigToDisk) => match protobuf_event.payload {
346                Some(ProtobufEventPayload::FailedToWriteConfigToDiskPayload(
347                    failed_to_write_configuration_payload,
348                )) => Ok(Event::FailedToWriteConfigToDisk(
349                    failed_to_write_configuration_payload.file_path,
350                )),
351                _ => Err("Malformed payload for the FailedToWriteConfigToDisk Event"),
352            },
353            Some(ProtobufEventType::ListClients) => match protobuf_event.payload {
354                Some(ProtobufEventPayload::ListClientsPayload(mut list_clients_payload)) => {
355                    Ok(Event::ListClients(
356                        list_clients_payload
357                            .client_info
358                            .drain(..)
359                            .filter_map(|c| c.try_into().ok())
360                            .collect(),
361                    ))
362                },
363                _ => Err("Malformed payload for the FailedToWriteConfigToDisk Event"),
364            },
365            Some(ProtobufEventType::HostFolderChanged) => match protobuf_event.payload {
366                Some(ProtobufEventPayload::HostFolderChangedPayload(
367                    host_folder_changed_payload,
368                )) => Ok(Event::HostFolderChanged(PathBuf::from(
369                    host_folder_changed_payload.new_host_folder_path,
370                ))),
371                _ => Err("Malformed payload for the HostFolderChanged Event"),
372            },
373            Some(ProtobufEventType::FailedToChangeHostFolder) => match protobuf_event.payload {
374                Some(ProtobufEventPayload::FailedToChangeHostFolderPayload(
375                    failed_to_change_host_folder_payload,
376                )) => Ok(Event::FailedToChangeHostFolder(
377                    failed_to_change_host_folder_payload.error_message,
378                )),
379                _ => Err("Malformed payload for the FailedToChangeHostFolder Event"),
380            },
381            Some(ProtobufEventType::PastedText) => match protobuf_event.payload {
382                Some(ProtobufEventPayload::PastedTextPayload(pasted_text_payload)) => {
383                    Ok(Event::PastedText(pasted_text_payload.pasted_text))
384                },
385                _ => Err("Malformed payload for the PastedText Event"),
386            },
387            Some(ProtobufEventType::ConfigWasWrittenToDisk) => match protobuf_event.payload {
388                None => Ok(Event::ConfigWasWrittenToDisk),
389                _ => Err("Malformed payload for the ConfigWasWrittenToDisk Event"),
390            },
391            Some(ProtobufEventType::WebServerStatus) => match protobuf_event.payload {
392                Some(ProtobufEventPayload::WebServerStatusPayload(web_server_status)) => {
393                    Ok(Event::WebServerStatus(web_server_status.try_into()?))
394                },
395                _ => Err("Malformed payload for the WebServerStatus Event"),
396            },
397            Some(ProtobufEventType::BeforeClose) => match protobuf_event.payload {
398                None => Ok(Event::BeforeClose),
399                _ => Err("Malformed payload for the BeforeClose Event"),
400            },
401            Some(ProtobufEventType::FailedToStartWebServer) => match protobuf_event.payload {
402                Some(ProtobufEventPayload::FailedToStartWebServerPayload(
403                    failed_to_start_web_server_payload,
404                )) => Ok(Event::FailedToStartWebServer(
405                    failed_to_start_web_server_payload.error,
406                )),
407                _ => Err("Malformed payload for the FailedToStartWebServer Event"),
408            },
409            Some(ProtobufEventType::InterceptedKeyPress) => match protobuf_event.payload {
410                Some(ProtobufEventPayload::KeyPayload(protobuf_key)) => {
411                    Ok(Event::InterceptedKeyPress(protobuf_key.try_into()?))
412                },
413                _ => Err("Malformed payload for the InterceptedKeyPress Event"),
414            },
415            Some(ProtobufEventType::PaneRenderReport) => match protobuf_event.payload {
416                Some(ProtobufEventPayload::PaneRenderReportPayload(protobuf_payload)) => {
417                    Ok(Event::PaneRenderReport(protobuf_payload.try_into()?))
418                },
419                _ => Err("Malformed payload for the PaneRenderReport Event"),
420            },
421            Some(ProtobufEventType::UserAction) => match protobuf_event.payload {
422                Some(ProtobufEventPayload::UserActionPayload(protobuf_payload)) => {
423                    let action: Action = protobuf_payload
424                        .action
425                        .ok_or("Missing action in UserAction payload")?
426                        .try_into()
427                        .map_err(|_| "Failed to convert Action in UserAction payload")?;
428                    let client_id = protobuf_payload.client_id as u16;
429                    let terminal_id = protobuf_payload.terminal_id;
430                    let cli_client_id = protobuf_payload.cli_client_id.map(|id| id as u16);
431                    Ok(Event::UserAction(
432                        action,
433                        client_id,
434                        terminal_id,
435                        cli_client_id,
436                    ))
437                },
438                _ => Err("Malformed payload for the UserAction Event"),
439            },
440            Some(ProtobufEventType::ActionComplete) => match protobuf_event.payload {
441                Some(ProtobufEventPayload::ActionCompletePayload(protobuf_payload)) => {
442                    let action: Action = protobuf_payload
443                        .action
444                        .ok_or("Missing action in ActionComplete payload")?
445                        .try_into()
446                        .map_err(|_| "Failed to convert Action in ActionComplete payload")?;
447                    let pane_id = protobuf_payload
448                        .pane_id
449                        .map(|id| id.try_into())
450                        .transpose()
451                        .map_err(|_| "Failed to convert PaneId in ActionComplete payload")?;
452                    let context: BTreeMap<String, String> = protobuf_payload
453                        .context
454                        .into_iter()
455                        .map(|item| (item.name, item.value))
456                        .collect();
457                    Ok(Event::ActionComplete(action, pane_id, context))
458                },
459                _ => Err("Malformed payload for the ActionComplete Event"),
460            },
461            Some(ProtobufEventType::CwdChanged) => match protobuf_event.payload {
462                Some(ProtobufEventPayload::CwdChangedPayload(protobuf_payload)) => {
463                    let pane_id: PaneId = protobuf_payload
464                        .pane_id
465                        .ok_or("Missing pane_id in CwdChanged payload")?
466                        .try_into()
467                        .map_err(|_| "Failed to convert PaneId in CwdChanged payload")?;
468                    let new_cwd = PathBuf::from(protobuf_payload.new_cwd);
469                    let focused_client_ids: Vec<ClientId> = protobuf_payload
470                        .focused_client_ids
471                        .into_iter()
472                        .map(|id| id as u16)
473                        .collect();
474                    Ok(Event::CwdChanged(pane_id, new_cwd, focused_client_ids))
475                },
476                _ => Err("Malformed payload for the CwdChanged Event"),
477            },
478            Some(ProtobufEventType::CommandChanged) => match protobuf_event.payload {
479                Some(ProtobufEventPayload::CommandChangedPayload(p)) => {
480                    let pane_id: PaneId = p
481                        .pane_id
482                        .ok_or("Missing pane_id in CommandChanged payload")?
483                        .try_into()
484                        .map_err(|_| "Failed to convert PaneId in CommandChanged payload")?;
485                    let focused_client_ids: Vec<ClientId> = p
486                        .focused_client_ids
487                        .into_iter()
488                        .map(|id| id as u16)
489                        .collect();
490                    Ok(Event::CommandChanged(
491                        pane_id,
492                        p.command,
493                        p.is_foreground,
494                        focused_client_ids,
495                    ))
496                },
497                _ => Err("Malformed payload for the CommandChanged Event"),
498            },
499            Some(ProtobufEventType::AvailableLayoutInfo) => match protobuf_event.payload {
500                Some(ProtobufEventPayload::AvailableLayoutInfoPayload(
501                    available_layout_info_payload,
502                )) => {
503                    let mut available_layouts: Vec<LayoutInfo> = vec![];
504                    let mut layouts_with_errors: Vec<crate::data::LayoutWithError> = vec![];
505
506                    for protobuf_layout_info in available_layout_info_payload.available_layouts {
507                        available_layouts.push(LayoutInfo::try_from(protobuf_layout_info)?);
508                    }
509
510                    for protobuf_error in available_layout_info_payload.layouts_with_errors {
511                        layouts_with_errors
512                            .push(crate::data::LayoutWithError::try_from(protobuf_error)?);
513                    }
514
515                    Ok(Event::AvailableLayoutInfo(
516                        available_layouts,
517                        layouts_with_errors,
518                    ))
519                },
520                _ => Err("Malformed payload for the AvailableLayoutInfo Event"),
521            },
522            Some(ProtobufEventType::PluginConfigurationChanged) => match protobuf_event.payload {
523                Some(ProtobufEventPayload::PluginConfigurationChangedPayload(payload)) => {
524                    let configuration = payload
525                        .configuration
526                        .into_iter()
527                        .map(|item| (item.name, item.value))
528                        .collect();
529                    Ok(Event::PluginConfigurationChanged(configuration))
530                },
531                _ => Err("Malformed payload for PluginConfigurationChanged Event"),
532            },
533            Some(ProtobufEventType::HighlightClicked) => match protobuf_event.payload {
534                Some(ProtobufEventPayload::HighlightClickedPayload(p)) => {
535                    let pane_id = p
536                        .pane_id
537                        .ok_or("Missing pane_id in HighlightClicked")?
538                        .try_into()?;
539                    let context = p
540                        .context
541                        .into_iter()
542                        .map(|item| (item.name, item.value))
543                        .collect();
544                    Ok(Event::HighlightClicked {
545                        pane_id,
546                        pattern: p.pattern,
547                        matched_string: p.matched_string,
548                        context,
549                    })
550                },
551                _ => Err("Malformed payload for HighlightClicked Event"),
552            },
553            Some(ProtobufEventType::InitialKeybinds) => match protobuf_event.payload {
554                Some(ProtobufEventPayload::InitialKeybindsPayload(p)) => {
555                    let keybinds = p
556                        .keybinds
557                        .into_iter()
558                        .filter_map(|imk| {
559                            let mode: InputMode = ProtobufInputMode::try_from(imk.mode)
560                                .ok()?
561                                .try_into()
562                                .ok()?;
563                            let key_binds: Vec<(KeyWithModifier, Vec<Action>)> = imk
564                                .key_bind
565                                .into_iter()
566                                .filter_map(|kb| {
567                                    let key: KeyWithModifier = kb.key?.try_into().ok()?;
568                                    let actions: Vec<Action> = kb
569                                        .action
570                                        .into_iter()
571                                        .filter_map(|a| a.try_into().ok())
572                                        .collect();
573                                    Some((key, actions))
574                                })
575                                .collect();
576                            Some((mode, key_binds))
577                        })
578                        .collect();
579                    Ok(Event::InitialKeybinds(keybinds))
580                },
581                _ => Err("Malformed payload for InitialKeybinds Event"),
582            },
583            Some(ProtobufEventType::HostTerminalThemeChanged) => match protobuf_event.payload {
584                Some(ProtobufEventPayload::HostTerminalThemeChangedPayload(p)) => {
585                    let mode = ProtobufHostTerminalThemeIndication::try_from(p.mode)
586                        .ok()
587                        .ok_or("Unknown HostTerminalThemeIndication")?;
588                    Ok(Event::HostTerminalThemeChanged(mode.into()))
589                },
590                _ => Err("Malformed payload for HostTerminalThemeChanged Event"),
591            },
592            Some(ProtobufEventType::SoftKeyboardVisibilityChanged) => {
593                match protobuf_event.payload {
594                    Some(ProtobufEventPayload::SoftKeyboardVisibilityChangedPayload(p)) => {
595                        Ok(Event::SoftKeyboardVisibilityChanged(p.visible))
596                    },
597                    _ => Err("Malformed payload for SoftKeyboardVisibilityChanged Event"),
598                }
599            },
600            Some(ProtobufEventType::HintText) => match protobuf_event.payload {
601                Some(ProtobufEventPayload::HintTextPayload(p)) => {
602                    let mut hint_text = BTreeMap::new();
603                    for (width, styled_text) in p.hint_text {
604                        hint_text.insert(width as usize, StyledText::from(styled_text));
605                    }
606                    Ok(Event::HintText(hint_text))
607                },
608                _ => Err("Malformed payload for HintText Event"),
609            },
610            Some(ProtobufEventType::ActivePaneScroll) => match protobuf_event.payload {
611                Some(ProtobufEventPayload::ActivePaneScrollPayload(p)) => {
612                    let scroll = match (p.position, p.length) {
613                        (Some(position), Some(length)) => {
614                            Some((position as usize, length as usize))
615                        },
616                        _ => None,
617                    };
618                    Ok(Event::ActivePaneScroll(scroll))
619                },
620                _ => Err("Malformed payload for ActivePaneScroll Event"),
621            },
622            None => Err("Unknown Protobuf Event"),
623        }
624    }
625}
626
627impl TryFrom<ProtobufClientInfo> for ClientInfo {
628    type Error = &'static str;
629    fn try_from(protobuf_client_info: ProtobufClientInfo) -> Result<Self, &'static str> {
630        Ok(ClientInfo::new(
631            protobuf_client_info.client_id as u16,
632            protobuf_client_info
633                .pane_id
634                .ok_or("No pane id found")?
635                .try_into()?,
636            protobuf_client_info.running_command,
637            protobuf_client_info.is_current_client,
638        ))
639    }
640}
641
642impl TryFrom<ClientInfo> for ProtobufClientInfo {
643    type Error = &'static str;
644    fn try_from(client_info: ClientInfo) -> Result<Self, &'static str> {
645        Ok(ProtobufClientInfo {
646            client_id: client_info.client_id as u32,
647            pane_id: Some(client_info.pane_id.try_into()?),
648            running_command: client_info.running_command,
649            is_current_client: client_info.is_current_client,
650        })
651    }
652}
653
654impl TryFrom<Event> for ProtobufEvent {
655    type Error = &'static str;
656    fn try_from(event: Event) -> Result<Self, &'static str> {
657        match event {
658            Event::ModeUpdate(mode_info) => {
659                let protobuf_mode_update_payload = mode_info.try_into()?;
660                Ok(ProtobufEvent {
661                    name: ProtobufEventType::ModeUpdate as i32,
662                    payload: Some(event::Payload::ModeUpdatePayload(
663                        protobuf_mode_update_payload,
664                    )),
665                })
666            },
667            Event::TabUpdate(tab_infos) => {
668                let mut protobuf_tab_infos = vec![];
669                for tab_info in tab_infos {
670                    protobuf_tab_infos.push(tab_info.try_into()?);
671                }
672                let tab_update_payload = TabUpdatePayload {
673                    tab_info: protobuf_tab_infos,
674                };
675                Ok(ProtobufEvent {
676                    name: ProtobufEventType::TabUpdate as i32,
677                    payload: Some(event::Payload::TabUpdatePayload(tab_update_payload)),
678                })
679            },
680            Event::PaneUpdate(pane_manifest) => {
681                let mut protobuf_pane_manifests = vec![];
682                for (tab_index, pane_infos) in pane_manifest.panes {
683                    let mut protobuf_pane_infos = vec![];
684                    for pane_info in pane_infos {
685                        protobuf_pane_infos.push(pane_info.try_into()?);
686                    }
687                    protobuf_pane_manifests.push(ProtobufPaneManifest {
688                        tab_index: tab_index as u32,
689                        panes: protobuf_pane_infos,
690                    });
691                }
692                Ok(ProtobufEvent {
693                    name: ProtobufEventType::PaneUpdate as i32,
694                    payload: Some(event::Payload::PaneUpdatePayload(PaneUpdatePayload {
695                        pane_manifest: protobuf_pane_manifests,
696                    })),
697                })
698            },
699            Event::Key(key) => Ok(ProtobufEvent {
700                name: ProtobufEventType::Key as i32,
701                payload: Some(event::Payload::KeyPayload(key.try_into()?)),
702            }),
703            Event::Mouse(mouse_event) => {
704                let protobuf_mouse_payload = mouse_event.try_into()?;
705                Ok(ProtobufEvent {
706                    name: ProtobufEventType::Mouse as i32,
707                    payload: Some(event::Payload::MouseEventPayload(protobuf_mouse_payload)),
708                })
709            },
710            Event::Timer(seconds) => Ok(ProtobufEvent {
711                name: ProtobufEventType::Timer as i32,
712                payload: Some(event::Payload::TimerPayload(seconds as f32)),
713            }),
714            Event::CopyToClipboard(clipboard_destination) => {
715                let protobuf_copy_destination: ProtobufCopyDestination =
716                    clipboard_destination.try_into()?;
717                Ok(ProtobufEvent {
718                    name: ProtobufEventType::CopyToClipboard as i32,
719                    payload: Some(event::Payload::CopyToClipboardPayload(
720                        protobuf_copy_destination as i32,
721                    )),
722                })
723            },
724            Event::SystemClipboardFailure => Ok(ProtobufEvent {
725                name: ProtobufEventType::SystemClipboardFailure as i32,
726                payload: None,
727            }),
728            Event::InputReceived => Ok(ProtobufEvent {
729                name: ProtobufEventType::InputReceived as i32,
730                payload: None,
731            }),
732            Event::Visible(is_visible) => Ok(ProtobufEvent {
733                name: ProtobufEventType::Visible as i32,
734                payload: Some(event::Payload::VisiblePayload(is_visible)),
735            }),
736            Event::CustomMessage(message, payload) => Ok(ProtobufEvent {
737                name: ProtobufEventType::CustomMessage as i32,
738                payload: Some(event::Payload::CustomMessagePayload(CustomMessagePayload {
739                    message_name: message,
740                    payload,
741                })),
742            }),
743            Event::FileSystemCreate(event_paths) => {
744                let mut paths = vec![];
745                let mut paths_metadata = vec![];
746                for (path, path_metadata) in event_paths {
747                    paths.push(path.display().to_string());
748                    paths_metadata.push(path_metadata.into());
749                }
750                let file_list_payload = FileListPayload {
751                    paths,
752                    paths_metadata,
753                };
754                Ok(ProtobufEvent {
755                    name: ProtobufEventType::FileSystemCreate as i32,
756                    payload: Some(event::Payload::FileListPayload(file_list_payload)),
757                })
758            },
759            Event::FileSystemRead(event_paths) => {
760                let mut paths = vec![];
761                let mut paths_metadata = vec![];
762                for (path, path_metadata) in event_paths {
763                    paths.push(path.display().to_string());
764                    paths_metadata.push(path_metadata.into());
765                }
766                let file_list_payload = FileListPayload {
767                    paths,
768                    paths_metadata,
769                };
770                Ok(ProtobufEvent {
771                    name: ProtobufEventType::FileSystemRead as i32,
772                    payload: Some(event::Payload::FileListPayload(file_list_payload)),
773                })
774            },
775            Event::FileSystemUpdate(event_paths) => {
776                let mut paths = vec![];
777                let mut paths_metadata = vec![];
778                for (path, path_metadata) in event_paths {
779                    paths.push(path.display().to_string());
780                    paths_metadata.push(path_metadata.into());
781                }
782                let file_list_payload = FileListPayload {
783                    paths,
784                    paths_metadata,
785                };
786                Ok(ProtobufEvent {
787                    name: ProtobufEventType::FileSystemUpdate as i32,
788                    payload: Some(event::Payload::FileListPayload(file_list_payload)),
789                })
790            },
791            Event::FileSystemDelete(event_paths) => {
792                let mut paths = vec![];
793                let mut paths_metadata = vec![];
794                for (path, path_metadata) in event_paths {
795                    paths.push(path.display().to_string());
796                    paths_metadata.push(path_metadata.into());
797                }
798                let file_list_payload = FileListPayload {
799                    paths,
800                    paths_metadata,
801                };
802                Ok(ProtobufEvent {
803                    name: ProtobufEventType::FileSystemDelete as i32,
804                    payload: Some(event::Payload::FileListPayload(file_list_payload)),
805                })
806            },
807            Event::PermissionRequestResult(permission_status) => {
808                let granted = match permission_status {
809                    PermissionStatus::Granted => true,
810                    PermissionStatus::Denied => false,
811                };
812                Ok(ProtobufEvent {
813                    name: ProtobufEventType::PermissionRequestResult as i32,
814                    payload: Some(event::Payload::PermissionRequestResultPayload(
815                        PermissionRequestResultPayload { granted },
816                    )),
817                })
818            },
819            Event::SessionUpdate(session_infos, resurrectable_sessions) => {
820                let mut protobuf_session_manifests = vec![];
821                for session_info in session_infos {
822                    protobuf_session_manifests.push(session_info.try_into()?);
823                }
824                let mut protobuf_resurrectable_sessions = vec![];
825                for resurrectable_session in resurrectable_sessions {
826                    protobuf_resurrectable_sessions.push(resurrectable_session.into());
827                }
828                let session_update_payload = SessionUpdatePayload {
829                    session_manifests: protobuf_session_manifests,
830                    resurrectable_sessions: protobuf_resurrectable_sessions,
831                };
832                Ok(ProtobufEvent {
833                    name: ProtobufEventType::SessionUpdate as i32,
834                    payload: Some(event::Payload::SessionUpdatePayload(session_update_payload)),
835                })
836            },
837            Event::RunCommandResult(exit_code, stdout, stderr, context) => {
838                let run_command_result_payload = RunCommandResultPayload {
839                    exit_code,
840                    stdout,
841                    stderr,
842                    context: context
843                        .into_iter()
844                        .map(|(name, value)| ContextItem { name, value })
845                        .collect(),
846                };
847                Ok(ProtobufEvent {
848                    name: ProtobufEventType::RunCommandResult as i32,
849                    payload: Some(event::Payload::RunCommandResultPayload(
850                        run_command_result_payload,
851                    )),
852                })
853            },
854            Event::WebRequestResult(status, headers, body, context) => {
855                let web_request_result_payload = WebRequestResultPayload {
856                    status: status as i32,
857                    headers: headers
858                        .into_iter()
859                        .map(|(name, value)| Header { name, value })
860                        .collect(),
861                    body,
862                    context: context
863                        .into_iter()
864                        .map(|(name, value)| ContextItem { name, value })
865                        .collect(),
866                };
867                Ok(ProtobufEvent {
868                    name: ProtobufEventType::WebRequestResult as i32,
869                    payload: Some(event::Payload::WebRequestResultPayload(
870                        web_request_result_payload,
871                    )),
872                })
873            },
874            Event::CommandPaneOpened(terminal_pane_id, context) => {
875                let command_pane_opened_payload = CommandPaneOpenedPayload {
876                    terminal_pane_id,
877                    context: context
878                        .into_iter()
879                        .map(|(name, value)| ContextItem { name, value })
880                        .collect(),
881                };
882                Ok(ProtobufEvent {
883                    name: ProtobufEventType::CommandPaneOpened as i32,
884                    payload: Some(event::Payload::CommandPaneOpenedPayload(
885                        command_pane_opened_payload,
886                    )),
887                })
888            },
889            Event::CommandPaneExited(terminal_pane_id, exit_code, context) => {
890                let command_pane_exited_payload = CommandPaneExitedPayload {
891                    terminal_pane_id,
892                    exit_code,
893                    context: context
894                        .into_iter()
895                        .map(|(name, value)| ContextItem { name, value })
896                        .collect(),
897                };
898                Ok(ProtobufEvent {
899                    name: ProtobufEventType::CommandPaneExited as i32,
900                    payload: Some(event::Payload::CommandPaneExitedPayload(
901                        command_pane_exited_payload,
902                    )),
903                })
904            },
905            Event::PaneClosed(pane_id) => Ok(ProtobufEvent {
906                name: ProtobufEventType::PaneClosed as i32,
907                payload: Some(event::Payload::PaneClosedPayload(PaneClosedPayload {
908                    pane_id: Some(pane_id.try_into()?),
909                })),
910            }),
911            Event::EditPaneOpened(terminal_pane_id, context) => {
912                let command_pane_opened_payload = EditPaneOpenedPayload {
913                    terminal_pane_id,
914                    context: context
915                        .into_iter()
916                        .map(|(name, value)| ContextItem { name, value })
917                        .collect(),
918                };
919                Ok(ProtobufEvent {
920                    name: ProtobufEventType::EditPaneOpened as i32,
921                    payload: Some(event::Payload::EditPaneOpenedPayload(
922                        command_pane_opened_payload,
923                    )),
924                })
925            },
926            Event::EditPaneExited(terminal_pane_id, exit_code, context) => {
927                let command_pane_exited_payload = EditPaneExitedPayload {
928                    terminal_pane_id,
929                    exit_code,
930                    context: context
931                        .into_iter()
932                        .map(|(name, value)| ContextItem { name, value })
933                        .collect(),
934                };
935                Ok(ProtobufEvent {
936                    name: ProtobufEventType::EditPaneExited as i32,
937                    payload: Some(event::Payload::EditPaneExitedPayload(
938                        command_pane_exited_payload,
939                    )),
940                })
941            },
942            Event::CommandPaneReRun(terminal_pane_id, context) => {
943                let command_pane_rerun_payload = CommandPaneReRunPayload {
944                    terminal_pane_id,
945                    context: context
946                        .into_iter()
947                        .map(|(name, value)| ContextItem { name, value })
948                        .collect(),
949                };
950                Ok(ProtobufEvent {
951                    name: ProtobufEventType::CommandPaneReRun as i32,
952                    payload: Some(event::Payload::CommandPaneRerunPayload(
953                        command_pane_rerun_payload,
954                    )),
955                })
956            },
957            Event::FailedToWriteConfigToDisk(file_path) => Ok(ProtobufEvent {
958                name: ProtobufEventType::FailedToWriteConfigToDisk as i32,
959                payload: Some(event::Payload::FailedToWriteConfigToDiskPayload(
960                    FailedToWriteConfigToDiskPayload { file_path },
961                )),
962            }),
963            Event::ListClients(mut client_info_list) => Ok(ProtobufEvent {
964                name: ProtobufEventType::ListClients as i32,
965                payload: Some(event::Payload::ListClientsPayload(ListClientsPayload {
966                    client_info: client_info_list
967                        .drain(..)
968                        .filter_map(|c| c.try_into().ok())
969                        .collect(),
970                })),
971            }),
972            Event::HostFolderChanged(new_host_folder_path) => Ok(ProtobufEvent {
973                name: ProtobufEventType::HostFolderChanged as i32,
974                payload: Some(event::Payload::HostFolderChangedPayload(
975                    HostFolderChangedPayload {
976                        new_host_folder_path: new_host_folder_path.display().to_string(),
977                    },
978                )),
979            }),
980            Event::FailedToChangeHostFolder(error_message) => Ok(ProtobufEvent {
981                name: ProtobufEventType::FailedToChangeHostFolder as i32,
982                payload: Some(event::Payload::FailedToChangeHostFolderPayload(
983                    FailedToChangeHostFolderPayload { error_message },
984                )),
985            }),
986            Event::PastedText(pasted_text) => Ok(ProtobufEvent {
987                name: ProtobufEventType::PastedText as i32,
988                payload: Some(event::Payload::PastedTextPayload(PastedTextPayload {
989                    pasted_text,
990                })),
991            }),
992            Event::ConfigWasWrittenToDisk => Ok(ProtobufEvent {
993                name: ProtobufEventType::ConfigWasWrittenToDisk as i32,
994                payload: None,
995            }),
996            Event::WebServerStatus(web_server_status) => Ok(ProtobufEvent {
997                name: ProtobufEventType::WebServerStatus as i32,
998                payload: Some(event::Payload::WebServerStatusPayload(
999                    ProtobufWebServerStatusPayload::try_from(web_server_status)?,
1000                )),
1001            }),
1002            Event::BeforeClose => Ok(ProtobufEvent {
1003                name: ProtobufEventType::BeforeClose as i32,
1004                payload: None,
1005            }),
1006            Event::FailedToStartWebServer(error) => Ok(ProtobufEvent {
1007                name: ProtobufEventType::FailedToStartWebServer as i32,
1008                payload: Some(event::Payload::FailedToStartWebServerPayload(
1009                    FailedToStartWebServerPayload { error },
1010                )),
1011            }),
1012            Event::InterceptedKeyPress(key) => Ok(ProtobufEvent {
1013                name: ProtobufEventType::InterceptedKeyPress as i32,
1014                payload: Some(event::Payload::KeyPayload(key.try_into()?)),
1015            }),
1016            Event::PaneRenderReport(pane_contents_map) => Ok(ProtobufEvent {
1017                name: ProtobufEventType::PaneRenderReport as i32,
1018                payload: Some(event::Payload::PaneRenderReportPayload(
1019                    pane_contents_map.try_into()?,
1020                )),
1021            }),
1022            Event::UserAction(action, client_id, terminal_id, cli_client_id) => {
1023                let protobuf_action: ProtobufAction = action
1024                    .try_into()
1025                    .map_err(|_| "Failed to convert Action to protobuf")?;
1026                let protobuf_payload = ProtobufUserActionPayload {
1027                    action: Some(protobuf_action),
1028                    client_id: client_id as u32,
1029                    terminal_id,
1030                    cli_client_id: cli_client_id.map(|id| id as u32),
1031                };
1032                Ok(ProtobufEvent {
1033                    name: ProtobufEventType::UserAction as i32,
1034                    payload: Some(event::Payload::UserActionPayload(protobuf_payload)),
1035                })
1036            },
1037            Event::ActionComplete(action, pane_id, context) => {
1038                let protobuf_action = action.try_into()?;
1039                let protobuf_pane_id = pane_id.map(|id| id.try_into()).transpose()?;
1040                let context_items: Vec<ProtobufContextItem> = context
1041                    .into_iter()
1042                    .map(|(name, value)| ProtobufContextItem { name, value })
1043                    .collect();
1044                let action_complete_payload = ProtobufActionCompletePayload {
1045                    action: Some(protobuf_action),
1046                    pane_id: protobuf_pane_id,
1047                    context: context_items,
1048                };
1049                Ok(ProtobufEvent {
1050                    name: ProtobufEventType::ActionComplete as i32,
1051                    payload: Some(event::Payload::ActionCompletePayload(
1052                        action_complete_payload,
1053                    )),
1054                })
1055            },
1056            Event::CwdChanged(pane_id, new_cwd, focused_client_ids) => {
1057                let protobuf_pane_id: ProtobufPaneId = pane_id.try_into()?;
1058                let new_cwd_string = new_cwd
1059                    .to_str()
1060                    .ok_or("Failed to convert PathBuf to string")?
1061                    .to_string();
1062                let focused_client_ids_u32: Vec<u32> =
1063                    focused_client_ids.into_iter().map(|id| id as u32).collect();
1064                let cwd_changed_payload = ProtobufCwdChangedPayload {
1065                    pane_id: Some(protobuf_pane_id),
1066                    new_cwd: new_cwd_string,
1067                    focused_client_ids: focused_client_ids_u32,
1068                };
1069                Ok(ProtobufEvent {
1070                    name: ProtobufEventType::CwdChanged as i32,
1071                    payload: Some(event::Payload::CwdChangedPayload(cwd_changed_payload)),
1072                })
1073            },
1074            Event::CommandChanged(pane_id, command, is_foreground, focused_client_ids) => {
1075                let protobuf_pane_id: ProtobufPaneId = pane_id.try_into()?;
1076                let focused_client_ids_u32: Vec<u32> =
1077                    focused_client_ids.into_iter().map(|id| id as u32).collect();
1078                let payload = ProtobufCommandChangedPayload {
1079                    pane_id: Some(protobuf_pane_id),
1080                    command,
1081                    is_foreground,
1082                    focused_client_ids: focused_client_ids_u32,
1083                };
1084                Ok(ProtobufEvent {
1085                    name: ProtobufEventType::CommandChanged as i32,
1086                    payload: Some(event::Payload::CommandChangedPayload(payload)),
1087                })
1088            },
1089            Event::AvailableLayoutInfo(available_layouts, layouts_with_errors) => {
1090                let mut protobuf_available_layouts = vec![];
1091                let mut protobuf_layouts_with_errors = vec![];
1092
1093                for layout_info in available_layouts {
1094                    protobuf_available_layouts.push(layout_info.try_into()?);
1095                }
1096
1097                for layout_error in layouts_with_errors {
1098                    protobuf_layouts_with_errors.push(layout_error.try_into()?);
1099                }
1100
1101                let available_layout_info_payload = ProtobufAvailableLayoutInfoPayload {
1102                    available_layouts: protobuf_available_layouts,
1103                    layouts_with_errors: protobuf_layouts_with_errors,
1104                };
1105
1106                Ok(ProtobufEvent {
1107                    name: ProtobufEventType::AvailableLayoutInfo as i32,
1108                    payload: Some(event::Payload::AvailableLayoutInfoPayload(
1109                        available_layout_info_payload,
1110                    )),
1111                })
1112            },
1113            Event::PluginConfigurationChanged(configuration) => {
1114                let configuration_items: Vec<ProtobufContextItem> = configuration
1115                    .into_iter()
1116                    .map(|(name, value)| ProtobufContextItem { name, value })
1117                    .collect();
1118
1119                let payload = ProtobufPluginConfigurationChangedPayload {
1120                    configuration: configuration_items,
1121                };
1122
1123                Ok(ProtobufEvent {
1124                    name: ProtobufEventType::PluginConfigurationChanged as i32,
1125                    payload: Some(event::Payload::PluginConfigurationChangedPayload(payload)),
1126                })
1127            },
1128            Event::HighlightClicked {
1129                pane_id,
1130                pattern,
1131                matched_string,
1132                context,
1133            } => Ok(ProtobufEvent {
1134                name: ProtobufEventType::HighlightClicked as i32,
1135                payload: Some(event::Payload::HighlightClickedPayload(
1136                    HighlightClickedPayload {
1137                        pane_id: pane_id.try_into().ok(),
1138                        pattern,
1139                        matched_string,
1140                        context: context
1141                            .into_iter()
1142                            .map(|(name, value)| ProtobufContextItem { name, value })
1143                            .collect(),
1144                    },
1145                )),
1146            }),
1147            Event::HostTerminalThemeChanged(mode) => {
1148                let proto_mode: ProtobufHostTerminalThemeIndication = mode.into();
1149                let payload = ProtobufHostTerminalThemeChangedPayload {
1150                    mode: proto_mode as i32,
1151                };
1152                Ok(ProtobufEvent {
1153                    name: ProtobufEventType::HostTerminalThemeChanged as i32,
1154                    payload: Some(event::Payload::HostTerminalThemeChangedPayload(payload)),
1155                })
1156            },
1157            Event::SoftKeyboardVisibilityChanged(visible) => {
1158                let payload = ProtobufSoftKeyboardVisibilityChangedPayload { visible };
1159                Ok(ProtobufEvent {
1160                    name: ProtobufEventType::SoftKeyboardVisibilityChanged as i32,
1161                    payload: Some(event::Payload::SoftKeyboardVisibilityChangedPayload(
1162                        payload,
1163                    )),
1164                })
1165            },
1166            Event::HintText(hint_text) => {
1167                let mut proto_hint_text = HashMap::new();
1168                for (width, styled_text) in hint_text {
1169                    proto_hint_text.insert(width as u32, ProtobufStyledText::from(styled_text));
1170                }
1171                let payload = ProtobufHintTextPayload {
1172                    hint_text: proto_hint_text,
1173                };
1174                Ok(ProtobufEvent {
1175                    name: ProtobufEventType::HintText as i32,
1176                    payload: Some(event::Payload::HintTextPayload(payload)),
1177                })
1178            },
1179            Event::ActivePaneScroll(scroll) => {
1180                let payload = match scroll {
1181                    Some((position, length)) => ProtobufActivePaneScrollPayload {
1182                        position: Some(position as u32),
1183                        length: Some(length as u32),
1184                    },
1185                    None => ProtobufActivePaneScrollPayload {
1186                        position: None,
1187                        length: None,
1188                    },
1189                };
1190                Ok(ProtobufEvent {
1191                    name: ProtobufEventType::ActivePaneScroll as i32,
1192                    payload: Some(event::Payload::ActivePaneScrollPayload(payload)),
1193                })
1194            },
1195            Event::InitialKeybinds(keybinds) => {
1196                let mut protobuf_keybinds: Vec<ProtobufInputModeKeybinds> = vec![];
1197                for (input_mode, input_mode_keybinds) in keybinds {
1198                    let mode: ProtobufInputMode = input_mode.try_into()?;
1199                    let mut key_binds: Vec<ProtobufKeyBind> = vec![];
1200                    for (key, actions) in input_mode_keybinds {
1201                        let protobuf_key: ProtobufKey = key.try_into()?;
1202                        let mut protobuf_actions: Vec<ProtobufAction> = vec![];
1203                        for action in actions {
1204                            if let Ok(protobuf_action) = action.try_into() {
1205                                protobuf_actions.push(protobuf_action);
1206                            }
1207                        }
1208                        key_binds.push(ProtobufKeyBind {
1209                            key: Some(protobuf_key),
1210                            action: protobuf_actions,
1211                        });
1212                    }
1213                    protobuf_keybinds.push(ProtobufInputModeKeybinds {
1214                        mode: mode as i32,
1215                        key_bind: key_binds,
1216                    });
1217                }
1218                Ok(ProtobufEvent {
1219                    name: ProtobufEventType::InitialKeybinds as i32,
1220                    payload: Some(event::Payload::InitialKeybindsPayload(
1221                        InitialKeybindsPayload {
1222                            keybinds: protobuf_keybinds,
1223                        },
1224                    )),
1225                })
1226            },
1227        }
1228    }
1229}
1230
1231impl TryFrom<SessionInfo> for ProtobufSessionManifest {
1232    type Error = &'static str;
1233    fn try_from(session_info: SessionInfo) -> Result<Self, &'static str> {
1234        let mut protobuf_pane_manifests = vec![];
1235        for (tab_index, pane_infos) in session_info.panes.panes {
1236            let mut protobuf_pane_infos = vec![];
1237            for pane_info in pane_infos {
1238                protobuf_pane_infos.push(pane_info.try_into()?);
1239            }
1240            protobuf_pane_manifests.push(ProtobufPaneManifest {
1241                tab_index: tab_index as u32,
1242                panes: protobuf_pane_infos,
1243            });
1244        }
1245        Ok(ProtobufSessionManifest {
1246            name: session_info.name,
1247            panes: protobuf_pane_manifests,
1248            tabs: session_info
1249                .tabs
1250                .iter()
1251                .filter_map(|t| t.clone().try_into().ok())
1252                .collect(),
1253            connected_clients: session_info.connected_clients as u32,
1254            is_current_session: session_info.is_current_session,
1255            available_layouts: session_info
1256                .available_layouts
1257                .into_iter()
1258                .filter_map(|l| ProtobufLayoutInfo::try_from(l).ok())
1259                .collect(),
1260            plugins: session_info
1261                .plugins
1262                .into_iter()
1263                .map(|p| ProtobufPluginInfo::from(p))
1264                .collect(),
1265            web_clients_allowed: session_info.web_clients_allowed,
1266            web_client_count: session_info.web_client_count as u32,
1267            tab_history: session_info
1268                .tab_history
1269                .into_iter()
1270                .map(|t| ProtobufClientTabHistory::from(t))
1271                .collect(),
1272            pane_history: session_info
1273                .pane_history
1274                .into_iter()
1275                .map(|p| ProtobufClientPaneHistory::from(p))
1276                .collect(),
1277            creation_time: session_info.creation_time.as_secs(),
1278        })
1279    }
1280}
1281
1282impl From<(u16, Vec<usize>)> for ProtobufClientTabHistory {
1283    fn from((client_id, tab_history): (u16, Vec<usize>)) -> ProtobufClientTabHistory {
1284        ProtobufClientTabHistory {
1285            client_id: client_id as u32,
1286            tab_history: tab_history.into_iter().map(|t| t as u32).collect(),
1287        }
1288    }
1289}
1290
1291impl From<(u16, Vec<PaneId>)> for ProtobufClientPaneHistory {
1292    fn from((client_id, pane_history): (u16, Vec<PaneId>)) -> ProtobufClientPaneHistory {
1293        ProtobufClientPaneHistory {
1294            client_id: client_id as u32,
1295            pane_history: pane_history
1296                .into_iter()
1297                .filter_map(|p| p.try_into().ok())
1298                .collect(),
1299        }
1300    }
1301}
1302impl From<(u32, PluginInfo)> for ProtobufPluginInfo {
1303    fn from((plugin_id, plugin_info): (u32, PluginInfo)) -> ProtobufPluginInfo {
1304        ProtobufPluginInfo {
1305            plugin_id,
1306            plugin_url: plugin_info.location,
1307            plugin_config: plugin_info
1308                .configuration
1309                .into_iter()
1310                .map(|(name, value)| ContextItem { name, value })
1311                .collect(),
1312        }
1313    }
1314}
1315
1316impl TryFrom<ProtobufSessionManifest> for SessionInfo {
1317    type Error = &'static str;
1318    fn try_from(protobuf_session_manifest: ProtobufSessionManifest) -> Result<Self, &'static str> {
1319        let mut pane_manifest: HashMap<usize, Vec<PaneInfo>> = HashMap::new();
1320        for protobuf_pane_manifest in protobuf_session_manifest.panes {
1321            let tab_index = protobuf_pane_manifest.tab_index as usize;
1322            let mut panes = vec![];
1323            for protobuf_pane_info in protobuf_pane_manifest.panes {
1324                panes.push(protobuf_pane_info.try_into()?);
1325            }
1326            if pane_manifest.contains_key(&tab_index) {
1327                return Err("Duplicate tab definition in pane manifest");
1328            }
1329            pane_manifest.insert(tab_index, panes);
1330        }
1331        let panes = PaneManifest {
1332            panes: pane_manifest,
1333        };
1334        let mut plugins = BTreeMap::new();
1335        for plugin_info in protobuf_session_manifest.plugins.into_iter() {
1336            let mut configuration = BTreeMap::new();
1337            for context_item in plugin_info.plugin_config.into_iter() {
1338                configuration.insert(context_item.name, context_item.value);
1339            }
1340            plugins.insert(
1341                plugin_info.plugin_id,
1342                PluginInfo {
1343                    location: plugin_info.plugin_url,
1344                    configuration,
1345                },
1346            );
1347        }
1348        let mut tab_history = BTreeMap::new();
1349        for client_tab_history in protobuf_session_manifest.tab_history.into_iter() {
1350            let client_id = client_tab_history.client_id;
1351            let tab_history_for_client = client_tab_history
1352                .tab_history
1353                .iter()
1354                .map(|t| *t as usize)
1355                .collect();
1356            tab_history.insert(client_id as u16, tab_history_for_client);
1357        }
1358        let mut pane_history = BTreeMap::new();
1359        for client_pane_history in protobuf_session_manifest.pane_history.into_iter() {
1360            let client_id = client_pane_history.client_id;
1361            let pane_history_for_client = client_pane_history
1362                .pane_history
1363                .into_iter()
1364                .filter_map(|p| p.try_into().ok())
1365                .collect();
1366            pane_history.insert(client_id as u16, pane_history_for_client);
1367        }
1368        Ok(SessionInfo {
1369            name: protobuf_session_manifest.name,
1370            tabs: protobuf_session_manifest
1371                .tabs
1372                .iter()
1373                .filter_map(|t| t.clone().try_into().ok())
1374                .collect(),
1375            panes,
1376            connected_clients: protobuf_session_manifest.connected_clients as usize,
1377            is_current_session: protobuf_session_manifest.is_current_session,
1378            available_layouts: protobuf_session_manifest
1379                .available_layouts
1380                .into_iter()
1381                .filter_map(|l| LayoutInfo::try_from(l).ok())
1382                .collect(),
1383            plugins,
1384            web_clients_allowed: protobuf_session_manifest.web_clients_allowed,
1385            web_client_count: protobuf_session_manifest.web_client_count as usize,
1386            tab_history,
1387            pane_history,
1388            creation_time: Duration::from_secs(protobuf_session_manifest.creation_time),
1389        })
1390    }
1391}
1392
1393impl TryFrom<LayoutInfo> for ProtobufLayoutInfo {
1394    type Error = &'static str;
1395    fn try_from(layout_info: LayoutInfo) -> Result<Self, &'static str> {
1396        match layout_info {
1397            LayoutInfo::File(name, layout_metadata) => Ok(ProtobufLayoutInfo {
1398                source: "file".to_owned(),
1399                name,
1400                layout_metadata: Some(layout_metadata.try_into()?),
1401            }),
1402            LayoutInfo::BuiltIn(name) => Ok(ProtobufLayoutInfo {
1403                source: "built-in".to_owned(),
1404                name,
1405                layout_metadata: None,
1406            }),
1407            LayoutInfo::Url(name) => Ok(ProtobufLayoutInfo {
1408                source: "url".to_owned(),
1409                name,
1410                layout_metadata: None,
1411            }),
1412            LayoutInfo::Stringified(stringified_layout) => Ok(ProtobufLayoutInfo {
1413                source: "stringified".to_owned(),
1414                name: stringified_layout.clone(),
1415                layout_metadata: None,
1416            }),
1417        }
1418    }
1419}
1420
1421impl TryFrom<ProtobufLayoutInfo> for LayoutInfo {
1422    type Error = &'static str;
1423    fn try_from(protobuf_layout_info: ProtobufLayoutInfo) -> Result<Self, &'static str> {
1424        match protobuf_layout_info.source.as_str() {
1425            "file" => {
1426                let layout_metadata = protobuf_layout_info
1427                    .layout_metadata
1428                    .map(|m| m.try_into())
1429                    .transpose()?
1430                    .unwrap_or_default();
1431                Ok(LayoutInfo::File(protobuf_layout_info.name, layout_metadata))
1432            },
1433            "built-in" => Ok(LayoutInfo::BuiltIn(protobuf_layout_info.name)),
1434            "url" => Ok(LayoutInfo::Url(protobuf_layout_info.name)),
1435            "stringified" => Ok(LayoutInfo::Stringified(protobuf_layout_info.name)),
1436            _ => Err("Unknown source for layout"),
1437        }
1438    }
1439}
1440
1441impl TryFrom<ProtobufLayoutMetadata> for LayoutMetadata {
1442    type Error = &'static str;
1443    fn try_from(protobuf_metadata: ProtobufLayoutMetadata) -> Result<Self, &'static str> {
1444        let tabs = protobuf_metadata
1445            .tabs
1446            .into_iter()
1447            .map(|t| t.try_into())
1448            .collect::<Result<Vec<_>, _>>()?;
1449        Ok(LayoutMetadata {
1450            tabs,
1451            creation_time: protobuf_metadata.creation_time,
1452            update_time: protobuf_metadata.update_time,
1453        })
1454    }
1455}
1456
1457impl TryFrom<LayoutMetadata> for ProtobufLayoutMetadata {
1458    type Error = &'static str;
1459    fn try_from(metadata: LayoutMetadata) -> Result<Self, &'static str> {
1460        let tabs = metadata
1461            .tabs
1462            .into_iter()
1463            .map(|t| t.try_into())
1464            .collect::<Result<Vec<_>, _>>()?;
1465        Ok(ProtobufLayoutMetadata {
1466            tabs,
1467            creation_time: metadata.creation_time,
1468            update_time: metadata.update_time,
1469        })
1470    }
1471}
1472
1473impl TryFrom<ProtobufTabMetadata> for TabMetadata {
1474    type Error = &'static str;
1475    fn try_from(protobuf_metadata: ProtobufTabMetadata) -> Result<Self, &'static str> {
1476        let panes = protobuf_metadata
1477            .pane_metadata
1478            .into_iter()
1479            .map(|p| p.try_into())
1480            .collect::<Result<Vec<_>, _>>()?;
1481        Ok(TabMetadata {
1482            panes,
1483            name: protobuf_metadata.name,
1484        })
1485    }
1486}
1487
1488impl TryFrom<TabMetadata> for ProtobufTabMetadata {
1489    type Error = &'static str;
1490    fn try_from(metadata: TabMetadata) -> Result<Self, &'static str> {
1491        let pane_metadata = metadata
1492            .panes
1493            .into_iter()
1494            .map(|p| p.try_into())
1495            .collect::<Result<Vec<_>, _>>()?;
1496        Ok(ProtobufTabMetadata {
1497            pane_metadata,
1498            name: metadata.name,
1499        })
1500    }
1501}
1502
1503impl TryFrom<ProtobufPaneMetadata> for PaneMetadata {
1504    type Error = &'static str;
1505    fn try_from(protobuf_metadata: ProtobufPaneMetadata) -> Result<Self, &'static str> {
1506        Ok(PaneMetadata {
1507            name: protobuf_metadata.name,
1508            is_plugin: protobuf_metadata.is_plugin,
1509            is_builtin_plugin: protobuf_metadata.is_builtin_plugin,
1510        })
1511    }
1512}
1513
1514impl TryFrom<PaneMetadata> for ProtobufPaneMetadata {
1515    type Error = &'static str;
1516    fn try_from(metadata: PaneMetadata) -> Result<Self, &'static str> {
1517        Ok(ProtobufPaneMetadata {
1518            name: metadata.name,
1519            is_plugin: metadata.is_plugin,
1520            is_builtin_plugin: metadata.is_builtin_plugin,
1521        })
1522    }
1523}
1524
1525// LayoutWithError conversions
1526impl TryFrom<ProtobufLayoutWithError> for crate::data::LayoutWithError {
1527    type Error = &'static str;
1528    fn try_from(protobuf: ProtobufLayoutWithError) -> Result<Self, Self::Error> {
1529        Ok(crate::data::LayoutWithError {
1530            layout_name: protobuf.layout_name,
1531            error: protobuf.error.ok_or("Missing error field")?.try_into()?,
1532        })
1533    }
1534}
1535
1536impl TryFrom<crate::data::LayoutWithError> for ProtobufLayoutWithError {
1537    type Error = &'static str;
1538    fn try_from(layout_error: crate::data::LayoutWithError) -> Result<Self, Self::Error> {
1539        Ok(ProtobufLayoutWithError {
1540            layout_name: layout_error.layout_name,
1541            error: Some(layout_error.error.try_into()?),
1542        })
1543    }
1544}
1545
1546// LayoutParsingError conversions
1547impl TryFrom<ProtobufLayoutParsingError> for crate::data::LayoutParsingError {
1548    type Error = &'static str;
1549    fn try_from(protobuf: ProtobufLayoutParsingError) -> Result<Self, Self::Error> {
1550        match protobuf.error_type.ok_or("Missing error_type")? {
1551            ProtobufLayoutParsingErrorType::KdlError(kdl_variant) => {
1552                Ok(crate::data::LayoutParsingError::KdlError {
1553                    kdl_error: kdl_variant
1554                        .kdl_error
1555                        .ok_or("Missing kdl_error")?
1556                        .try_into()?,
1557                    file_name: kdl_variant.file_name,
1558                    source_code: kdl_variant.source_code,
1559                })
1560            },
1561            ProtobufLayoutParsingErrorType::SyntaxError(_) => {
1562                Ok(crate::data::LayoutParsingError::SyntaxError)
1563            },
1564        }
1565    }
1566}
1567
1568impl TryFrom<crate::data::LayoutParsingError> for ProtobufLayoutParsingError {
1569    type Error = &'static str;
1570    fn try_from(error: crate::data::LayoutParsingError) -> Result<Self, Self::Error> {
1571        let error_type = match error {
1572            crate::data::LayoutParsingError::KdlError {
1573                kdl_error,
1574                file_name,
1575                source_code,
1576            } => ProtobufLayoutParsingErrorType::KdlError(ProtobufKdlErrorVariant {
1577                kdl_error: Some(kdl_error.try_into()?),
1578                file_name,
1579                source_code,
1580            }),
1581            crate::data::LayoutParsingError::SyntaxError => {
1582                ProtobufLayoutParsingErrorType::SyntaxError(ProtobufSyntaxError {})
1583            },
1584        };
1585        Ok(ProtobufLayoutParsingError {
1586            error_type: Some(error_type),
1587        })
1588    }
1589}
1590
1591// KdlError conversions
1592impl TryFrom<ProtobufKdlError> for crate::input::config::KdlError {
1593    type Error = &'static str;
1594    fn try_from(protobuf: ProtobufKdlError) -> Result<Self, Self::Error> {
1595        Ok(crate::input::config::KdlError {
1596            error_message: protobuf.error_message,
1597            src: None, // We don't serialize NamedSource
1598            offset: protobuf.offset.map(|o| o as usize),
1599            len: protobuf.len.map(|l| l as usize),
1600            help_message: protobuf.help_message,
1601        })
1602    }
1603}
1604
1605impl TryFrom<crate::input::config::KdlError> for ProtobufKdlError {
1606    type Error = &'static str;
1607    fn try_from(kdl: crate::input::config::KdlError) -> Result<Self, Self::Error> {
1608        Ok(ProtobufKdlError {
1609            error_message: kdl.error_message,
1610            // src is not serialized
1611            offset: kdl.offset.map(|o| o as u64),
1612            len: kdl.len.map(|l| l as u64),
1613            help_message: kdl.help_message,
1614        })
1615    }
1616}
1617
1618impl TryFrom<CopyDestination> for ProtobufCopyDestination {
1619    type Error = &'static str;
1620    fn try_from(copy_destination: CopyDestination) -> Result<Self, &'static str> {
1621        match copy_destination {
1622            CopyDestination::Command => Ok(ProtobufCopyDestination::Command),
1623            CopyDestination::Primary => Ok(ProtobufCopyDestination::Primary),
1624            CopyDestination::System => Ok(ProtobufCopyDestination::System),
1625        }
1626    }
1627}
1628
1629impl TryFrom<ProtobufCopyDestination> for CopyDestination {
1630    type Error = &'static str;
1631    fn try_from(protobuf_copy_destination: ProtobufCopyDestination) -> Result<Self, &'static str> {
1632        match protobuf_copy_destination {
1633            ProtobufCopyDestination::Command => Ok(CopyDestination::Command),
1634            ProtobufCopyDestination::Primary => Ok(CopyDestination::Primary),
1635            ProtobufCopyDestination::System => Ok(CopyDestination::System),
1636        }
1637    }
1638}
1639
1640impl TryFrom<MouseEventPayload> for Mouse {
1641    type Error = &'static str;
1642    fn try_from(mouse_event_payload: MouseEventPayload) -> Result<Self, &'static str> {
1643        match MouseEventName::try_from(mouse_event_payload.mouse_event_name).ok() {
1644            Some(MouseEventName::MouseScrollUp) => match mouse_event_payload.mouse_event_payload {
1645                Some(mouse_event_payload::MouseEventPayload::LineCount(line_count)) => {
1646                    Ok(Mouse::ScrollUp(line_count as usize))
1647                },
1648                _ => Err("Malformed payload for mouse scroll up"),
1649            },
1650            Some(MouseEventName::MouseScrollDown) => {
1651                match mouse_event_payload.mouse_event_payload {
1652                    Some(mouse_event_payload::MouseEventPayload::LineCount(line_count)) => {
1653                        Ok(Mouse::ScrollDown(line_count as usize))
1654                    },
1655                    _ => Err("Malformed payload for mouse scroll down"),
1656                }
1657            },
1658            Some(MouseEventName::MouseLeftClick) => match mouse_event_payload.mouse_event_payload {
1659                Some(mouse_event_payload::MouseEventPayload::Position(position)) => Ok(
1660                    Mouse::LeftClick(position.line as isize, position.column as usize),
1661                ),
1662                _ => Err("Malformed payload for mouse left click"),
1663            },
1664            Some(MouseEventName::MouseRightClick) => {
1665                match mouse_event_payload.mouse_event_payload {
1666                    Some(mouse_event_payload::MouseEventPayload::Position(position)) => Ok(
1667                        Mouse::RightClick(position.line as isize, position.column as usize),
1668                    ),
1669                    _ => Err("Malformed payload for mouse right click"),
1670                }
1671            },
1672            Some(MouseEventName::MouseHold) => match mouse_event_payload.mouse_event_payload {
1673                Some(mouse_event_payload::MouseEventPayload::Position(position)) => Ok(
1674                    Mouse::Hold(position.line as isize, position.column as usize),
1675                ),
1676                _ => Err("Malformed payload for mouse hold"),
1677            },
1678            Some(MouseEventName::MouseRelease) => match mouse_event_payload.mouse_event_payload {
1679                Some(mouse_event_payload::MouseEventPayload::Position(position)) => Ok(
1680                    Mouse::Release(position.line as isize, position.column as usize),
1681                ),
1682                _ => Err("Malformed payload for mouse release"),
1683            },
1684            Some(MouseEventName::MouseHover) => match mouse_event_payload.mouse_event_payload {
1685                Some(mouse_event_payload::MouseEventPayload::Position(position)) => Ok(
1686                    Mouse::Hover(position.line as isize, position.column as usize),
1687                ),
1688                _ => Err("Malformed payload for mouse hover"),
1689            },
1690            Some(MouseEventName::MouseScrollLeft) => {
1691                match mouse_event_payload.mouse_event_payload {
1692                    Some(mouse_event_payload::MouseEventPayload::LineCount(line_count)) => {
1693                        Ok(Mouse::ScrollLeft(line_count as usize))
1694                    },
1695                    _ => Err("Malformed payload for mouse scroll left"),
1696                }
1697            },
1698            Some(MouseEventName::MouseScrollRight) => {
1699                match mouse_event_payload.mouse_event_payload {
1700                    Some(mouse_event_payload::MouseEventPayload::LineCount(line_count)) => {
1701                        Ok(Mouse::ScrollRight(line_count as usize))
1702                    },
1703                    _ => Err("Malformed payload for mouse scroll right"),
1704                }
1705            },
1706            None => Err("Malformed payload for MouseEventName"),
1707        }
1708    }
1709}
1710
1711impl TryFrom<Mouse> for MouseEventPayload {
1712    type Error = &'static str;
1713    fn try_from(mouse: Mouse) -> Result<Self, &'static str> {
1714        match mouse {
1715            Mouse::ScrollUp(number_of_lines) => Ok(MouseEventPayload {
1716                mouse_event_name: MouseEventName::MouseScrollUp as i32,
1717                mouse_event_payload: Some(mouse_event_payload::MouseEventPayload::LineCount(
1718                    number_of_lines as u32,
1719                )),
1720            }),
1721            Mouse::ScrollDown(number_of_lines) => Ok(MouseEventPayload {
1722                mouse_event_name: MouseEventName::MouseScrollDown as i32,
1723                mouse_event_payload: Some(mouse_event_payload::MouseEventPayload::LineCount(
1724                    number_of_lines as u32,
1725                )),
1726            }),
1727            Mouse::LeftClick(line, column) => Ok(MouseEventPayload {
1728                mouse_event_name: MouseEventName::MouseLeftClick as i32,
1729                mouse_event_payload: Some(mouse_event_payload::MouseEventPayload::Position(
1730                    ProtobufPosition {
1731                        line: line as i64,
1732                        column: column as i64,
1733                    },
1734                )),
1735            }),
1736            Mouse::RightClick(line, column) => Ok(MouseEventPayload {
1737                mouse_event_name: MouseEventName::MouseRightClick as i32,
1738                mouse_event_payload: Some(mouse_event_payload::MouseEventPayload::Position(
1739                    ProtobufPosition {
1740                        line: line as i64,
1741                        column: column as i64,
1742                    },
1743                )),
1744            }),
1745            Mouse::Hold(line, column) => Ok(MouseEventPayload {
1746                mouse_event_name: MouseEventName::MouseHold as i32,
1747                mouse_event_payload: Some(mouse_event_payload::MouseEventPayload::Position(
1748                    ProtobufPosition {
1749                        line: line as i64,
1750                        column: column as i64,
1751                    },
1752                )),
1753            }),
1754            Mouse::Release(line, column) => Ok(MouseEventPayload {
1755                mouse_event_name: MouseEventName::MouseRelease as i32,
1756                mouse_event_payload: Some(mouse_event_payload::MouseEventPayload::Position(
1757                    ProtobufPosition {
1758                        line: line as i64,
1759                        column: column as i64,
1760                    },
1761                )),
1762            }),
1763            Mouse::Hover(line, column) => Ok(MouseEventPayload {
1764                mouse_event_name: MouseEventName::MouseHover as i32,
1765                mouse_event_payload: Some(mouse_event_payload::MouseEventPayload::Position(
1766                    ProtobufPosition {
1767                        line: line as i64,
1768                        column: column as i64,
1769                    },
1770                )),
1771            }),
1772            Mouse::ScrollLeft(cols) => Ok(MouseEventPayload {
1773                mouse_event_name: MouseEventName::MouseScrollLeft as i32,
1774                mouse_event_payload: Some(mouse_event_payload::MouseEventPayload::LineCount(
1775                    cols as u32,
1776                )),
1777            }),
1778            Mouse::ScrollRight(cols) => Ok(MouseEventPayload {
1779                mouse_event_name: MouseEventName::MouseScrollRight as i32,
1780                mouse_event_payload: Some(mouse_event_payload::MouseEventPayload::LineCount(
1781                    cols as u32,
1782                )),
1783            }),
1784        }
1785    }
1786}
1787
1788impl TryFrom<ProtobufPaneInfo> for PaneInfo {
1789    type Error = &'static str;
1790    fn try_from(protobuf_pane_info: ProtobufPaneInfo) -> Result<Self, &'static str> {
1791        Ok(PaneInfo {
1792            id: protobuf_pane_info.id,
1793            is_plugin: protobuf_pane_info.is_plugin,
1794            is_focused: protobuf_pane_info.is_focused,
1795            is_fullscreen: protobuf_pane_info.is_fullscreen,
1796            is_floating: protobuf_pane_info.is_floating,
1797            is_suppressed: protobuf_pane_info.is_suppressed,
1798            title: protobuf_pane_info.title,
1799            exited: protobuf_pane_info.exited,
1800            exit_status: protobuf_pane_info.exit_status,
1801            is_held: protobuf_pane_info.is_held,
1802            pane_x: protobuf_pane_info.pane_x as usize,
1803            pane_content_x: protobuf_pane_info.pane_content_x as usize,
1804            pane_y: protobuf_pane_info.pane_y as usize,
1805            pane_content_y: protobuf_pane_info.pane_content_y as usize,
1806            pane_rows: protobuf_pane_info.pane_rows as usize,
1807            pane_content_rows: protobuf_pane_info.pane_content_rows as usize,
1808            pane_columns: protobuf_pane_info.pane_columns as usize,
1809            pane_content_columns: protobuf_pane_info.pane_content_columns as usize,
1810            cursor_coordinates_in_pane: protobuf_pane_info
1811                .cursor_coordinates_in_pane
1812                .map(|position| (position.column as usize, position.line as usize)),
1813            terminal_command: protobuf_pane_info.terminal_command,
1814            plugin_url: protobuf_pane_info.plugin_url,
1815            is_selectable: protobuf_pane_info.is_selectable,
1816            index_in_pane_group: protobuf_pane_info
1817                .index_in_pane_group
1818                .iter()
1819                .map(|index_in_pane_group| {
1820                    (
1821                        index_in_pane_group.client_id as u16,
1822                        index_in_pane_group.index as usize,
1823                    )
1824                })
1825                .collect(),
1826            default_fg: protobuf_pane_info.default_fg,
1827            default_bg: protobuf_pane_info.default_bg,
1828        })
1829    }
1830}
1831
1832impl TryFrom<PaneInfo> for ProtobufPaneInfo {
1833    type Error = &'static str;
1834    fn try_from(pane_info: PaneInfo) -> Result<Self, &'static str> {
1835        Ok(ProtobufPaneInfo {
1836            id: pane_info.id,
1837            is_plugin: pane_info.is_plugin,
1838            is_focused: pane_info.is_focused,
1839            is_fullscreen: pane_info.is_fullscreen,
1840            is_floating: pane_info.is_floating,
1841            is_suppressed: pane_info.is_suppressed,
1842            title: pane_info.title,
1843            exited: pane_info.exited,
1844            exit_status: pane_info.exit_status,
1845            is_held: pane_info.is_held,
1846            pane_x: pane_info.pane_x as u32,
1847            pane_content_x: pane_info.pane_content_x as u32,
1848            pane_y: pane_info.pane_y as u32,
1849            pane_content_y: pane_info.pane_content_y as u32,
1850            pane_rows: pane_info.pane_rows as u32,
1851            pane_content_rows: pane_info.pane_content_rows as u32,
1852            pane_columns: pane_info.pane_columns as u32,
1853            pane_content_columns: pane_info.pane_content_columns as u32,
1854            cursor_coordinates_in_pane: pane_info.cursor_coordinates_in_pane.map(|(x, y)| {
1855                ProtobufPosition {
1856                    column: x as i64,
1857                    line: y as i64,
1858                }
1859            }),
1860            terminal_command: pane_info.terminal_command,
1861            plugin_url: pane_info.plugin_url,
1862            is_selectable: pane_info.is_selectable,
1863            index_in_pane_group: pane_info
1864                .index_in_pane_group
1865                .iter()
1866                .map(|(&client_id, &index)| IndexInPaneGroup {
1867                    client_id: client_id as u32,
1868                    index: index as u32,
1869                })
1870                .collect(),
1871            default_fg: pane_info.default_fg,
1872            default_bg: pane_info.default_bg,
1873        })
1874    }
1875}
1876
1877impl TryFrom<ProtobufTabInfo> for TabInfo {
1878    type Error = &'static str;
1879    fn try_from(protobuf_tab_info: ProtobufTabInfo) -> Result<Self, &'static str> {
1880        Ok(TabInfo {
1881            position: protobuf_tab_info.position as usize,
1882            name: protobuf_tab_info.name,
1883            active: protobuf_tab_info.active,
1884            panes_to_hide: protobuf_tab_info.panes_to_hide as usize,
1885            is_fullscreen_active: protobuf_tab_info.is_fullscreen_active,
1886            is_sync_panes_active: protobuf_tab_info.is_sync_panes_active,
1887            are_floating_panes_visible: protobuf_tab_info.are_floating_panes_visible,
1888            other_focused_clients: protobuf_tab_info
1889                .other_focused_clients
1890                .iter()
1891                .map(|c| *c as u16)
1892                .collect(),
1893            active_swap_layout_name: protobuf_tab_info.active_swap_layout_name,
1894            is_swap_layout_dirty: protobuf_tab_info.is_swap_layout_dirty,
1895            viewport_rows: protobuf_tab_info.viewport_rows as usize,
1896            viewport_columns: protobuf_tab_info.viewport_columns as usize,
1897            display_area_rows: protobuf_tab_info.display_area_rows as usize,
1898            display_area_columns: protobuf_tab_info.display_area_columns as usize,
1899            selectable_tiled_panes_count: protobuf_tab_info.selectable_tiled_panes_count as usize,
1900            selectable_floating_panes_count: protobuf_tab_info.selectable_floating_panes_count
1901                as usize,
1902            tab_id: protobuf_tab_info.tab_id as usize,
1903            has_bell_notification: protobuf_tab_info.has_bell_notification,
1904            is_flashing_bell: protobuf_tab_info.is_flashing_bell,
1905        })
1906    }
1907}
1908
1909impl TryFrom<TabInfo> for ProtobufTabInfo {
1910    type Error = &'static str;
1911    fn try_from(tab_info: TabInfo) -> Result<Self, &'static str> {
1912        Ok(ProtobufTabInfo {
1913            position: tab_info.position as u32,
1914            name: tab_info.name,
1915            active: tab_info.active,
1916            panes_to_hide: tab_info.panes_to_hide as u32,
1917            is_fullscreen_active: tab_info.is_fullscreen_active,
1918            is_sync_panes_active: tab_info.is_sync_panes_active,
1919            are_floating_panes_visible: tab_info.are_floating_panes_visible,
1920            other_focused_clients: tab_info
1921                .other_focused_clients
1922                .iter()
1923                .map(|c| *c as u32)
1924                .collect(),
1925            active_swap_layout_name: tab_info.active_swap_layout_name,
1926            is_swap_layout_dirty: tab_info.is_swap_layout_dirty,
1927            viewport_rows: tab_info.viewport_rows as u32,
1928            viewport_columns: tab_info.viewport_columns as u32,
1929            display_area_rows: tab_info.display_area_rows as u32,
1930            display_area_columns: tab_info.display_area_columns as u32,
1931            selectable_tiled_panes_count: tab_info.selectable_tiled_panes_count as u32,
1932            selectable_floating_panes_count: tab_info.selectable_floating_panes_count as u32,
1933            tab_id: tab_info.tab_id as u32,
1934            has_bell_notification: tab_info.has_bell_notification,
1935            is_flashing_bell: tab_info.is_flashing_bell,
1936        })
1937    }
1938}
1939
1940impl TryFrom<ProtobufModeUpdatePayload> for ModeInfo {
1941    type Error = &'static str;
1942    fn try_from(
1943        mut protobuf_mode_update_payload: ProtobufModeUpdatePayload,
1944    ) -> Result<Self, &'static str> {
1945        let current_mode: InputMode =
1946            ProtobufInputMode::try_from(protobuf_mode_update_payload.current_mode)
1947                .ok()
1948                .ok_or("Malformed InputMode in the ModeUpdate Event")?
1949                .try_into()?;
1950        let base_mode: Option<InputMode> = protobuf_mode_update_payload
1951            .base_mode
1952            .and_then(|b_m| ProtobufInputMode::try_from(b_m).ok()?.try_into().ok());
1953        let keybinds: Vec<(InputMode, Vec<(KeyWithModifier, Vec<Action>)>)> =
1954            protobuf_mode_update_payload
1955                .keybinds
1956                .iter_mut()
1957                .filter_map(|k| {
1958                    let input_mode: InputMode = ProtobufInputMode::try_from(k.mode)
1959                        .ok()
1960                        .ok_or("Malformed InputMode in the ModeUpdate Event")
1961                        .ok()?
1962                        .try_into()
1963                        .ok()?;
1964                    let mut keybinds: Vec<(KeyWithModifier, Vec<Action>)> = vec![];
1965                    for mut protobuf_keybind in k.key_bind.drain(..) {
1966                        let key: KeyWithModifier = protobuf_keybind.key.unwrap().try_into().ok()?;
1967                        let mut actions: Vec<Action> = vec![];
1968                        for action in protobuf_keybind.action.drain(..) {
1969                            if let Ok(action) = action.try_into() {
1970                                actions.push(action);
1971                            }
1972                        }
1973                        keybinds.push((key, actions));
1974                    }
1975                    Some((input_mode, keybinds))
1976                })
1977                .collect();
1978        let style: Style = protobuf_mode_update_payload
1979            .style
1980            .and_then(|m| m.try_into().ok())
1981            .ok_or("malformed payload for mode_info")?;
1982        let session_name = protobuf_mode_update_payload.session_name;
1983        let editor = protobuf_mode_update_payload
1984            .editor
1985            .map(|e| PathBuf::from(e));
1986        let shell = protobuf_mode_update_payload.shell.map(|s| PathBuf::from(s));
1987        let web_clients_allowed = protobuf_mode_update_payload.web_clients_allowed;
1988        let web_sharing = protobuf_mode_update_payload
1989            .web_sharing
1990            .and_then(|w| ProtobufWebSharing::try_from(w).ok())
1991            .map(|w| w.into());
1992        let capabilities = PluginCapabilities {
1993            arrow_fonts: protobuf_mode_update_payload.arrow_fonts_support,
1994        };
1995        let currently_marking_pane_group =
1996            protobuf_mode_update_payload.currently_marking_pane_group;
1997        let is_web_client = protobuf_mode_update_payload.is_web_client;
1998
1999        let web_server_ip = protobuf_mode_update_payload
2000            .web_server_ip
2001            .as_ref()
2002            .and_then(|web_server_ip| IpAddr::from_str(web_server_ip).ok());
2003
2004        let web_server_port = protobuf_mode_update_payload
2005            .web_server_port
2006            .map(|w| w as u16);
2007
2008        let web_server_capability = protobuf_mode_update_payload.web_server_capability;
2009
2010        let pane_frame_style = protobuf_mode_update_payload
2011            .pane_frame_style
2012            .and_then(|p| ProtobufPaneFrameStyle::try_from(p).ok())
2013            .map(|p| p.into());
2014
2015        let session_dimmed = protobuf_mode_update_payload.session_dimmed;
2016
2017        let session_ancestry = protobuf_mode_update_payload.ancestry;
2018
2019        let host_fullscreen = protobuf_mode_update_payload.host_fullscreen;
2020
2021        let nested_ascend_keys = protobuf_mode_update_payload
2022            .nested_ascend_keys
2023            .iter()
2024            .filter_map(|key| KeyWithModifier::from_str(key).ok())
2025            .collect();
2026
2027        let session_ascended = protobuf_mode_update_payload.session_ascended;
2028
2029        let nested_descend_keys = protobuf_mode_update_payload
2030            .nested_descend_keys
2031            .iter()
2032            .filter_map(|key| KeyWithModifier::from_str(key).ok())
2033            .collect();
2034
2035        let mode_info = ModeInfo {
2036            mode: current_mode,
2037            keybinds,
2038            style,
2039            capabilities,
2040            session_name,
2041            base_mode,
2042            editor,
2043            shell,
2044            web_clients_allowed,
2045            web_sharing,
2046            currently_marking_pane_group,
2047            is_web_client,
2048            web_server_ip,
2049            web_server_port,
2050            web_server_capability,
2051            pane_frame_style,
2052            session_dimmed,
2053            session_ancestry,
2054            host_fullscreen,
2055            nested_ascend_keys,
2056            session_ascended,
2057            nested_descend_keys,
2058        };
2059        Ok(mode_info)
2060    }
2061}
2062
2063impl TryFrom<ModeInfo> for ProtobufModeUpdatePayload {
2064    type Error = &'static str;
2065    fn try_from(mode_info: ModeInfo) -> Result<Self, &'static str> {
2066        let current_mode: ProtobufInputMode = mode_info.mode.try_into()?;
2067        let base_mode: Option<ProtobufInputMode> = mode_info
2068            .base_mode
2069            .and_then(|mode| ProtobufInputMode::try_from(mode).ok());
2070        let style: ProtobufStyle = mode_info.style.try_into()?;
2071        let arrow_fonts_support: bool = mode_info.capabilities.arrow_fonts;
2072        let session_name = mode_info.session_name;
2073        let editor = mode_info.editor.map(|e| e.display().to_string());
2074        let shell = mode_info.shell.map(|s| s.display().to_string());
2075        let web_clients_allowed = mode_info.web_clients_allowed;
2076        let web_sharing = mode_info.web_sharing.map(|w| w as i32);
2077        let currently_marking_pane_group = mode_info.currently_marking_pane_group;
2078        let is_web_client = mode_info.is_web_client;
2079        let web_server_ip = mode_info.web_server_ip.map(|i| format!("{}", i));
2080        let web_server_port = mode_info.web_server_port.map(|p| p as u32);
2081        let web_server_capability = mode_info.web_server_capability;
2082        let pane_frame_style = mode_info.pane_frame_style.map(|p| {
2083            let protobuf_pane_frame_style: ProtobufPaneFrameStyle = p.into();
2084            protobuf_pane_frame_style as i32
2085        });
2086        let session_dimmed = mode_info.session_dimmed;
2087        let session_ancestry = mode_info.session_ancestry;
2088        let host_fullscreen = mode_info.host_fullscreen;
2089        let nested_ascend_keys = mode_info
2090            .nested_ascend_keys
2091            .iter()
2092            .map(|key| key.to_kdl())
2093            .collect();
2094        let session_ascended = mode_info.session_ascended;
2095        let nested_descend_keys = mode_info
2096            .nested_descend_keys
2097            .iter()
2098            .map(|key| key.to_kdl())
2099            .collect();
2100        let mut protobuf_input_mode_keybinds: Vec<ProtobufInputModeKeybinds> = vec![];
2101        for (input_mode, input_mode_keybinds) in mode_info.keybinds {
2102            let mode: ProtobufInputMode = input_mode.try_into()?;
2103            let mut keybinds: Vec<ProtobufKeyBind> = vec![];
2104            for (key, actions) in input_mode_keybinds {
2105                let protobuf_key: ProtobufKey = key.try_into()?;
2106                let mut protobuf_actions: Vec<ProtobufAction> = vec![];
2107                for action in actions {
2108                    if let Ok(protobuf_action) = action.try_into() {
2109                        protobuf_actions.push(protobuf_action);
2110                    }
2111                }
2112                let key_bind = ProtobufKeyBind {
2113                    key: Some(protobuf_key),
2114                    action: protobuf_actions,
2115                };
2116                keybinds.push(key_bind);
2117            }
2118            let input_mode_keybind = ProtobufInputModeKeybinds {
2119                mode: mode as i32,
2120                key_bind: keybinds,
2121            };
2122            protobuf_input_mode_keybinds.push(input_mode_keybind);
2123        }
2124        Ok(ProtobufModeUpdatePayload {
2125            current_mode: current_mode as i32,
2126            style: Some(style),
2127            keybinds: protobuf_input_mode_keybinds,
2128            arrow_fonts_support,
2129            session_name,
2130            base_mode: base_mode.map(|b_m| b_m as i32),
2131            editor,
2132            shell,
2133            web_clients_allowed,
2134            web_sharing,
2135            currently_marking_pane_group,
2136            is_web_client,
2137            web_server_ip,
2138            web_server_port,
2139            web_server_capability,
2140            pane_frame_style,
2141            session_dimmed,
2142            ancestry: session_ancestry,
2143            host_fullscreen,
2144            nested_ascend_keys,
2145            session_ascended,
2146            nested_descend_keys,
2147        })
2148    }
2149}
2150
2151impl TryFrom<ProtobufEventNameList> for HashSet<EventType> {
2152    type Error = &'static str;
2153    fn try_from(protobuf_event_name_list: ProtobufEventNameList) -> Result<Self, &'static str> {
2154        let event_types: Vec<ProtobufEventType> = protobuf_event_name_list
2155            .event_types
2156            .iter()
2157            .filter_map(|i| ProtobufEventType::try_from(*i).ok())
2158            .collect();
2159        let event_types: Vec<EventType> = event_types
2160            .iter()
2161            .filter_map(|e| EventType::try_from(*e).ok())
2162            .collect();
2163        Ok(event_types.into_iter().collect())
2164    }
2165}
2166
2167impl TryFrom<HashSet<EventType>> for ProtobufEventNameList {
2168    type Error = &'static str;
2169    fn try_from(event_types: HashSet<EventType>) -> Result<Self, &'static str> {
2170        let protobuf_event_name_list = ProtobufEventNameList {
2171            event_types: event_types
2172                .iter()
2173                .filter_map(|e| ProtobufEventType::try_from(*e).ok())
2174                .map(|e| e as i32)
2175                .collect(),
2176        };
2177        Ok(protobuf_event_name_list)
2178    }
2179}
2180
2181impl TryFrom<ProtobufEventType> for EventType {
2182    type Error = &'static str;
2183    fn try_from(protobuf_event_type: ProtobufEventType) -> Result<Self, &'static str> {
2184        Ok(match protobuf_event_type {
2185            ProtobufEventType::ModeUpdate => EventType::ModeUpdate,
2186            ProtobufEventType::TabUpdate => EventType::TabUpdate,
2187            ProtobufEventType::PaneUpdate => EventType::PaneUpdate,
2188            ProtobufEventType::Key => EventType::Key,
2189            ProtobufEventType::Mouse => EventType::Mouse,
2190            ProtobufEventType::Timer => EventType::Timer,
2191            ProtobufEventType::CopyToClipboard => EventType::CopyToClipboard,
2192            ProtobufEventType::SystemClipboardFailure => EventType::SystemClipboardFailure,
2193            ProtobufEventType::InputReceived => EventType::InputReceived,
2194            ProtobufEventType::Visible => EventType::Visible,
2195            ProtobufEventType::CustomMessage => EventType::CustomMessage,
2196            ProtobufEventType::FileSystemCreate => EventType::FileSystemCreate,
2197            ProtobufEventType::FileSystemRead => EventType::FileSystemRead,
2198            ProtobufEventType::FileSystemUpdate => EventType::FileSystemUpdate,
2199            ProtobufEventType::FileSystemDelete => EventType::FileSystemDelete,
2200            ProtobufEventType::PermissionRequestResult => EventType::PermissionRequestResult,
2201            ProtobufEventType::SessionUpdate => EventType::SessionUpdate,
2202            ProtobufEventType::RunCommandResult => EventType::RunCommandResult,
2203            ProtobufEventType::WebRequestResult => EventType::WebRequestResult,
2204            ProtobufEventType::CommandPaneOpened => EventType::CommandPaneOpened,
2205            ProtobufEventType::CommandPaneExited => EventType::CommandPaneExited,
2206            ProtobufEventType::PaneClosed => EventType::PaneClosed,
2207            ProtobufEventType::EditPaneOpened => EventType::EditPaneOpened,
2208            ProtobufEventType::EditPaneExited => EventType::EditPaneExited,
2209            ProtobufEventType::CommandPaneReRun => EventType::CommandPaneReRun,
2210            ProtobufEventType::FailedToWriteConfigToDisk => EventType::FailedToWriteConfigToDisk,
2211            ProtobufEventType::ListClients => EventType::ListClients,
2212            ProtobufEventType::HostFolderChanged => EventType::HostFolderChanged,
2213            ProtobufEventType::FailedToChangeHostFolder => EventType::FailedToChangeHostFolder,
2214            ProtobufEventType::PastedText => EventType::PastedText,
2215            ProtobufEventType::ConfigWasWrittenToDisk => EventType::ConfigWasWrittenToDisk,
2216            ProtobufEventType::WebServerStatus => EventType::WebServerStatus,
2217            ProtobufEventType::BeforeClose => EventType::BeforeClose,
2218            ProtobufEventType::FailedToStartWebServer => EventType::FailedToStartWebServer,
2219            ProtobufEventType::InterceptedKeyPress => EventType::InterceptedKeyPress,
2220            ProtobufEventType::PaneRenderReport => EventType::PaneRenderReport,
2221            ProtobufEventType::UserAction => EventType::UserAction,
2222            ProtobufEventType::ActionComplete => EventType::ActionComplete,
2223            ProtobufEventType::CwdChanged => EventType::CwdChanged,
2224            ProtobufEventType::CommandChanged => EventType::CommandChanged,
2225            ProtobufEventType::AvailableLayoutInfo => EventType::AvailableLayoutInfo,
2226            ProtobufEventType::PluginConfigurationChanged => EventType::PluginConfigurationChanged,
2227            ProtobufEventType::HighlightClicked => EventType::HighlightClicked,
2228            ProtobufEventType::InitialKeybinds => EventType::InitialKeybinds,
2229            ProtobufEventType::HostTerminalThemeChanged => EventType::HostTerminalThemeChanged,
2230            ProtobufEventType::SoftKeyboardVisibilityChanged => {
2231                EventType::SoftKeyboardVisibilityChanged
2232            },
2233            ProtobufEventType::HintText => EventType::HintText,
2234            ProtobufEventType::ActivePaneScroll => EventType::ActivePaneScroll,
2235        })
2236    }
2237}
2238
2239impl TryFrom<EventType> for ProtobufEventType {
2240    type Error = &'static str;
2241    fn try_from(event_type: EventType) -> Result<Self, &'static str> {
2242        Ok(match event_type {
2243            EventType::ModeUpdate => ProtobufEventType::ModeUpdate,
2244            EventType::TabUpdate => ProtobufEventType::TabUpdate,
2245            EventType::PaneUpdate => ProtobufEventType::PaneUpdate,
2246            EventType::Key => ProtobufEventType::Key,
2247            EventType::Mouse => ProtobufEventType::Mouse,
2248            EventType::Timer => ProtobufEventType::Timer,
2249            EventType::CopyToClipboard => ProtobufEventType::CopyToClipboard,
2250            EventType::SystemClipboardFailure => ProtobufEventType::SystemClipboardFailure,
2251            EventType::InputReceived => ProtobufEventType::InputReceived,
2252            EventType::Visible => ProtobufEventType::Visible,
2253            EventType::CustomMessage => ProtobufEventType::CustomMessage,
2254            EventType::FileSystemCreate => ProtobufEventType::FileSystemCreate,
2255            EventType::FileSystemRead => ProtobufEventType::FileSystemRead,
2256            EventType::FileSystemUpdate => ProtobufEventType::FileSystemUpdate,
2257            EventType::FileSystemDelete => ProtobufEventType::FileSystemDelete,
2258            EventType::PermissionRequestResult => ProtobufEventType::PermissionRequestResult,
2259            EventType::SessionUpdate => ProtobufEventType::SessionUpdate,
2260            EventType::RunCommandResult => ProtobufEventType::RunCommandResult,
2261            EventType::WebRequestResult => ProtobufEventType::WebRequestResult,
2262            EventType::CommandPaneOpened => ProtobufEventType::CommandPaneOpened,
2263            EventType::CommandPaneExited => ProtobufEventType::CommandPaneExited,
2264            EventType::PaneClosed => ProtobufEventType::PaneClosed,
2265            EventType::EditPaneOpened => ProtobufEventType::EditPaneOpened,
2266            EventType::EditPaneExited => ProtobufEventType::EditPaneExited,
2267            EventType::CommandPaneReRun => ProtobufEventType::CommandPaneReRun,
2268            EventType::FailedToWriteConfigToDisk => ProtobufEventType::FailedToWriteConfigToDisk,
2269            EventType::ListClients => ProtobufEventType::ListClients,
2270            EventType::HostFolderChanged => ProtobufEventType::HostFolderChanged,
2271            EventType::FailedToChangeHostFolder => ProtobufEventType::FailedToChangeHostFolder,
2272            EventType::PastedText => ProtobufEventType::PastedText,
2273            EventType::ConfigWasWrittenToDisk => ProtobufEventType::ConfigWasWrittenToDisk,
2274            EventType::WebServerStatus => ProtobufEventType::WebServerStatus,
2275            EventType::BeforeClose => ProtobufEventType::BeforeClose,
2276            EventType::FailedToStartWebServer => ProtobufEventType::FailedToStartWebServer,
2277            EventType::InterceptedKeyPress => ProtobufEventType::InterceptedKeyPress,
2278            EventType::PaneRenderReport => ProtobufEventType::PaneRenderReport,
2279            EventType::UserAction => ProtobufEventType::UserAction,
2280            EventType::ActionComplete => ProtobufEventType::ActionComplete,
2281            EventType::CwdChanged => ProtobufEventType::CwdChanged,
2282            EventType::CommandChanged => ProtobufEventType::CommandChanged,
2283            EventType::AvailableLayoutInfo => ProtobufEventType::AvailableLayoutInfo,
2284            EventType::PluginConfigurationChanged => ProtobufEventType::PluginConfigurationChanged,
2285            EventType::HighlightClicked => ProtobufEventType::HighlightClicked,
2286            EventType::InitialKeybinds => ProtobufEventType::InitialKeybinds,
2287            EventType::HostTerminalThemeChanged => ProtobufEventType::HostTerminalThemeChanged,
2288            EventType::SoftKeyboardVisibilityChanged => {
2289                ProtobufEventType::SoftKeyboardVisibilityChanged
2290            },
2291            EventType::HintText => ProtobufEventType::HintText,
2292            EventType::ActivePaneScroll => ProtobufEventType::ActivePaneScroll,
2293        })
2294    }
2295}
2296
2297impl From<StyledText> for ProtobufStyledText {
2298    fn from(styled_text: StyledText) -> Self {
2299        ProtobufStyledText {
2300            text: styled_text.text,
2301            indices: styled_text
2302                .indices
2303                .into_iter()
2304                .map(|inner| ProtobufStyledTextIndices {
2305                    indices: inner.into_iter().map(|i| i as u32).collect(),
2306                })
2307                .collect(),
2308        }
2309    }
2310}
2311
2312impl From<ProtobufStyledText> for StyledText {
2313    fn from(styled_text: ProtobufStyledText) -> Self {
2314        StyledText {
2315            text: styled_text.text,
2316            indices: styled_text
2317                .indices
2318                .into_iter()
2319                .map(|inner| inner.indices.into_iter().map(|i| i as usize).collect())
2320                .collect(),
2321        }
2322    }
2323}
2324
2325impl From<HostTerminalThemeMode> for ProtobufHostTerminalThemeIndication {
2326    fn from(mode: HostTerminalThemeMode) -> Self {
2327        match mode {
2328            HostTerminalThemeMode::Dark => ProtobufHostTerminalThemeIndication::Dark,
2329            HostTerminalThemeMode::Light => ProtobufHostTerminalThemeIndication::Light,
2330        }
2331    }
2332}
2333
2334impl From<ProtobufHostTerminalThemeIndication> for HostTerminalThemeMode {
2335    fn from(mode: ProtobufHostTerminalThemeIndication) -> Self {
2336        match mode {
2337            ProtobufHostTerminalThemeIndication::Dark => HostTerminalThemeMode::Dark,
2338            ProtobufHostTerminalThemeIndication::Light => HostTerminalThemeMode::Light,
2339        }
2340    }
2341}
2342
2343impl From<ProtobufResurrectableSession> for (String, Duration) {
2344    fn from(protobuf_resurrectable_session: ProtobufResurrectableSession) -> (String, Duration) {
2345        (
2346            protobuf_resurrectable_session.name,
2347            Duration::from_secs(protobuf_resurrectable_session.creation_time),
2348        )
2349    }
2350}
2351
2352impl From<(String, Duration)> for ProtobufResurrectableSession {
2353    fn from(session_name_and_creation_time: (String, Duration)) -> ProtobufResurrectableSession {
2354        ProtobufResurrectableSession {
2355            name: session_name_and_creation_time.0,
2356            creation_time: session_name_and_creation_time.1.as_secs(),
2357        }
2358    }
2359}
2360
2361impl From<&ProtobufFileMetadata> for Option<FileMetadata> {
2362    fn from(protobuf_file_metadata: &ProtobufFileMetadata) -> Option<FileMetadata> {
2363        if protobuf_file_metadata.metadata_is_set {
2364            Some(FileMetadata {
2365                is_file: protobuf_file_metadata.is_file,
2366                is_dir: protobuf_file_metadata.is_dir,
2367                is_symlink: protobuf_file_metadata.is_symlink,
2368                len: protobuf_file_metadata.len,
2369            })
2370        } else {
2371            None
2372        }
2373    }
2374}
2375
2376impl From<Option<FileMetadata>> for ProtobufFileMetadata {
2377    fn from(file_metadata: Option<FileMetadata>) -> ProtobufFileMetadata {
2378        match file_metadata {
2379            Some(file_metadata) => ProtobufFileMetadata {
2380                metadata_is_set: true,
2381                is_file: file_metadata.is_file,
2382                is_dir: file_metadata.is_dir,
2383                is_symlink: file_metadata.is_symlink,
2384                len: file_metadata.len,
2385            },
2386            None => ProtobufFileMetadata {
2387                metadata_is_set: false,
2388                ..Default::default()
2389            },
2390        }
2391    }
2392}
2393
2394#[test]
2395fn serialize_mode_update_event() {
2396    use prost::Message;
2397    let mode_update_event = Event::ModeUpdate(Default::default());
2398    let protobuf_event: ProtobufEvent = mode_update_event.clone().try_into().unwrap();
2399    let serialized_protobuf_event = protobuf_event.encode_to_vec();
2400    let deserialized_protobuf_event: ProtobufEvent =
2401        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
2402    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
2403    assert_eq!(
2404        mode_update_event, deserialized_event,
2405        "Event properly serialized/deserialized without change"
2406    );
2407}
2408
2409#[test]
2410fn serialize_mode_update_event_with_non_default_values() {
2411    use crate::data::{BareKey, Palette, PaletteColor, ThemeHue};
2412    use prost::Message;
2413    let mode_update_event = Event::ModeUpdate(ModeInfo {
2414        mode: InputMode::Locked,
2415        keybinds: vec![
2416            (
2417                InputMode::Locked,
2418                vec![(
2419                    KeyWithModifier::new(BareKey::Char('b')).with_alt_modifier(),
2420                    vec![Action::SwitchToMode {
2421                        input_mode: InputMode::Normal,
2422                    }],
2423                )],
2424            ),
2425            (
2426                InputMode::Tab,
2427                vec![(
2428                    KeyWithModifier::new(BareKey::Up).with_alt_modifier(),
2429                    vec![Action::SwitchToMode {
2430                        input_mode: InputMode::Pane,
2431                    }],
2432                )],
2433            ),
2434            (
2435                InputMode::Pane,
2436                vec![
2437                    (
2438                        KeyWithModifier::new(BareKey::Char('b')).with_ctrl_modifier(),
2439                        vec![
2440                            Action::SwitchToMode {
2441                                input_mode: InputMode::Tmux,
2442                            },
2443                            Action::Write {
2444                                key_with_modifier: None,
2445                                bytes: vec![10],
2446                                is_kitty_keyboard_protocol: false,
2447                            },
2448                        ],
2449                    ),
2450                    (
2451                        KeyWithModifier::new(BareKey::Char('a')),
2452                        vec![Action::WriteChars {
2453                            chars: "foo".to_owned(),
2454                        }],
2455                    ),
2456                ],
2457            ),
2458        ],
2459        style: Style {
2460            colors: Palette {
2461                source: crate::data::PaletteSource::Default,
2462                theme_hue: ThemeHue::Light,
2463                fg: PaletteColor::Rgb((1, 1, 1)),
2464                bg: PaletteColor::Rgb((200, 200, 200)),
2465                black: PaletteColor::EightBit(1),
2466                red: PaletteColor::EightBit(2),
2467                green: PaletteColor::EightBit(2),
2468                yellow: PaletteColor::EightBit(2),
2469                blue: PaletteColor::EightBit(2),
2470                magenta: PaletteColor::EightBit(2),
2471                cyan: PaletteColor::EightBit(2),
2472                white: PaletteColor::EightBit(2),
2473                orange: PaletteColor::EightBit(2),
2474                gray: PaletteColor::EightBit(2),
2475                purple: PaletteColor::EightBit(2),
2476                gold: PaletteColor::EightBit(2),
2477                silver: PaletteColor::EightBit(2),
2478                pink: PaletteColor::EightBit(2),
2479                brown: PaletteColor::Rgb((222, 221, 220)),
2480            }
2481            .into(),
2482            // TODO: replace default
2483            rounded_corners: true,
2484            hide_session_name: false,
2485        },
2486        capabilities: PluginCapabilities { arrow_fonts: false },
2487        session_name: Some("my awesome test session".to_owned()),
2488        base_mode: Some(InputMode::Locked),
2489        editor: Some(PathBuf::from("my_awesome_editor")),
2490        shell: Some(PathBuf::from("my_awesome_shell")),
2491        web_clients_allowed: Some(true),
2492        web_sharing: Some(WebSharing::default()),
2493        currently_marking_pane_group: Some(false),
2494        is_web_client: Some(false),
2495        web_server_ip: IpAddr::from_str("127.0.0.1").ok(),
2496        web_server_port: Some(8082),
2497        web_server_capability: Some(true),
2498        pane_frame_style: Some(crate::input::options::PaneFrameStyle::Titles),
2499        session_dimmed: Some(true),
2500        session_ancestry: vec!["work".to_owned(), "prod".to_owned()],
2501        host_fullscreen: Some(true),
2502        nested_ascend_keys: vec![
2503            KeyWithModifier::new(BareKey::Char('o')).with_ctrl_modifier(),
2504            KeyWithModifier::new(BareKey::Up),
2505        ],
2506        session_ascended: Some(true),
2507        nested_descend_keys: vec![
2508            KeyWithModifier::new(BareKey::Char('o')).with_ctrl_modifier(),
2509            KeyWithModifier::new(BareKey::Down),
2510        ],
2511    });
2512    let protobuf_event: ProtobufEvent = mode_update_event.clone().try_into().unwrap();
2513    let serialized_protobuf_event = protobuf_event.encode_to_vec();
2514    let deserialized_protobuf_event: ProtobufEvent =
2515        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
2516    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
2517    assert_eq!(
2518        mode_update_event, deserialized_event,
2519        "Event properly serialized/deserialized without change"
2520    );
2521}
2522
2523#[test]
2524fn serialize_tab_update_event() {
2525    use prost::Message;
2526    let tab_update_event = Event::TabUpdate(Default::default());
2527    let protobuf_event: ProtobufEvent = tab_update_event.clone().try_into().unwrap();
2528    let serialized_protobuf_event = protobuf_event.encode_to_vec();
2529    let deserialized_protobuf_event: ProtobufEvent =
2530        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
2531    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
2532    assert_eq!(
2533        tab_update_event, deserialized_event,
2534        "Event properly serialized/deserialized without change"
2535    );
2536}
2537
2538#[test]
2539fn serialize_tab_update_event_with_non_default_values() {
2540    use prost::Message;
2541    let tab_update_event = Event::TabUpdate(vec![
2542        TabInfo {
2543            position: 0,
2544            name: "First tab".to_owned(),
2545            active: true,
2546            panes_to_hide: 2,
2547            is_fullscreen_active: true,
2548            is_sync_panes_active: false,
2549            are_floating_panes_visible: true,
2550            other_focused_clients: vec![2, 3, 4],
2551            active_swap_layout_name: Some("my cool swap layout".to_owned()),
2552            is_swap_layout_dirty: false,
2553            viewport_rows: 10,
2554            viewport_columns: 10,
2555            display_area_rows: 10,
2556            display_area_columns: 10,
2557            selectable_tiled_panes_count: 10,
2558            selectable_floating_panes_count: 10,
2559            tab_id: 0,
2560            has_bell_notification: false,
2561            is_flashing_bell: false,
2562        },
2563        TabInfo {
2564            position: 1,
2565            name: "Secondtab".to_owned(),
2566            active: false,
2567            panes_to_hide: 5,
2568            is_fullscreen_active: false,
2569            is_sync_panes_active: true,
2570            are_floating_panes_visible: true,
2571            other_focused_clients: vec![1, 5, 111],
2572            active_swap_layout_name: None,
2573            is_swap_layout_dirty: true,
2574            viewport_rows: 10,
2575            viewport_columns: 10,
2576            display_area_rows: 10,
2577            display_area_columns: 10,
2578            selectable_tiled_panes_count: 10,
2579            selectable_floating_panes_count: 10,
2580            tab_id: 1,
2581            has_bell_notification: false,
2582            is_flashing_bell: false,
2583        },
2584        TabInfo::default(),
2585    ]);
2586    let protobuf_event: ProtobufEvent = tab_update_event.clone().try_into().unwrap();
2587    let serialized_protobuf_event = protobuf_event.encode_to_vec();
2588    let deserialized_protobuf_event: ProtobufEvent =
2589        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
2590    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
2591    assert_eq!(
2592        tab_update_event, deserialized_event,
2593        "Event properly serialized/deserialized without change"
2594    );
2595}
2596
2597#[test]
2598fn serialize_pane_update_event() {
2599    use prost::Message;
2600    let pane_update_event = Event::PaneUpdate(Default::default());
2601    let protobuf_event: ProtobufEvent = pane_update_event.clone().try_into().unwrap();
2602    let serialized_protobuf_event = protobuf_event.encode_to_vec();
2603    let deserialized_protobuf_event: ProtobufEvent =
2604        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
2605    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
2606    assert_eq!(
2607        pane_update_event, deserialized_event,
2608        "Event properly serialized/deserialized without change"
2609    );
2610}
2611
2612#[test]
2613fn serialize_hint_text_event() {
2614    use prost::Message;
2615    let hint_text_event = Event::HintText(BTreeMap::from([
2616        (
2617            10,
2618            StyledText {
2619                text: "group".to_owned(),
2620                indices: vec![vec![0]],
2621            },
2622        ),
2623        (
2624            42,
2625            StyledText {
2626                text: "resize".to_owned(),
2627                indices: vec![vec![0, 1], vec![3]],
2628            },
2629        ),
2630    ]));
2631    let protobuf_event: ProtobufEvent = hint_text_event.clone().try_into().unwrap();
2632    let serialized_protobuf_event = protobuf_event.encode_to_vec();
2633    let deserialized_protobuf_event: ProtobufEvent =
2634        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
2635    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
2636    assert_eq!(
2637        hint_text_event, deserialized_event,
2638        "Event properly serialized/deserialized without change"
2639    );
2640}
2641
2642#[test]
2643fn serialize_active_pane_scroll_event() {
2644    use prost::Message;
2645    for active_pane_scroll_event in [
2646        Event::ActivePaneScroll(Some((3, 12))),
2647        Event::ActivePaneScroll(None),
2648    ] {
2649        let protobuf_event: ProtobufEvent = active_pane_scroll_event.clone().try_into().unwrap();
2650        let serialized_protobuf_event = protobuf_event.encode_to_vec();
2651        let deserialized_protobuf_event: ProtobufEvent =
2652            Message::decode(serialized_protobuf_event.as_slice()).unwrap();
2653        let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
2654        assert_eq!(
2655            active_pane_scroll_event, deserialized_event,
2656            "Event properly serialized/deserialized without change"
2657        );
2658    }
2659}
2660
2661#[test]
2662fn serialize_key_event() {
2663    use crate::data::BareKey;
2664    use prost::Message;
2665    let key_event = Event::Key(KeyWithModifier::new(BareKey::Char('a')).with_ctrl_modifier());
2666    let protobuf_event: ProtobufEvent = key_event.clone().try_into().unwrap();
2667    let serialized_protobuf_event = protobuf_event.encode_to_vec();
2668    let deserialized_protobuf_event: ProtobufEvent =
2669        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
2670    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
2671    assert_eq!(
2672        key_event, deserialized_event,
2673        "Event properly serialized/deserialized without change"
2674    );
2675}
2676
2677#[test]
2678fn serialize_mouse_event() {
2679    use prost::Message;
2680    let mouse_event = Event::Mouse(Mouse::LeftClick(1, 1));
2681    let protobuf_event: ProtobufEvent = mouse_event.clone().try_into().unwrap();
2682    let serialized_protobuf_event = protobuf_event.encode_to_vec();
2683    let deserialized_protobuf_event: ProtobufEvent =
2684        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
2685    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
2686    assert_eq!(
2687        mouse_event, deserialized_event,
2688        "Event properly serialized/deserialized without change"
2689    );
2690}
2691
2692#[test]
2693fn serialize_mouse_event_without_position() {
2694    use prost::Message;
2695    let mouse_event = Event::Mouse(Mouse::ScrollUp(17));
2696    let protobuf_event: ProtobufEvent = mouse_event.clone().try_into().unwrap();
2697    let serialized_protobuf_event = protobuf_event.encode_to_vec();
2698    let deserialized_protobuf_event: ProtobufEvent =
2699        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
2700    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
2701    assert_eq!(
2702        mouse_event, deserialized_event,
2703        "Event properly serialized/deserialized without change"
2704    );
2705}
2706
2707#[test]
2708fn serialize_timer_event() {
2709    use prost::Message;
2710    let timer_event = Event::Timer(1.5);
2711    let protobuf_event: ProtobufEvent = timer_event.clone().try_into().unwrap();
2712    let serialized_protobuf_event = protobuf_event.encode_to_vec();
2713    let deserialized_protobuf_event: ProtobufEvent =
2714        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
2715    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
2716    assert_eq!(
2717        timer_event, deserialized_event,
2718        "Event properly serialized/deserialized without change"
2719    );
2720}
2721
2722#[test]
2723fn serialize_copy_to_clipboard_event() {
2724    use prost::Message;
2725    let copy_event = Event::CopyToClipboard(CopyDestination::Primary);
2726    let protobuf_event: ProtobufEvent = copy_event.clone().try_into().unwrap();
2727    let serialized_protobuf_event = protobuf_event.encode_to_vec();
2728    let deserialized_protobuf_event: ProtobufEvent =
2729        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
2730    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
2731    assert_eq!(
2732        copy_event, deserialized_event,
2733        "Event properly serialized/deserialized without change"
2734    );
2735}
2736
2737#[test]
2738fn serialize_clipboard_failure_event() {
2739    use prost::Message;
2740    let copy_event = Event::SystemClipboardFailure;
2741    let protobuf_event: ProtobufEvent = copy_event.clone().try_into().unwrap();
2742    let serialized_protobuf_event = protobuf_event.encode_to_vec();
2743    let deserialized_protobuf_event: ProtobufEvent =
2744        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
2745    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
2746    assert_eq!(
2747        copy_event, deserialized_event,
2748        "Event properly serialized/deserialized without change"
2749    );
2750}
2751
2752#[test]
2753fn serialize_input_received_event() {
2754    use prost::Message;
2755    let input_received_event = Event::InputReceived;
2756    let protobuf_event: ProtobufEvent = input_received_event.clone().try_into().unwrap();
2757    let serialized_protobuf_event = protobuf_event.encode_to_vec();
2758    let deserialized_protobuf_event: ProtobufEvent =
2759        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
2760    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
2761    assert_eq!(
2762        input_received_event, deserialized_event,
2763        "Event properly serialized/deserialized without change"
2764    );
2765}
2766
2767#[test]
2768fn serialize_visible_event() {
2769    use prost::Message;
2770    let visible_event = Event::Visible(true);
2771    let protobuf_event: ProtobufEvent = visible_event.clone().try_into().unwrap();
2772    let serialized_protobuf_event = protobuf_event.encode_to_vec();
2773    let deserialized_protobuf_event: ProtobufEvent =
2774        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
2775    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
2776    assert_eq!(
2777        visible_event, deserialized_event,
2778        "Event properly serialized/deserialized without change"
2779    );
2780}
2781
2782#[test]
2783fn serialize_custom_message_event() {
2784    use prost::Message;
2785    let custom_message_event = Event::CustomMessage("foo".to_owned(), "bar".to_owned());
2786    let protobuf_event: ProtobufEvent = custom_message_event.clone().try_into().unwrap();
2787    let serialized_protobuf_event = protobuf_event.encode_to_vec();
2788    let deserialized_protobuf_event: ProtobufEvent =
2789        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
2790    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
2791    assert_eq!(
2792        custom_message_event, deserialized_event,
2793        "Event properly serialized/deserialized without change"
2794    );
2795}
2796
2797#[test]
2798fn serialize_file_system_create_event() {
2799    use prost::Message;
2800    let file_system_event = Event::FileSystemCreate(vec![
2801        ("/absolute/path".into(), None),
2802        ("./relative_path".into(), Default::default()),
2803    ]);
2804    let protobuf_event: ProtobufEvent = file_system_event.clone().try_into().unwrap();
2805    let serialized_protobuf_event = protobuf_event.encode_to_vec();
2806    let deserialized_protobuf_event: ProtobufEvent =
2807        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
2808    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
2809    assert_eq!(
2810        file_system_event, deserialized_event,
2811        "Event properly serialized/deserialized without change"
2812    );
2813}
2814
2815#[test]
2816fn serialize_file_system_read_event() {
2817    use prost::Message;
2818    let file_system_event = Event::FileSystemRead(vec![
2819        ("/absolute/path".into(), None),
2820        ("./relative_path".into(), Default::default()),
2821    ]);
2822    let protobuf_event: ProtobufEvent = file_system_event.clone().try_into().unwrap();
2823    let serialized_protobuf_event = protobuf_event.encode_to_vec();
2824    let deserialized_protobuf_event: ProtobufEvent =
2825        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
2826    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
2827    assert_eq!(
2828        file_system_event, deserialized_event,
2829        "Event properly serialized/deserialized without change"
2830    );
2831}
2832
2833#[test]
2834fn serialize_file_system_update_event() {
2835    use prost::Message;
2836    let file_system_event = Event::FileSystemUpdate(vec![
2837        ("/absolute/path".into(), None),
2838        ("./relative_path".into(), Some(Default::default())),
2839    ]);
2840    let protobuf_event: ProtobufEvent = file_system_event.clone().try_into().unwrap();
2841    let serialized_protobuf_event = protobuf_event.encode_to_vec();
2842    let deserialized_protobuf_event: ProtobufEvent =
2843        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
2844    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
2845    assert_eq!(
2846        file_system_event, deserialized_event,
2847        "Event properly serialized/deserialized without change"
2848    );
2849}
2850
2851#[test]
2852fn serialize_file_system_delete_event() {
2853    use prost::Message;
2854    let file_system_event = Event::FileSystemDelete(vec![
2855        ("/absolute/path".into(), None),
2856        ("./relative_path".into(), Default::default()),
2857    ]);
2858    let protobuf_event: ProtobufEvent = file_system_event.clone().try_into().unwrap();
2859    let serialized_protobuf_event = protobuf_event.encode_to_vec();
2860    let deserialized_protobuf_event: ProtobufEvent =
2861        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
2862    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
2863    assert_eq!(
2864        file_system_event, deserialized_event,
2865        "Event properly serialized/deserialized without change"
2866    );
2867}
2868
2869#[test]
2870fn serialize_session_update_event() {
2871    use prost::Message;
2872    let session_update_event = Event::SessionUpdate(Default::default(), Default::default());
2873    let protobuf_event: ProtobufEvent = session_update_event.clone().try_into().unwrap();
2874    let serialized_protobuf_event = protobuf_event.encode_to_vec();
2875    let deserialized_protobuf_event: ProtobufEvent =
2876        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
2877    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
2878    assert_eq!(
2879        session_update_event, deserialized_event,
2880        "Event properly serialized/deserialized without change"
2881    );
2882}
2883
2884#[test]
2885fn serialize_session_update_event_with_non_default_values() {
2886    use prost::Message;
2887    let tab_infos = vec![
2888        TabInfo {
2889            position: 0,
2890            name: "First tab".to_owned(),
2891            active: true,
2892            panes_to_hide: 2,
2893            is_fullscreen_active: true,
2894            is_sync_panes_active: false,
2895            are_floating_panes_visible: true,
2896            other_focused_clients: vec![2, 3, 4],
2897            active_swap_layout_name: Some("my cool swap layout".to_owned()),
2898            is_swap_layout_dirty: false,
2899            viewport_rows: 10,
2900            viewport_columns: 10,
2901            display_area_rows: 10,
2902            display_area_columns: 10,
2903            selectable_tiled_panes_count: 10,
2904            selectable_floating_panes_count: 10,
2905            tab_id: 0,
2906            has_bell_notification: false,
2907            is_flashing_bell: false,
2908        },
2909        TabInfo {
2910            position: 1,
2911            name: "Secondtab".to_owned(),
2912            active: false,
2913            panes_to_hide: 5,
2914            is_fullscreen_active: false,
2915            is_sync_panes_active: true,
2916            are_floating_panes_visible: true,
2917            other_focused_clients: vec![1, 5, 111],
2918            active_swap_layout_name: None,
2919            is_swap_layout_dirty: true,
2920            viewport_rows: 10,
2921            viewport_columns: 10,
2922            display_area_rows: 10,
2923            display_area_columns: 10,
2924            selectable_tiled_panes_count: 10,
2925            selectable_floating_panes_count: 10,
2926            tab_id: 1,
2927            has_bell_notification: false,
2928            is_flashing_bell: false,
2929        },
2930        TabInfo::default(),
2931    ];
2932    let mut panes = HashMap::new();
2933    let mut index_in_pane_group_1 = BTreeMap::new();
2934    index_in_pane_group_1.insert(1, 0);
2935    index_in_pane_group_1.insert(2, 0);
2936    index_in_pane_group_1.insert(3, 0);
2937    let mut index_in_pane_group_2 = BTreeMap::new();
2938    index_in_pane_group_2.insert(1, 1);
2939    index_in_pane_group_2.insert(2, 1);
2940    index_in_pane_group_2.insert(3, 1);
2941    let panes_list = vec![
2942        PaneInfo {
2943            id: 1,
2944            is_plugin: false,
2945            is_focused: true,
2946            is_fullscreen: true,
2947            is_floating: false,
2948            is_suppressed: false,
2949            title: "pane 1".to_owned(),
2950            exited: false,
2951            exit_status: None,
2952            is_held: false,
2953            pane_x: 0,
2954            pane_content_x: 1,
2955            pane_y: 0,
2956            pane_content_y: 1,
2957            pane_rows: 5,
2958            pane_content_rows: 4,
2959            pane_columns: 22,
2960            pane_content_columns: 21,
2961            cursor_coordinates_in_pane: Some((0, 0)),
2962            terminal_command: Some("foo".to_owned()),
2963            plugin_url: None,
2964            is_selectable: true,
2965            index_in_pane_group: index_in_pane_group_1,
2966            default_fg: None,
2967            default_bg: None,
2968        },
2969        PaneInfo {
2970            id: 1,
2971            is_plugin: true,
2972            is_focused: true,
2973            is_fullscreen: true,
2974            is_floating: false,
2975            is_suppressed: false,
2976            title: "pane 1".to_owned(),
2977            exited: false,
2978            exit_status: None,
2979            is_held: false,
2980            pane_x: 0,
2981            pane_content_x: 1,
2982            pane_y: 0,
2983            pane_content_y: 1,
2984            pane_rows: 5,
2985            pane_content_rows: 4,
2986            pane_columns: 22,
2987            pane_content_columns: 21,
2988            cursor_coordinates_in_pane: Some((0, 0)),
2989            terminal_command: None,
2990            plugin_url: Some("i_am_a_fake_plugin".to_owned()),
2991            is_selectable: true,
2992            index_in_pane_group: index_in_pane_group_2,
2993            default_fg: None,
2994            default_bg: None,
2995        },
2996    ];
2997    panes.insert(0, panes_list);
2998    let mut plugins = BTreeMap::new();
2999    let mut plugin_configuration = BTreeMap::new();
3000    plugin_configuration.insert("config_key".to_owned(), "config_value".to_owned());
3001    plugins.insert(
3002        1,
3003        PluginInfo {
3004            location: "https://example.com/my-plugin.wasm".to_owned(),
3005            configuration: plugin_configuration,
3006        },
3007    );
3008    let mut tab_history = BTreeMap::new();
3009    tab_history.insert(1, vec![1, 2, 3]);
3010    tab_history.insert(2, vec![1, 2, 3]);
3011    let session_info_1 = SessionInfo {
3012        name: "session 1".to_owned(),
3013        tabs: tab_infos,
3014        panes: PaneManifest { panes },
3015        connected_clients: 2,
3016        is_current_session: true,
3017        available_layouts: vec![
3018            LayoutInfo::File(
3019                "layout 1".to_owned(),
3020                LayoutMetadata {
3021                    tabs: vec![],
3022                    creation_time: "0".to_owned(),
3023                    update_time: "0".to_owned(),
3024                },
3025            ),
3026            LayoutInfo::BuiltIn("layout2".to_owned()),
3027            LayoutInfo::File(
3028                "layout3".to_owned(),
3029                LayoutMetadata {
3030                    tabs: vec![],
3031                    creation_time: "0".to_owned(),
3032                    update_time: "0".to_owned(),
3033                },
3034            ),
3035        ],
3036        plugins,
3037        web_clients_allowed: false,
3038        web_client_count: 1,
3039        tab_history,
3040        pane_history: Default::default(),
3041        creation_time: Duration::from_secs(100),
3042    };
3043    let session_info_2 = SessionInfo {
3044        name: "session 2".to_owned(),
3045        tabs: vec![],
3046        panes: PaneManifest {
3047            panes: HashMap::new(),
3048        },
3049        connected_clients: 0,
3050        is_current_session: false,
3051        available_layouts: vec![
3052            LayoutInfo::File(
3053                "layout 1".to_owned(),
3054                LayoutMetadata {
3055                    tabs: vec![],
3056                    creation_time: "0".to_owned(),
3057                    update_time: "0".to_owned(),
3058                },
3059            ),
3060            LayoutInfo::BuiltIn("layout2".to_owned()),
3061            LayoutInfo::File(
3062                "layout3".to_owned(),
3063                LayoutMetadata {
3064                    tabs: vec![],
3065                    creation_time: "0".to_owned(),
3066                    update_time: "0".to_owned(),
3067                },
3068            ),
3069        ],
3070        plugins: Default::default(),
3071        web_clients_allowed: false,
3072        web_client_count: 0,
3073        tab_history: Default::default(),
3074        pane_history: Default::default(),
3075        creation_time: Duration::from_secs(200),
3076    };
3077    let session_infos = vec![session_info_1, session_info_2];
3078    let resurrectable_sessions = vec![];
3079
3080    let session_update_event = Event::SessionUpdate(session_infos, resurrectable_sessions);
3081    let protobuf_event: ProtobufEvent = session_update_event.clone().try_into().unwrap();
3082    let serialized_protobuf_event = protobuf_event.encode_to_vec();
3083    let deserialized_protobuf_event: ProtobufEvent =
3084        Message::decode(serialized_protobuf_event.as_slice()).unwrap();
3085    let deserialized_event: Event = deserialized_protobuf_event.try_into().unwrap();
3086    assert_eq!(
3087        session_update_event, deserialized_event,
3088        "Event properly serialized/deserialized without change"
3089    );
3090}
3091
3092// note: ProtobufPaneId and ProtobufPaneType are not the same as the ones defined in plugin_command.rs
3093// this is a duplicate type - we are forced to do this because protobuffs do not support recursive
3094// imports
3095impl TryFrom<ProtobufPaneId> for PaneId {
3096    type Error = &'static str;
3097    fn try_from(protobuf_pane_id: ProtobufPaneId) -> Result<Self, &'static str> {
3098        match ProtobufPaneType::try_from(protobuf_pane_id.pane_type).ok() {
3099            Some(ProtobufPaneType::Terminal) => Ok(PaneId::Terminal(protobuf_pane_id.id)),
3100            Some(ProtobufPaneType::Plugin) => Ok(PaneId::Plugin(protobuf_pane_id.id)),
3101            None => Err("Failed to convert PaneId"),
3102        }
3103    }
3104}
3105
3106// note: ProtobufPaneId and ProtobufPaneType are not the same as the ones defined in plugin_command.rs
3107// this is a duplicate type - we are forced to do this because protobuffs do not support recursive
3108// imports
3109impl TryFrom<PaneId> for ProtobufPaneId {
3110    type Error = &'static str;
3111    fn try_from(pane_id: PaneId) -> Result<Self, &'static str> {
3112        match pane_id {
3113            PaneId::Terminal(id) => Ok(ProtobufPaneId {
3114                pane_type: ProtobufPaneType::Terminal as i32,
3115                id,
3116            }),
3117            PaneId::Plugin(id) => Ok(ProtobufPaneId {
3118                pane_type: ProtobufPaneType::Plugin as i32,
3119                id,
3120            }),
3121        }
3122    }
3123}
3124
3125impl Into<ProtobufWebSharing> for WebSharing {
3126    fn into(self) -> ProtobufWebSharing {
3127        match self {
3128            WebSharing::On => ProtobufWebSharing::On,
3129            WebSharing::Off => ProtobufWebSharing::Off,
3130            WebSharing::Disabled => ProtobufWebSharing::Disabled,
3131        }
3132    }
3133}
3134
3135impl Into<WebSharing> for ProtobufWebSharing {
3136    fn into(self) -> WebSharing {
3137        match self {
3138            ProtobufWebSharing::On => WebSharing::On,
3139            ProtobufWebSharing::Off => WebSharing::Off,
3140            ProtobufWebSharing::Disabled => WebSharing::Disabled,
3141        }
3142    }
3143}
3144
3145impl Into<ProtobufPaneFrameStyle> for crate::input::options::PaneFrameStyle {
3146    fn into(self) -> ProtobufPaneFrameStyle {
3147        match self {
3148            crate::input::options::PaneFrameStyle::Full => ProtobufPaneFrameStyle::Full,
3149            crate::input::options::PaneFrameStyle::Titles => ProtobufPaneFrameStyle::Titles,
3150            crate::input::options::PaneFrameStyle::None => ProtobufPaneFrameStyle::None,
3151        }
3152    }
3153}
3154
3155impl Into<crate::input::options::PaneFrameStyle> for ProtobufPaneFrameStyle {
3156    fn into(self) -> crate::input::options::PaneFrameStyle {
3157        match self {
3158            ProtobufPaneFrameStyle::Full => crate::input::options::PaneFrameStyle::Full,
3159            ProtobufPaneFrameStyle::Titles => crate::input::options::PaneFrameStyle::Titles,
3160            ProtobufPaneFrameStyle::None => crate::input::options::PaneFrameStyle::None,
3161        }
3162    }
3163}
3164
3165impl TryFrom<WebServerStatus> for ProtobufWebServerStatusPayload {
3166    type Error = &'static str;
3167    fn try_from(web_server_status: WebServerStatus) -> Result<Self, &'static str> {
3168        match web_server_status {
3169            WebServerStatus::Online(url) => Ok(ProtobufWebServerStatusPayload {
3170                web_server_status_indication: WebServerStatusIndication::Online as i32,
3171                payload: Some(url),
3172            }),
3173            WebServerStatus::DifferentVersion(version) => Ok(ProtobufWebServerStatusPayload {
3174                web_server_status_indication: WebServerStatusIndication::DifferentVersion as i32,
3175                payload: Some(format!("{}", version)),
3176            }),
3177            WebServerStatus::Offline => Ok(ProtobufWebServerStatusPayload {
3178                web_server_status_indication: WebServerStatusIndication::Offline as i32,
3179                payload: None,
3180            }),
3181        }
3182    }
3183}
3184
3185impl TryFrom<ProtobufWebServerStatusPayload> for WebServerStatus {
3186    type Error = &'static str;
3187    fn try_from(
3188        protobuf_web_server_status: ProtobufWebServerStatusPayload,
3189    ) -> Result<Self, &'static str> {
3190        match WebServerStatusIndication::try_from(
3191            protobuf_web_server_status.web_server_status_indication,
3192        )
3193        .ok()
3194        {
3195            Some(WebServerStatusIndication::Online) => {
3196                let payload = protobuf_web_server_status
3197                    .payload
3198                    .ok_or("payload_not_found")?;
3199                Ok(WebServerStatus::Online(payload))
3200            },
3201            Some(WebServerStatusIndication::DifferentVersion) => {
3202                let payload = protobuf_web_server_status
3203                    .payload
3204                    .ok_or("payload_not_found")?;
3205                Ok(WebServerStatus::DifferentVersion(payload))
3206            },
3207            Some(WebServerStatusIndication::Offline) => Ok(WebServerStatus::Offline),
3208            None => Err("Unknown status"),
3209        }
3210    }
3211}
3212
3213impl TryFrom<ProtobufPaneRenderReportPayload> for HashMap<PaneId, PaneContents> {
3214    type Error = &'static str;
3215    fn try_from(protobuf_payload: ProtobufPaneRenderReportPayload) -> Result<Self, &'static str> {
3216        let mut pane_contents_map = HashMap::new();
3217
3218        for entry in protobuf_payload.pane_contents {
3219            let pane_id = entry
3220                .pane_id
3221                .ok_or("Missing pane_id in PaneContentsEntry")?
3222                .try_into()?;
3223            let pane_contents = entry
3224                .pane_contents
3225                .ok_or("Missing pane_contents in PaneContentsEntry")?
3226                .try_into()?;
3227            pane_contents_map.insert(pane_id, pane_contents);
3228        }
3229
3230        Ok(pane_contents_map)
3231    }
3232}
3233
3234impl TryFrom<HashMap<PaneId, PaneContents>> for ProtobufPaneRenderReportPayload {
3235    type Error = &'static str;
3236    fn try_from(pane_contents_map: HashMap<PaneId, PaneContents>) -> Result<Self, &'static str> {
3237        let mut pane_contents_vec = vec![];
3238
3239        for (pane_id, pane_contents) in pane_contents_map {
3240            pane_contents_vec.push(ProtobufPaneContentsEntry {
3241                pane_id: Some(pane_id.try_into()?),
3242                pane_contents: Some(pane_contents.try_into()?),
3243            });
3244        }
3245
3246        Ok(ProtobufPaneRenderReportPayload {
3247            pane_contents: pane_contents_vec,
3248        })
3249    }
3250}
3251
3252impl TryFrom<ProtobufPaneContents> for PaneContents {
3253    type Error = &'static str;
3254    fn try_from(protobuf_contents: ProtobufPaneContents) -> Result<Self, &'static str> {
3255        let selected_text = protobuf_contents
3256            .selected_text
3257            .map(|st| st.try_into())
3258            .transpose()?;
3259        let cursor = protobuf_contents
3260            .cursor
3261            .map(|p| (p.column as usize, p.line as usize));
3262
3263        Ok(PaneContents {
3264            viewport: protobuf_contents.viewport,
3265            selected_text,
3266            lines_above_viewport: protobuf_contents.lines_above_viewport,
3267            lines_below_viewport: protobuf_contents.lines_below_viewport,
3268            cursor,
3269        })
3270    }
3271}
3272
3273impl TryFrom<PaneContents> for ProtobufPaneContents {
3274    type Error = &'static str;
3275    fn try_from(pane_contents: PaneContents) -> Result<Self, &'static str> {
3276        let selected_text = pane_contents
3277            .selected_text
3278            .map(|st| st.try_into())
3279            .transpose()?;
3280        let cursor = pane_contents.cursor.map(|(x, y)| ProtobufPosition {
3281            line: y as i64,
3282            column: x as i64,
3283        });
3284
3285        Ok(ProtobufPaneContents {
3286            viewport: pane_contents.viewport,
3287            selected_text,
3288            lines_above_viewport: pane_contents.lines_above_viewport,
3289            lines_below_viewport: pane_contents.lines_below_viewport,
3290            cursor,
3291        })
3292    }
3293}
3294
3295impl TryFrom<ProtobufPaneScrollbackResponse> for PaneScrollbackResponse {
3296    type Error = &'static str;
3297    fn try_from(protobuf_response: ProtobufPaneScrollbackResponse) -> Result<Self, &'static str> {
3298        match protobuf_response.response {
3299            Some(pane_scrollback_response::Response::Ok(pane_contents)) => {
3300                Ok(PaneScrollbackResponse::Ok(pane_contents.try_into()?))
3301            },
3302            Some(pane_scrollback_response::Response::Err(error_msg)) => {
3303                Ok(PaneScrollbackResponse::Err(error_msg))
3304            },
3305            None => Err("PaneScrollbackResponse missing response field"),
3306        }
3307    }
3308}
3309
3310impl TryFrom<PaneScrollbackResponse> for ProtobufPaneScrollbackResponse {
3311    type Error = &'static str;
3312    fn try_from(response: PaneScrollbackResponse) -> Result<Self, &'static str> {
3313        let response_field = match response {
3314            PaneScrollbackResponse::Ok(pane_contents) => {
3315                pane_scrollback_response::Response::Ok(pane_contents.try_into()?)
3316            },
3317            PaneScrollbackResponse::Err(error_msg) => {
3318                pane_scrollback_response::Response::Err(error_msg)
3319            },
3320        };
3321        Ok(ProtobufPaneScrollbackResponse {
3322            response: Some(response_field),
3323        })
3324    }
3325}
3326
3327impl TryFrom<ProtobufSelectedText> for SelectedText {
3328    type Error = &'static str;
3329    fn try_from(protobuf_selected_text: ProtobufSelectedText) -> Result<Self, &'static str> {
3330        Ok(SelectedText {
3331            start: protobuf_selected_text
3332                .start
3333                .ok_or("Missing start in SelectedText")?
3334                .try_into()?,
3335            end: protobuf_selected_text
3336                .end
3337                .ok_or("Missing end in SelectedText")?
3338                .try_into()?,
3339        })
3340    }
3341}
3342
3343impl TryFrom<SelectedText> for ProtobufSelectedText {
3344    type Error = &'static str;
3345    fn try_from(selected_text: SelectedText) -> Result<Self, &'static str> {
3346        Ok(ProtobufSelectedText {
3347            start: Some(selected_text.start.try_into()?),
3348            end: Some(selected_text.end.try_into()?),
3349        })
3350    }
3351}