zellij_utils/plugin_api/
event.rs

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