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