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