zellij_utils/plugin_api/
plugin_command.rs

1pub use super::generated_api::api::{
2    action::{PaneIdAndShouldFloat, SwitchToModePayload},
3    event::{EventNameList as ProtobufEventNameList, Header},
4    input_mode::InputMode as ProtobufInputMode,
5    plugin_command::{
6        plugin_command::Payload, BreakPanesToNewTabPayload, BreakPanesToTabWithIndexPayload,
7        ChangeFloatingPanesCoordinatesPayload, ChangeHostFolderPayload,
8        ClearScreenForPaneIdPayload, CliPipeOutputPayload, CloseMultiplePanesPayload,
9        CloseTabWithIndexPayload, CommandName, ContextItem,
10        CreateTokenResponse as ProtobufCreateTokenResponse, CreateTokenResponse,
11        EditScrollbackForPaneWithIdPayload, EmbedMultiplePanesPayload, EnvVariable, ExecCmdPayload,
12        FixedOrPercent as ProtobufFixedOrPercent,
13        FixedOrPercentValue as ProtobufFixedOrPercentValue, FloatMultiplePanesPayload,
14        FloatingPaneCoordinates as ProtobufFloatingPaneCoordinates, GenerateWebLoginTokenPayload,
15        GroupAndUngroupPanesPayload, HidePaneWithIdPayload, HighlightAndUnhighlightPanesPayload,
16        HttpVerb as ProtobufHttpVerb, IdAndNewName, KeyToRebind, KeyToUnbind, KillSessionsPayload,
17        ListTokensResponse, LoadNewPluginPayload, MessageToPluginPayload,
18        MovePaneWithPaneIdInDirectionPayload, MovePaneWithPaneIdPayload, MovePayload,
19        NewPluginArgs as ProtobufNewPluginArgs, NewTabPayload, NewTabsWithLayoutInfoPayload,
20        OpenCommandPaneFloatingNearPluginPayload, OpenCommandPaneInPlaceOfPluginPayload,
21        OpenCommandPaneNearPluginPayload, OpenCommandPanePayload,
22        OpenFileFloatingNearPluginPayload, OpenFileInPlaceOfPluginPayload,
23        OpenFileNearPluginPayload, OpenFilePayload, OpenTerminalFloatingNearPluginPayload,
24        OpenTerminalInPlaceOfPluginPayload, OpenTerminalNearPluginPayload,
25        PageScrollDownInPaneIdPayload, PageScrollUpInPaneIdPayload, PaneId as ProtobufPaneId,
26        PaneIdAndFloatingPaneCoordinates, PaneType as ProtobufPaneType,
27        PluginCommand as ProtobufPluginCommand, PluginMessagePayload, RebindKeysPayload,
28        ReconfigurePayload, ReloadPluginPayload, RenameWebLoginTokenPayload,
29        RenameWebTokenResponse, ReplacePaneWithExistingPanePayload, RequestPluginPermissionPayload,
30        RerunCommandPanePayload, ResizePaneIdWithDirectionPayload, ResizePayload,
31        RevokeAllWebTokensResponse, RevokeTokenResponse, RevokeWebLoginTokenPayload,
32        RunCommandPayload, ScrollDownInPaneIdPayload, ScrollToBottomInPaneIdPayload,
33        ScrollToTopInPaneIdPayload, ScrollUpInPaneIdPayload, SetFloatingPanePinnedPayload,
34        SetSelfMouseSelectionSupportPayload, SetTimeoutPayload, ShowPaneWithIdPayload,
35        StackPanesPayload, SubscribePayload, SwitchSessionPayload, SwitchTabToPayload,
36        TogglePaneEmbedOrEjectForPaneIdPayload, TogglePaneIdFullscreenPayload, UnsubscribePayload,
37        WebRequestPayload, WriteCharsToPaneIdPayload, WriteToPaneIdPayload,
38    },
39    plugin_permission::PermissionType as ProtobufPermissionType,
40    resize::ResizeAction as ProtobufResizeAction,
41};
42
43use crate::data::{
44    ConnectToSession, FloatingPaneCoordinates, HttpVerb, InputMode, KeyWithModifier,
45    MessageToPlugin, NewPluginArgs, PaneId, PermissionType, PluginCommand,
46};
47use crate::input::actions::Action;
48use crate::input::layout::SplitSize;
49
50use std::collections::BTreeMap;
51use std::convert::TryFrom;
52use std::path::PathBuf;
53
54impl Into<FloatingPaneCoordinates> for ProtobufFloatingPaneCoordinates {
55    fn into(self) -> FloatingPaneCoordinates {
56        FloatingPaneCoordinates {
57            x: self
58                .x
59                .and_then(|x| match ProtobufFixedOrPercent::from_i32(x.r#type) {
60                    Some(ProtobufFixedOrPercent::Percent) => {
61                        Some(SplitSize::Percent(x.value as usize))
62                    },
63                    Some(ProtobufFixedOrPercent::Fixed) => Some(SplitSize::Fixed(x.value as usize)),
64                    None => None,
65                }),
66            y: self
67                .y
68                .and_then(|y| match ProtobufFixedOrPercent::from_i32(y.r#type) {
69                    Some(ProtobufFixedOrPercent::Percent) => {
70                        Some(SplitSize::Percent(y.value as usize))
71                    },
72                    Some(ProtobufFixedOrPercent::Fixed) => Some(SplitSize::Fixed(y.value as usize)),
73                    None => None,
74                }),
75            width: self.width.and_then(|width| {
76                match ProtobufFixedOrPercent::from_i32(width.r#type) {
77                    Some(ProtobufFixedOrPercent::Percent) => {
78                        Some(SplitSize::Percent(width.value as usize))
79                    },
80                    Some(ProtobufFixedOrPercent::Fixed) => {
81                        Some(SplitSize::Fixed(width.value as usize))
82                    },
83                    None => None,
84                }
85            }),
86            height: self.height.and_then(|height| {
87                match ProtobufFixedOrPercent::from_i32(height.r#type) {
88                    Some(ProtobufFixedOrPercent::Percent) => {
89                        Some(SplitSize::Percent(height.value as usize))
90                    },
91                    Some(ProtobufFixedOrPercent::Fixed) => {
92                        Some(SplitSize::Fixed(height.value as usize))
93                    },
94                    None => None,
95                }
96            }),
97            pinned: self.pinned,
98        }
99    }
100}
101
102impl Into<ProtobufFloatingPaneCoordinates> for FloatingPaneCoordinates {
103    fn into(self) -> ProtobufFloatingPaneCoordinates {
104        ProtobufFloatingPaneCoordinates {
105            x: match self.x {
106                Some(SplitSize::Percent(percent)) => Some(ProtobufFixedOrPercentValue {
107                    r#type: ProtobufFixedOrPercent::Percent as i32,
108                    value: percent as u32,
109                }),
110                Some(SplitSize::Fixed(fixed)) => Some(ProtobufFixedOrPercentValue {
111                    r#type: ProtobufFixedOrPercent::Fixed as i32,
112                    value: fixed as u32,
113                }),
114                None => None,
115            },
116            y: match self.y {
117                Some(SplitSize::Percent(percent)) => Some(ProtobufFixedOrPercentValue {
118                    r#type: ProtobufFixedOrPercent::Percent as i32,
119                    value: percent as u32,
120                }),
121                Some(SplitSize::Fixed(fixed)) => Some(ProtobufFixedOrPercentValue {
122                    r#type: ProtobufFixedOrPercent::Fixed as i32,
123                    value: fixed as u32,
124                }),
125                None => None,
126            },
127            width: match self.width {
128                Some(SplitSize::Percent(percent)) => Some(ProtobufFixedOrPercentValue {
129                    r#type: ProtobufFixedOrPercent::Percent as i32,
130                    value: percent as u32,
131                }),
132                Some(SplitSize::Fixed(fixed)) => Some(ProtobufFixedOrPercentValue {
133                    r#type: ProtobufFixedOrPercent::Fixed as i32,
134                    value: fixed as u32,
135                }),
136                None => None,
137            },
138            height: match self.height {
139                Some(SplitSize::Percent(percent)) => Some(ProtobufFixedOrPercentValue {
140                    r#type: ProtobufFixedOrPercent::Percent as i32,
141                    value: percent as u32,
142                }),
143                Some(SplitSize::Fixed(fixed)) => Some(ProtobufFixedOrPercentValue {
144                    r#type: ProtobufFixedOrPercent::Fixed as i32,
145                    value: fixed as u32,
146                }),
147                None => None,
148            },
149            pinned: self.pinned,
150        }
151    }
152}
153
154impl Into<HttpVerb> for ProtobufHttpVerb {
155    fn into(self) -> HttpVerb {
156        match self {
157            ProtobufHttpVerb::Get => HttpVerb::Get,
158            ProtobufHttpVerb::Post => HttpVerb::Post,
159            ProtobufHttpVerb::Put => HttpVerb::Put,
160            ProtobufHttpVerb::Delete => HttpVerb::Delete,
161        }
162    }
163}
164
165impl Into<ProtobufHttpVerb> for HttpVerb {
166    fn into(self) -> ProtobufHttpVerb {
167        match self {
168            HttpVerb::Get => ProtobufHttpVerb::Get,
169            HttpVerb::Post => ProtobufHttpVerb::Post,
170            HttpVerb::Put => ProtobufHttpVerb::Put,
171            HttpVerb::Delete => ProtobufHttpVerb::Delete,
172        }
173    }
174}
175
176impl TryFrom<ProtobufPaneId> for PaneId {
177    type Error = &'static str;
178    fn try_from(protobuf_pane_id: ProtobufPaneId) -> Result<Self, &'static str> {
179        match ProtobufPaneType::from_i32(protobuf_pane_id.pane_type) {
180            Some(ProtobufPaneType::Terminal) => Ok(PaneId::Terminal(protobuf_pane_id.id)),
181            Some(ProtobufPaneType::Plugin) => Ok(PaneId::Plugin(protobuf_pane_id.id)),
182            None => Err("Failed to convert PaneId"),
183        }
184    }
185}
186
187impl TryFrom<PaneId> for ProtobufPaneId {
188    type Error = &'static str;
189    fn try_from(pane_id: PaneId) -> Result<Self, &'static str> {
190        match pane_id {
191            PaneId::Terminal(id) => Ok(ProtobufPaneId {
192                pane_type: ProtobufPaneType::Terminal as i32,
193                id,
194            }),
195            PaneId::Plugin(id) => Ok(ProtobufPaneId {
196                pane_type: ProtobufPaneType::Plugin as i32,
197                id,
198            }),
199        }
200    }
201}
202
203impl TryFrom<(InputMode, KeyWithModifier, Vec<Action>)> for KeyToRebind {
204    type Error = &'static str;
205    fn try_from(
206        key_to_rebind: (InputMode, KeyWithModifier, Vec<Action>),
207    ) -> Result<Self, &'static str> {
208        Ok(KeyToRebind {
209            input_mode: key_to_rebind.0 as i32,
210            key: Some(key_to_rebind.1.try_into()?),
211            actions: key_to_rebind
212                .2
213                .into_iter()
214                .filter_map(|a| a.try_into().ok())
215                .collect(),
216        })
217    }
218}
219
220impl TryFrom<(InputMode, KeyWithModifier)> for KeyToUnbind {
221    type Error = &'static str;
222    fn try_from(key_to_unbind: (InputMode, KeyWithModifier)) -> Result<Self, &'static str> {
223        Ok(KeyToUnbind {
224            input_mode: key_to_unbind.0 as i32,
225            key: Some(key_to_unbind.1.try_into()?),
226        })
227    }
228}
229
230fn key_to_rebind_to_plugin_command_assets(
231    key_to_rebind: KeyToRebind,
232) -> Option<(InputMode, KeyWithModifier, Vec<Action>)> {
233    Some((
234        ProtobufInputMode::from_i32(key_to_rebind.input_mode)?
235            .try_into()
236            .ok()?,
237        key_to_rebind.key?.try_into().ok()?,
238        key_to_rebind
239            .actions
240            .into_iter()
241            .filter_map(|a| a.try_into().ok())
242            .collect(),
243    ))
244}
245
246fn key_to_unbind_to_plugin_command_assets(
247    key_to_unbind: KeyToUnbind,
248) -> Option<(InputMode, KeyWithModifier)> {
249    Some((
250        ProtobufInputMode::from_i32(key_to_unbind.input_mode)?
251            .try_into()
252            .ok()?,
253        key_to_unbind.key?.try_into().ok()?,
254    ))
255}
256
257impl TryFrom<ProtobufPluginCommand> for PluginCommand {
258    type Error = &'static str;
259    fn try_from(protobuf_plugin_command: ProtobufPluginCommand) -> Result<Self, &'static str> {
260        match CommandName::from_i32(protobuf_plugin_command.name) {
261            Some(CommandName::Subscribe) => match protobuf_plugin_command.payload {
262                Some(Payload::SubscribePayload(subscribe_payload)) => {
263                    let protobuf_event_list = subscribe_payload.subscriptions;
264                    match protobuf_event_list {
265                        Some(protobuf_event_list) => {
266                            Ok(PluginCommand::Subscribe(protobuf_event_list.try_into()?))
267                        },
268                        None => Err("malformed subscription event"),
269                    }
270                },
271                _ => Err("Mismatched payload for Subscribe"),
272            },
273            Some(CommandName::Unsubscribe) => match protobuf_plugin_command.payload {
274                Some(Payload::UnsubscribePayload(unsubscribe_payload)) => {
275                    let protobuf_event_list = unsubscribe_payload.subscriptions;
276                    match protobuf_event_list {
277                        Some(protobuf_event_list) => {
278                            Ok(PluginCommand::Unsubscribe(protobuf_event_list.try_into()?))
279                        },
280                        None => Err("malformed unsubscription event"),
281                    }
282                },
283                _ => Err("Mismatched payload for Unsubscribe"),
284            },
285            Some(CommandName::SetSelectable) => match protobuf_plugin_command.payload {
286                Some(Payload::SetSelectablePayload(should_be_selectable)) => {
287                    Ok(PluginCommand::SetSelectable(should_be_selectable))
288                },
289                _ => Err("Mismatched payload for SetSelectable"),
290            },
291            Some(CommandName::GetPluginIds) => {
292                if protobuf_plugin_command.payload.is_some() {
293                    Err("GetPluginIds should not have a payload")
294                } else {
295                    Ok(PluginCommand::GetPluginIds)
296                }
297            },
298            Some(CommandName::GetZellijVersion) => {
299                if protobuf_plugin_command.payload.is_some() {
300                    Err("GetZellijVersion should not have a payload")
301                } else {
302                    Ok(PluginCommand::GetZellijVersion)
303                }
304            },
305            Some(CommandName::OpenFile) => match protobuf_plugin_command.payload {
306                Some(Payload::OpenFilePayload(file_to_open_payload)) => {
307                    match file_to_open_payload.file_to_open {
308                        Some(file_to_open) => {
309                            let context: BTreeMap<String, String> = file_to_open_payload
310                                .context
311                                .into_iter()
312                                .map(|e| (e.name, e.value))
313                                .collect();
314                            Ok(PluginCommand::OpenFile(file_to_open.try_into()?, context))
315                        },
316                        None => Err("Malformed open file payload"),
317                    }
318                },
319                _ => Err("Mismatched payload for OpenFile"),
320            },
321            Some(CommandName::OpenFileFloating) => match protobuf_plugin_command.payload {
322                Some(Payload::OpenFileFloatingPayload(file_to_open_payload)) => {
323                    let floating_pane_coordinates = file_to_open_payload
324                        .floating_pane_coordinates
325                        .map(|f| f.into());
326                    let context: BTreeMap<String, String> = file_to_open_payload
327                        .context
328                        .into_iter()
329                        .map(|e| (e.name, e.value))
330                        .collect();
331                    match file_to_open_payload.file_to_open {
332                        Some(file_to_open) => Ok(PluginCommand::OpenFileFloating(
333                            file_to_open.try_into()?,
334                            floating_pane_coordinates,
335                            context,
336                        )),
337                        None => Err("Malformed open file payload"),
338                    }
339                },
340                _ => Err("Mismatched payload for OpenFileFloating"),
341            },
342            Some(CommandName::OpenTerminal) => match protobuf_plugin_command.payload {
343                Some(Payload::OpenTerminalPayload(file_to_open_payload)) => {
344                    match file_to_open_payload.file_to_open {
345                        Some(file_to_open) => {
346                            Ok(PluginCommand::OpenTerminal(file_to_open.try_into()?))
347                        },
348                        None => Err("Malformed open terminal payload"),
349                    }
350                },
351                _ => Err("Mismatched payload for OpenTerminal"),
352            },
353            Some(CommandName::OpenTerminalFloating) => match protobuf_plugin_command.payload {
354                Some(Payload::OpenTerminalFloatingPayload(file_to_open_payload)) => {
355                    let floating_pane_coordinates = file_to_open_payload
356                        .floating_pane_coordinates
357                        .map(|f| f.into());
358                    match file_to_open_payload.file_to_open {
359                        Some(file_to_open) => Ok(PluginCommand::OpenTerminalFloating(
360                            file_to_open.try_into()?,
361                            floating_pane_coordinates,
362                        )),
363                        None => Err("Malformed open terminal floating payload"),
364                    }
365                },
366                _ => Err("Mismatched payload for OpenTerminalFloating"),
367            },
368            Some(CommandName::OpenCommandPane) => match protobuf_plugin_command.payload {
369                Some(Payload::OpenCommandPanePayload(command_to_run_payload)) => {
370                    match command_to_run_payload.command_to_run {
371                        Some(command_to_run) => {
372                            let context: BTreeMap<String, String> = command_to_run_payload
373                                .context
374                                .into_iter()
375                                .map(|e| (e.name, e.value))
376                                .collect();
377                            Ok(PluginCommand::OpenCommandPane(
378                                command_to_run.try_into()?,
379                                context,
380                            ))
381                        },
382                        None => Err("Malformed open open command pane payload"),
383                    }
384                },
385                _ => Err("Mismatched payload for OpenCommandPane"),
386            },
387            Some(CommandName::OpenCommandPaneFloating) => match protobuf_plugin_command.payload {
388                Some(Payload::OpenCommandPaneFloatingPayload(command_to_run_payload)) => {
389                    let floating_pane_coordinates = command_to_run_payload
390                        .floating_pane_coordinates
391                        .map(|f| f.into());
392                    match command_to_run_payload.command_to_run {
393                        Some(command_to_run) => {
394                            let context: BTreeMap<String, String> = command_to_run_payload
395                                .context
396                                .into_iter()
397                                .map(|e| (e.name, e.value))
398                                .collect();
399                            Ok(PluginCommand::OpenCommandPaneFloating(
400                                command_to_run.try_into()?,
401                                floating_pane_coordinates,
402                                context,
403                            ))
404                        },
405                        None => Err("Malformed open command pane floating payload"),
406                    }
407                },
408                _ => Err("Mismatched payload for OpenCommandPaneFloating"),
409            },
410            Some(CommandName::SwitchTabTo) => match protobuf_plugin_command.payload {
411                Some(Payload::SwitchTabToPayload(switch_to_tab_payload)) => Ok(
412                    PluginCommand::SwitchTabTo(switch_to_tab_payload.tab_index as u32),
413                ),
414                _ => Err("Mismatched payload for SwitchToTab"),
415            },
416            Some(CommandName::SetTimeout) => match protobuf_plugin_command.payload {
417                Some(Payload::SetTimeoutPayload(set_timeout_payload)) => {
418                    Ok(PluginCommand::SetTimeout(set_timeout_payload.seconds))
419                },
420                _ => Err("Mismatched payload for SetTimeout"),
421            },
422            Some(CommandName::ExecCmd) => match protobuf_plugin_command.payload {
423                Some(Payload::ExecCmdPayload(exec_cmd_payload)) => {
424                    Ok(PluginCommand::ExecCmd(exec_cmd_payload.command_line))
425                },
426                _ => Err("Mismatched payload for ExecCmd"),
427            },
428            Some(CommandName::PostMessageTo) => match protobuf_plugin_command.payload {
429                Some(Payload::PostMessageToPayload(post_message_to_payload)) => {
430                    match post_message_to_payload.message {
431                        Some(message) => Ok(PluginCommand::PostMessageTo(message.try_into()?)),
432                        None => Err("Malformed post message to payload"),
433                    }
434                },
435                _ => Err("Mismatched payload for PostMessageTo"),
436            },
437            Some(CommandName::PostMessageToPlugin) => match protobuf_plugin_command.payload {
438                Some(Payload::PostMessageToPluginPayload(post_message_to_payload)) => {
439                    match post_message_to_payload.message {
440                        Some(message) => {
441                            Ok(PluginCommand::PostMessageToPlugin(message.try_into()?))
442                        },
443                        None => Err("Malformed post message to plugin payload"),
444                    }
445                },
446                _ => Err("Mismatched payload for PostMessageToPlugin"),
447            },
448            Some(CommandName::HideSelf) => {
449                if protobuf_plugin_command.payload.is_some() {
450                    return Err("HideSelf should not have a payload");
451                }
452                Ok(PluginCommand::HideSelf)
453            },
454            Some(CommandName::ShowSelf) => match protobuf_plugin_command.payload {
455                Some(Payload::ShowSelfPayload(should_float_if_hidden)) => {
456                    Ok(PluginCommand::ShowSelf(should_float_if_hidden))
457                },
458                _ => Err("Mismatched payload for ShowSelf"),
459            },
460            Some(CommandName::SwitchToMode) => match protobuf_plugin_command.payload {
461                Some(Payload::SwitchToModePayload(switch_to_mode_payload)) => {
462                    match ProtobufInputMode::from_i32(switch_to_mode_payload.input_mode) {
463                        Some(protobuf_input_mode) => {
464                            Ok(PluginCommand::SwitchToMode(protobuf_input_mode.try_into()?))
465                        },
466                        None => Err("Malformed switch to mode payload"),
467                    }
468                },
469                _ => Err("Mismatched payload for SwitchToMode"),
470            },
471            Some(CommandName::NewTabsWithLayout) => match protobuf_plugin_command.payload {
472                Some(Payload::NewTabsWithLayoutPayload(raw_layout)) => {
473                    Ok(PluginCommand::NewTabsWithLayout(raw_layout))
474                },
475                _ => Err("Mismatched payload for NewTabsWithLayout"),
476            },
477            Some(CommandName::NewTab) => match protobuf_plugin_command.payload {
478                Some(Payload::NewTabPayload(protobuf_new_tab_payload)) => {
479                    Ok(PluginCommand::NewTab {
480                        name: protobuf_new_tab_payload.name,
481                        cwd: protobuf_new_tab_payload.cwd,
482                    })
483                },
484                None => Ok(PluginCommand::NewTab {
485                    name: None,
486                    cwd: None,
487                }),
488                _ => Err("Mismatched payload for NewTab"),
489            },
490            Some(CommandName::GoToNextTab) => {
491                if protobuf_plugin_command.payload.is_some() {
492                    return Err("GoToNextTab should not have a payload");
493                }
494                Ok(PluginCommand::GoToNextTab)
495            },
496            Some(CommandName::GoToPreviousTab) => {
497                if protobuf_plugin_command.payload.is_some() {
498                    return Err("GoToPreviousTab should not have a payload");
499                }
500                Ok(PluginCommand::GoToPreviousTab)
501            },
502            Some(CommandName::Resize) => match protobuf_plugin_command.payload {
503                Some(Payload::ResizePayload(resize_payload)) => match resize_payload.resize {
504                    Some(resize) => Ok(PluginCommand::Resize(resize.try_into()?)),
505                    None => Err("Malformed switch resize payload"),
506                },
507                _ => Err("Mismatched payload for Resize"),
508            },
509            Some(CommandName::ResizeWithDirection) => match protobuf_plugin_command.payload {
510                Some(Payload::ResizeWithDirectionPayload(resize_with_direction_payload)) => {
511                    match resize_with_direction_payload.resize {
512                        Some(resize) => Ok(PluginCommand::ResizeWithDirection(resize.try_into()?)),
513                        None => Err("Malformed switch resize payload"),
514                    }
515                },
516                _ => Err("Mismatched payload for Resize"),
517            },
518            Some(CommandName::FocusNextPane) => {
519                if protobuf_plugin_command.payload.is_some() {
520                    return Err("FocusNextPane should not have a payload");
521                }
522                Ok(PluginCommand::FocusNextPane)
523            },
524            Some(CommandName::FocusPreviousPane) => {
525                if protobuf_plugin_command.payload.is_some() {
526                    return Err("FocusPreviousPane should not have a payload");
527                }
528                Ok(PluginCommand::FocusPreviousPane)
529            },
530            Some(CommandName::MoveFocus) => match protobuf_plugin_command.payload {
531                Some(Payload::MoveFocusPayload(move_payload)) => match move_payload.direction {
532                    Some(direction) => Ok(PluginCommand::MoveFocus(direction.try_into()?)),
533                    None => Err("Malformed move focus payload"),
534                },
535                _ => Err("Mismatched payload for MoveFocus"),
536            },
537            Some(CommandName::MoveFocusOrTab) => match protobuf_plugin_command.payload {
538                Some(Payload::MoveFocusOrTabPayload(move_payload)) => {
539                    match move_payload.direction {
540                        Some(direction) => Ok(PluginCommand::MoveFocusOrTab(direction.try_into()?)),
541                        None => Err("Malformed move focus or tab payload"),
542                    }
543                },
544                _ => Err("Mismatched payload for MoveFocusOrTab"),
545            },
546            Some(CommandName::Detach) => {
547                if protobuf_plugin_command.payload.is_some() {
548                    return Err("Detach should not have a payload");
549                }
550                Ok(PluginCommand::Detach)
551            },
552            Some(CommandName::EditScrollback) => {
553                if protobuf_plugin_command.payload.is_some() {
554                    return Err("EditScrollback should not have a payload");
555                }
556                Ok(PluginCommand::EditScrollback)
557            },
558            Some(CommandName::Write) => match protobuf_plugin_command.payload {
559                Some(Payload::WritePayload(bytes)) => Ok(PluginCommand::Write(bytes)),
560                _ => Err("Mismatched payload for Write"),
561            },
562            Some(CommandName::WriteChars) => match protobuf_plugin_command.payload {
563                Some(Payload::WriteCharsPayload(chars)) => Ok(PluginCommand::WriteChars(chars)),
564                _ => Err("Mismatched payload for WriteChars"),
565            },
566            Some(CommandName::ToggleTab) => {
567                if protobuf_plugin_command.payload.is_some() {
568                    return Err("ToggleTab should not have a payload");
569                }
570                Ok(PluginCommand::ToggleTab)
571            },
572            Some(CommandName::MovePane) => {
573                if protobuf_plugin_command.payload.is_some() {
574                    return Err("MovePane should not have a payload");
575                }
576                Ok(PluginCommand::MovePane)
577            },
578            Some(CommandName::MovePaneWithDirection) => match protobuf_plugin_command.payload {
579                Some(Payload::MovePaneWithDirectionPayload(move_payload)) => {
580                    match move_payload.direction {
581                        Some(direction) => {
582                            Ok(PluginCommand::MovePaneWithDirection(direction.try_into()?))
583                        },
584                        None => Err("Malformed MovePaneWithDirection payload"),
585                    }
586                },
587                _ => Err("Mismatched payload for MovePaneWithDirection"),
588            },
589            Some(CommandName::ClearScreen) => {
590                if protobuf_plugin_command.payload.is_some() {
591                    return Err("ClearScreen should not have a payload");
592                }
593                Ok(PluginCommand::ClearScreen)
594            },
595            Some(CommandName::ScrollUp) => {
596                if protobuf_plugin_command.payload.is_some() {
597                    return Err("ScrollUp should not have a payload");
598                }
599                Ok(PluginCommand::ScrollUp)
600            },
601            Some(CommandName::ScrollDown) => {
602                if protobuf_plugin_command.payload.is_some() {
603                    return Err("ScrollDown should not have a payload");
604                }
605                Ok(PluginCommand::ScrollDown)
606            },
607            Some(CommandName::ScrollToTop) => {
608                if protobuf_plugin_command.payload.is_some() {
609                    return Err("ScrollToTop should not have a payload");
610                }
611                Ok(PluginCommand::ScrollToTop)
612            },
613            Some(CommandName::ScrollToBottom) => {
614                if protobuf_plugin_command.payload.is_some() {
615                    return Err("ScrollToBottom should not have a payload");
616                }
617                Ok(PluginCommand::ScrollToBottom)
618            },
619            Some(CommandName::PageScrollUp) => {
620                if protobuf_plugin_command.payload.is_some() {
621                    return Err("PageScrollUp should not have a payload");
622                }
623                Ok(PluginCommand::PageScrollUp)
624            },
625            Some(CommandName::PageScrollDown) => {
626                if protobuf_plugin_command.payload.is_some() {
627                    return Err("PageScrollDown should not have a payload");
628                }
629                Ok(PluginCommand::PageScrollDown)
630            },
631            Some(CommandName::ToggleFocusFullscreen) => {
632                if protobuf_plugin_command.payload.is_some() {
633                    return Err("ToggleFocusFullscreen should not have a payload");
634                }
635                Ok(PluginCommand::ToggleFocusFullscreen)
636            },
637            Some(CommandName::TogglePaneFrames) => {
638                if protobuf_plugin_command.payload.is_some() {
639                    return Err("TogglePaneFrames should not have a payload");
640                }
641                Ok(PluginCommand::TogglePaneFrames)
642            },
643            Some(CommandName::TogglePaneEmbedOrEject) => {
644                if protobuf_plugin_command.payload.is_some() {
645                    return Err("TogglePaneEmbedOrEject should not have a payload");
646                }
647                Ok(PluginCommand::TogglePaneEmbedOrEject)
648            },
649            Some(CommandName::UndoRenamePane) => {
650                if protobuf_plugin_command.payload.is_some() {
651                    return Err("UndoRenamePane should not have a payload");
652                }
653                Ok(PluginCommand::UndoRenamePane)
654            },
655            Some(CommandName::CloseFocus) => {
656                if protobuf_plugin_command.payload.is_some() {
657                    return Err("CloseFocus should not have a payload");
658                }
659                Ok(PluginCommand::CloseFocus)
660            },
661            Some(CommandName::ToggleActiveTabSync) => {
662                if protobuf_plugin_command.payload.is_some() {
663                    return Err("ToggleActiveTabSync should not have a payload");
664                }
665                Ok(PluginCommand::ToggleActiveTabSync)
666            },
667            Some(CommandName::CloseFocusedTab) => {
668                if protobuf_plugin_command.payload.is_some() {
669                    return Err("CloseFocusedTab should not have a payload");
670                }
671                Ok(PluginCommand::CloseFocusedTab)
672            },
673            Some(CommandName::UndoRenameTab) => {
674                if protobuf_plugin_command.payload.is_some() {
675                    return Err("UndoRenameTab should not have a payload");
676                }
677                Ok(PluginCommand::UndoRenameTab)
678            },
679            Some(CommandName::QuitZellij) => {
680                if protobuf_plugin_command.payload.is_some() {
681                    return Err("QuitZellij should not have a payload");
682                }
683                Ok(PluginCommand::QuitZellij)
684            },
685            Some(CommandName::PreviousSwapLayout) => {
686                if protobuf_plugin_command.payload.is_some() {
687                    return Err("PreviousSwapLayout should not have a payload");
688                }
689                Ok(PluginCommand::PreviousSwapLayout)
690            },
691            Some(CommandName::NextSwapLayout) => {
692                if protobuf_plugin_command.payload.is_some() {
693                    return Err("NextSwapLayout should not have a payload");
694                }
695                Ok(PluginCommand::NextSwapLayout)
696            },
697            Some(CommandName::GoToTabName) => match protobuf_plugin_command.payload {
698                Some(Payload::GoToTabNamePayload(tab_name)) => {
699                    Ok(PluginCommand::GoToTabName(tab_name))
700                },
701                _ => Err("Mismatched payload for GoToTabName"),
702            },
703            Some(CommandName::FocusOrCreateTab) => match protobuf_plugin_command.payload {
704                Some(Payload::FocusOrCreateTabPayload(tab_name)) => {
705                    Ok(PluginCommand::FocusOrCreateTab(tab_name))
706                },
707                _ => Err("Mismatched payload for FocusOrCreateTab"),
708            },
709            Some(CommandName::GoToTab) => match protobuf_plugin_command.payload {
710                Some(Payload::GoToTabPayload(tab_index)) => {
711                    Ok(PluginCommand::GoToTab(tab_index as u32))
712                },
713                _ => Err("Mismatched payload for GoToTab"),
714            },
715            Some(CommandName::StartOrReloadPlugin) => match protobuf_plugin_command.payload {
716                Some(Payload::StartOrReloadPluginPayload(url)) => {
717                    Ok(PluginCommand::StartOrReloadPlugin(url))
718                },
719                _ => Err("Mismatched payload for StartOrReloadPlugin"),
720            },
721            Some(CommandName::CloseTerminalPane) => match protobuf_plugin_command.payload {
722                Some(Payload::CloseTerminalPanePayload(pane_id)) => {
723                    Ok(PluginCommand::CloseTerminalPane(pane_id as u32))
724                },
725                _ => Err("Mismatched payload for CloseTerminalPane"),
726            },
727            Some(CommandName::ClosePluginPane) => match protobuf_plugin_command.payload {
728                Some(Payload::ClosePluginPanePayload(pane_id)) => {
729                    Ok(PluginCommand::ClosePluginPane(pane_id as u32))
730                },
731                _ => Err("Mismatched payload for ClosePluginPane"),
732            },
733            Some(CommandName::FocusTerminalPane) => match protobuf_plugin_command.payload {
734                Some(Payload::FocusTerminalPanePayload(payload)) => {
735                    let pane_id = payload.pane_id as u32;
736                    let should_float = payload.should_float;
737                    Ok(PluginCommand::FocusTerminalPane(pane_id, should_float))
738                },
739                _ => Err("Mismatched payload for ClosePluginPane"),
740            },
741            Some(CommandName::FocusPluginPane) => match protobuf_plugin_command.payload {
742                Some(Payload::FocusPluginPanePayload(payload)) => {
743                    let pane_id = payload.pane_id as u32;
744                    let should_float = payload.should_float;
745                    Ok(PluginCommand::FocusPluginPane(pane_id, should_float))
746                },
747                _ => Err("Mismatched payload for ClosePluginPane"),
748            },
749            Some(CommandName::RenameTerminalPane) => match protobuf_plugin_command.payload {
750                Some(Payload::RenameTerminalPanePayload(payload)) => {
751                    let pane_id = payload.id as u32;
752                    let new_name = payload.new_name;
753                    Ok(PluginCommand::RenameTerminalPane(pane_id, new_name))
754                },
755                _ => Err("Mismatched payload for RenameTerminalPane"),
756            },
757            Some(CommandName::RenamePluginPane) => match protobuf_plugin_command.payload {
758                Some(Payload::RenamePluginPanePayload(payload)) => {
759                    let pane_id = payload.id as u32;
760                    let new_name = payload.new_name;
761                    Ok(PluginCommand::RenamePluginPane(pane_id, new_name))
762                },
763                _ => Err("Mismatched payload for RenamePluginPane"),
764            },
765            Some(CommandName::RenameTab) => match protobuf_plugin_command.payload {
766                Some(Payload::RenameTabPayload(payload)) => {
767                    let tab_index = payload.id as u32;
768                    let name = payload.new_name;
769                    Ok(PluginCommand::RenameTab(tab_index, name))
770                },
771                _ => Err("Mismatched payload for RenameTab"),
772            },
773            Some(CommandName::ReportCrash) => match protobuf_plugin_command.payload {
774                Some(Payload::ReportCrashPayload(payload)) => {
775                    Ok(PluginCommand::ReportPanic(payload))
776                },
777                _ => Err("Mismatched payload for ReportCrash"),
778            },
779            Some(CommandName::RequestPluginPermissions) => match protobuf_plugin_command.payload {
780                Some(Payload::RequestPluginPermissionPayload(payload)) => {
781                    Ok(PluginCommand::RequestPluginPermissions(
782                        payload
783                            .permissions
784                            .iter()
785                            .filter_map(|p| ProtobufPermissionType::from_i32(*p))
786                            .filter_map(|p| PermissionType::try_from(p).ok())
787                            .collect(),
788                    ))
789                },
790                _ => Err("Mismatched payload for RequestPluginPermission"),
791            },
792            Some(CommandName::SwitchSession) => match protobuf_plugin_command.payload {
793                Some(Payload::SwitchSessionPayload(payload)) => {
794                    let pane_id = match (payload.pane_id, payload.pane_id_is_plugin) {
795                        (Some(pane_id), Some(is_plugin)) => Some((pane_id, is_plugin)),
796                        (None, None) => None,
797                        _ => {
798                            return Err("Malformed payload for SwitchSession, 'pane_id' and 'is_plugin' must be included together or not at all")
799                        }
800                    };
801                    Ok(PluginCommand::SwitchSession(ConnectToSession {
802                        name: payload.name,
803                        tab_position: payload.tab_position.map(|p| p as usize),
804                        pane_id,
805                        layout: payload.layout.and_then(|l| l.try_into().ok()),
806                        cwd: payload.cwd.map(|c| PathBuf::from(c)),
807                    }))
808                },
809                _ => Err("Mismatched payload for SwitchSession"),
810            },
811            Some(CommandName::OpenTerminalInPlace) => match protobuf_plugin_command.payload {
812                Some(Payload::OpenTerminalInPlacePayload(file_to_open_payload)) => {
813                    match file_to_open_payload.file_to_open {
814                        Some(file_to_open) => {
815                            Ok(PluginCommand::OpenTerminalInPlace(file_to_open.try_into()?))
816                        },
817                        None => Err("Malformed open terminal in-place payload"),
818                    }
819                },
820                _ => Err("Mismatched payload for OpenTerminalInPlace"),
821            },
822            Some(CommandName::OpenFileInPlace) => match protobuf_plugin_command.payload {
823                Some(Payload::OpenFileInPlacePayload(file_to_open_payload)) => {
824                    match file_to_open_payload.file_to_open {
825                        Some(file_to_open) => {
826                            let context: BTreeMap<String, String> = file_to_open_payload
827                                .context
828                                .into_iter()
829                                .map(|e| (e.name, e.value))
830                                .collect();
831                            Ok(PluginCommand::OpenFileInPlace(
832                                file_to_open.try_into()?,
833                                context,
834                            ))
835                        },
836                        None => Err("Malformed open file in place payload"),
837                    }
838                },
839                _ => Err("Mismatched payload for OpenFileInPlace"),
840            },
841            Some(CommandName::OpenCommandInPlace) => match protobuf_plugin_command.payload {
842                Some(Payload::OpenCommandPaneInPlacePayload(command_to_run_payload)) => {
843                    match command_to_run_payload.command_to_run {
844                        Some(command_to_run) => {
845                            let context: BTreeMap<String, String> = command_to_run_payload
846                                .context
847                                .into_iter()
848                                .map(|e| (e.name, e.value))
849                                .collect();
850                            Ok(PluginCommand::OpenCommandPaneInPlace(
851                                command_to_run.try_into()?,
852                                context,
853                            ))
854                        },
855                        None => Err("Malformed open command pane in-place payload"),
856                    }
857                },
858                _ => Err("Mismatched payload for OpenCommandPaneInPlace"),
859            },
860            Some(CommandName::RunCommand) => match protobuf_plugin_command.payload {
861                Some(Payload::RunCommandPayload(run_command_payload)) => {
862                    let env_variables: BTreeMap<String, String> = run_command_payload
863                        .env_variables
864                        .into_iter()
865                        .map(|e| (e.name, e.value))
866                        .collect();
867                    let context: BTreeMap<String, String> = run_command_payload
868                        .context
869                        .into_iter()
870                        .map(|e| (e.name, e.value))
871                        .collect();
872                    Ok(PluginCommand::RunCommand(
873                        run_command_payload.command_line,
874                        env_variables,
875                        PathBuf::from(run_command_payload.cwd),
876                        context,
877                    ))
878                },
879                _ => Err("Mismatched payload for RunCommand"),
880            },
881            Some(CommandName::WebRequest) => match protobuf_plugin_command.payload {
882                Some(Payload::WebRequestPayload(web_request_payload)) => {
883                    let context: BTreeMap<String, String> = web_request_payload
884                        .context
885                        .into_iter()
886                        .map(|e| (e.name, e.value))
887                        .collect();
888                    let headers: BTreeMap<String, String> = web_request_payload
889                        .headers
890                        .into_iter()
891                        .map(|e| (e.name, e.value))
892                        .collect();
893                    let verb = match ProtobufHttpVerb::from_i32(web_request_payload.verb) {
894                        Some(verb) => verb.into(),
895                        None => {
896                            return Err("Unrecognized http verb");
897                        },
898                    };
899                    Ok(PluginCommand::WebRequest(
900                        web_request_payload.url,
901                        verb,
902                        headers,
903                        web_request_payload.body,
904                        context,
905                    ))
906                },
907                _ => Err("Mismatched payload for WebRequest"),
908            },
909            Some(CommandName::DeleteDeadSession) => match protobuf_plugin_command.payload {
910                Some(Payload::DeleteDeadSessionPayload(dead_session_name)) => {
911                    Ok(PluginCommand::DeleteDeadSession(dead_session_name))
912                },
913                _ => Err("Mismatched payload for DeleteDeadSession"),
914            },
915            Some(CommandName::DeleteAllDeadSessions) => Ok(PluginCommand::DeleteAllDeadSessions),
916            Some(CommandName::RenameSession) => match protobuf_plugin_command.payload {
917                Some(Payload::RenameSessionPayload(new_session_name)) => {
918                    Ok(PluginCommand::RenameSession(new_session_name))
919                },
920                _ => Err("Mismatched payload for RenameSession"),
921            },
922            Some(CommandName::UnblockCliPipeInput) => match protobuf_plugin_command.payload {
923                Some(Payload::UnblockCliPipeInputPayload(pipe_name)) => {
924                    Ok(PluginCommand::UnblockCliPipeInput(pipe_name))
925                },
926                _ => Err("Mismatched payload for UnblockPipeInput"),
927            },
928            Some(CommandName::BlockCliPipeInput) => match protobuf_plugin_command.payload {
929                Some(Payload::BlockCliPipeInputPayload(pipe_name)) => {
930                    Ok(PluginCommand::BlockCliPipeInput(pipe_name))
931                },
932                _ => Err("Mismatched payload for BlockPipeInput"),
933            },
934            Some(CommandName::CliPipeOutput) => match protobuf_plugin_command.payload {
935                Some(Payload::CliPipeOutputPayload(CliPipeOutputPayload { pipe_name, output })) => {
936                    Ok(PluginCommand::CliPipeOutput(pipe_name, output))
937                },
938                _ => Err("Mismatched payload for PipeOutput"),
939            },
940            Some(CommandName::MessageToPlugin) => match protobuf_plugin_command.payload {
941                Some(Payload::MessageToPluginPayload(MessageToPluginPayload {
942                    plugin_url,
943                    plugin_config,
944                    message_name,
945                    message_payload,
946                    message_args,
947                    new_plugin_args,
948                    destination_plugin_id,
949                    floating_pane_coordinates,
950                })) => {
951                    let plugin_config: BTreeMap<String, String> = plugin_config
952                        .into_iter()
953                        .map(|e| (e.name, e.value))
954                        .collect();
955                    let message_args: BTreeMap<String, String> = message_args
956                        .into_iter()
957                        .map(|e| (e.name, e.value))
958                        .collect();
959                    Ok(PluginCommand::MessageToPlugin(MessageToPlugin {
960                        plugin_url,
961                        plugin_config,
962                        message_name,
963                        message_payload,
964                        message_args,
965                        new_plugin_args: new_plugin_args.and_then(|protobuf_new_plugin_args| {
966                            Some(NewPluginArgs {
967                                should_float: protobuf_new_plugin_args.should_float,
968                                pane_id_to_replace: protobuf_new_plugin_args
969                                    .pane_id_to_replace
970                                    .and_then(|p_id| PaneId::try_from(p_id).ok()),
971                                pane_title: protobuf_new_plugin_args.pane_title,
972                                cwd: protobuf_new_plugin_args.cwd.map(|cwd| PathBuf::from(cwd)),
973                                skip_cache: protobuf_new_plugin_args.skip_cache,
974                                should_focus: protobuf_new_plugin_args.should_focus,
975                            })
976                        }),
977                        destination_plugin_id,
978                        floating_pane_coordinates: floating_pane_coordinates
979                            .and_then(|f| f.try_into().ok()),
980                    }))
981                },
982                _ => Err("Mismatched payload for MessageToPlugin"),
983            },
984            Some(CommandName::DisconnectOtherClients) => match protobuf_plugin_command.payload {
985                None => Ok(PluginCommand::DisconnectOtherClients),
986                _ => Err("Mismatched payload for DisconnectOtherClients"),
987            },
988            Some(CommandName::KillSessions) => match protobuf_plugin_command.payload {
989                Some(Payload::KillSessionsPayload(KillSessionsPayload { session_names })) => {
990                    Ok(PluginCommand::KillSessions(session_names))
991                },
992                _ => Err("Mismatched payload for KillSessions"),
993            },
994            Some(CommandName::ScanHostFolder) => match protobuf_plugin_command.payload {
995                Some(Payload::ScanHostFolderPayload(folder_to_scan)) => {
996                    Ok(PluginCommand::ScanHostFolder(PathBuf::from(folder_to_scan)))
997                },
998                _ => Err("Mismatched payload for ScanHostFolder"),
999            },
1000            Some(CommandName::WatchFilesystem) => match protobuf_plugin_command.payload {
1001                Some(_) => Err("WatchFilesystem should have no payload, found a payload"),
1002                None => Ok(PluginCommand::WatchFilesystem),
1003            },
1004            Some(CommandName::DumpSessionLayout) => match protobuf_plugin_command.payload {
1005                Some(_) => Err("DumpSessionLayout should have no payload, found a payload"),
1006                None => Ok(PluginCommand::DumpSessionLayout),
1007            },
1008            Some(CommandName::CloseSelf) => match protobuf_plugin_command.payload {
1009                Some(_) => Err("CloseSelf should have no payload, found a payload"),
1010                None => Ok(PluginCommand::CloseSelf),
1011            },
1012            Some(CommandName::NewTabsWithLayoutInfo) => match protobuf_plugin_command.payload {
1013                Some(Payload::NewTabsWithLayoutInfoPayload(new_tabs_with_layout_info_payload)) => {
1014                    new_tabs_with_layout_info_payload
1015                        .layout_info
1016                        .and_then(|layout_info| {
1017                            Some(PluginCommand::NewTabsWithLayoutInfo(
1018                                layout_info.try_into().ok()?,
1019                            ))
1020                        })
1021                        .ok_or("Failed to parse NewTabsWithLayoutInfo command")
1022                },
1023                _ => Err("Mismatched payload for NewTabsWithLayoutInfo"),
1024            },
1025            Some(CommandName::Reconfigure) => match protobuf_plugin_command.payload {
1026                Some(Payload::ReconfigurePayload(reconfigure_payload)) => {
1027                    Ok(PluginCommand::Reconfigure(
1028                        reconfigure_payload.config,
1029                        reconfigure_payload.write_to_disk,
1030                    ))
1031                },
1032                _ => Err("Mismatched payload for Reconfigure"),
1033            },
1034            Some(CommandName::HidePaneWithId) => match protobuf_plugin_command.payload {
1035                Some(Payload::HidePaneWithIdPayload(hide_pane_with_id_payload)) => {
1036                    let pane_id = hide_pane_with_id_payload
1037                        .pane_id
1038                        .and_then(|p_id| PaneId::try_from(p_id).ok())
1039                        .ok_or("Failed to parse HidePaneWithId command")?;
1040                    Ok(PluginCommand::HidePaneWithId(pane_id))
1041                },
1042                _ => Err("Mismatched payload for HidePaneWithId"),
1043            },
1044            Some(CommandName::ShowPaneWithId) => match protobuf_plugin_command.payload {
1045                Some(Payload::ShowPaneWithIdPayload(show_pane_with_id_payload)) => {
1046                    let pane_id = show_pane_with_id_payload
1047                        .pane_id
1048                        .and_then(|p_id| PaneId::try_from(p_id).ok())
1049                        .ok_or("Failed to parse ShowPaneWithId command")?;
1050                    let should_float_if_hidden = show_pane_with_id_payload.should_float_if_hidden;
1051                    Ok(PluginCommand::ShowPaneWithId(
1052                        pane_id,
1053                        should_float_if_hidden,
1054                    ))
1055                },
1056                _ => Err("Mismatched payload for ShowPaneWithId"),
1057            },
1058            Some(CommandName::OpenCommandPaneBackground) => match protobuf_plugin_command.payload {
1059                Some(Payload::OpenCommandPaneBackgroundPayload(command_to_run_payload)) => {
1060                    match command_to_run_payload.command_to_run {
1061                        Some(command_to_run) => {
1062                            let context: BTreeMap<String, String> = command_to_run_payload
1063                                .context
1064                                .into_iter()
1065                                .map(|e| (e.name, e.value))
1066                                .collect();
1067                            Ok(PluginCommand::OpenCommandPaneBackground(
1068                                command_to_run.try_into()?,
1069                                context,
1070                            ))
1071                        },
1072                        None => Err("Malformed open command pane background payload"),
1073                    }
1074                },
1075                _ => Err("Mismatched payload for OpenCommandPaneBackground"),
1076            },
1077            Some(CommandName::RerunCommandPane) => match protobuf_plugin_command.payload {
1078                Some(Payload::RerunCommandPanePayload(rerun_command_pane_payload)) => Ok(
1079                    PluginCommand::RerunCommandPane(rerun_command_pane_payload.terminal_pane_id),
1080                ),
1081                _ => Err("Mismatched payload for RerunCommandPane"),
1082            },
1083            Some(CommandName::ResizePaneIdWithDirection) => match protobuf_plugin_command.payload {
1084                Some(Payload::ResizePaneIdWithDirectionPayload(resize_with_direction_payload)) => {
1085                    match (
1086                        resize_with_direction_payload.resize,
1087                        resize_with_direction_payload.pane_id,
1088                    ) {
1089                        (Some(resize), Some(pane_id)) => {
1090                            Ok(PluginCommand::ResizePaneIdWithDirection(
1091                                resize.try_into()?,
1092                                pane_id.try_into()?,
1093                            ))
1094                        },
1095                        _ => Err("Malformed resize_pane_with_id payload"),
1096                    }
1097                },
1098                _ => Err("Mismatched payload for Resize"),
1099            },
1100            Some(CommandName::EditScrollbackForPaneWithId) => match protobuf_plugin_command.payload
1101            {
1102                Some(Payload::EditScrollbackForPaneWithIdPayload(
1103                    edit_scrollback_for_pane_with_id_payload,
1104                )) => match edit_scrollback_for_pane_with_id_payload.pane_id {
1105                    Some(pane_id) => Ok(PluginCommand::EditScrollbackForPaneWithId(
1106                        pane_id.try_into()?,
1107                    )),
1108                    _ => Err("Malformed edit_scrollback_for_pane_with_id payload"),
1109                },
1110                _ => Err("Mismatched payload for EditScrollback"),
1111            },
1112            Some(CommandName::WriteToPaneId) => match protobuf_plugin_command.payload {
1113                Some(Payload::WriteToPaneIdPayload(write_to_pane_id_payload)) => {
1114                    match write_to_pane_id_payload.pane_id {
1115                        Some(pane_id) => Ok(PluginCommand::WriteToPaneId(
1116                            write_to_pane_id_payload.bytes_to_write,
1117                            pane_id.try_into()?,
1118                        )),
1119                        _ => Err("Malformed write_to_pane_id payload"),
1120                    }
1121                },
1122                _ => Err("Mismatched payload for WriteToPaneId"),
1123            },
1124            Some(CommandName::WriteCharsToPaneId) => match protobuf_plugin_command.payload {
1125                Some(Payload::WriteCharsToPaneIdPayload(write_chars_to_pane_id_payload)) => {
1126                    match write_chars_to_pane_id_payload.pane_id {
1127                        Some(pane_id) => Ok(PluginCommand::WriteCharsToPaneId(
1128                            write_chars_to_pane_id_payload.chars_to_write,
1129                            pane_id.try_into()?,
1130                        )),
1131                        _ => Err("Malformed write_chars_to_pane_id payload"),
1132                    }
1133                },
1134                _ => Err("Mismatched payload for WriteCharsCharsToPaneId"),
1135            },
1136            Some(CommandName::MovePaneWithPaneId) => match protobuf_plugin_command.payload {
1137                Some(Payload::MovePaneWithPaneIdPayload(move_pane_with_pane_id_payload)) => {
1138                    match move_pane_with_pane_id_payload.pane_id {
1139                        Some(pane_id) => Ok(PluginCommand::MovePaneWithPaneId(pane_id.try_into()?)),
1140                        _ => Err("Malformed move_pane_with_pane_id payload"),
1141                    }
1142                },
1143                _ => Err("Mismatched payload for MovePaneWithPaneId"),
1144            },
1145            Some(CommandName::MovePaneWithPaneIdInDirection) => {
1146                match protobuf_plugin_command.payload {
1147                    Some(Payload::MovePaneWithPaneIdInDirectionPayload(move_payload)) => {
1148                        match (move_payload.direction, move_payload.pane_id) {
1149                            (Some(direction), Some(pane_id)) => {
1150                                Ok(PluginCommand::MovePaneWithPaneIdInDirection(
1151                                    pane_id.try_into()?,
1152                                    direction.try_into()?,
1153                                ))
1154                            },
1155                            _ => Err("Malformed MovePaneWithPaneIdInDirection payload"),
1156                        }
1157                    },
1158                    _ => Err("Mismatched payload for MovePaneWithDirection"),
1159                }
1160            },
1161            Some(CommandName::ClearScreenForPaneId) => match protobuf_plugin_command.payload {
1162                Some(Payload::ClearScreenForPaneIdPayload(clear_screen_for_pane_id_payload)) => {
1163                    match clear_screen_for_pane_id_payload.pane_id {
1164                        Some(pane_id) => {
1165                            Ok(PluginCommand::ClearScreenForPaneId(pane_id.try_into()?))
1166                        },
1167                        _ => Err("Malformed clear_screen_for_pane_id_payload payload"),
1168                    }
1169                },
1170                _ => Err("Mismatched payload for ClearScreenForPaneId"),
1171            },
1172            Some(CommandName::ScrollUpInPaneId) => match protobuf_plugin_command.payload {
1173                Some(Payload::ScrollUpInPaneIdPayload(scroll_up_in_pane_id_payload)) => {
1174                    match scroll_up_in_pane_id_payload.pane_id {
1175                        Some(pane_id) => Ok(PluginCommand::ScrollUpInPaneId(pane_id.try_into()?)),
1176                        _ => Err("Malformed scroll_up_in_pane_id_payload payload"),
1177                    }
1178                },
1179                _ => Err("Mismatched payload for ScrollUpInPaneId"),
1180            },
1181            Some(CommandName::ScrollDownInPaneId) => match protobuf_plugin_command.payload {
1182                Some(Payload::ScrollDownInPaneIdPayload(scroll_down_in_pane_id_payload)) => {
1183                    match scroll_down_in_pane_id_payload.pane_id {
1184                        Some(pane_id) => Ok(PluginCommand::ScrollDownInPaneId(pane_id.try_into()?)),
1185                        _ => Err("Malformed scroll_down_in_pane_id_payload payload"),
1186                    }
1187                },
1188                _ => Err("Mismatched payload for ScrollDownInPaneId"),
1189            },
1190            Some(CommandName::ScrollToTopInPaneId) => match protobuf_plugin_command.payload {
1191                Some(Payload::ScrollToTopInPaneIdPayload(scroll_to_top_in_pane_id_payload)) => {
1192                    match scroll_to_top_in_pane_id_payload.pane_id {
1193                        Some(pane_id) => {
1194                            Ok(PluginCommand::ScrollToTopInPaneId(pane_id.try_into()?))
1195                        },
1196                        _ => Err("Malformed scroll_to_top_in_pane_id_payload payload"),
1197                    }
1198                },
1199                _ => Err("Mismatched payload for ScrollToTopInPaneId"),
1200            },
1201            Some(CommandName::ScrollToBottomInPaneId) => match protobuf_plugin_command.payload {
1202                Some(Payload::ScrollToBottomInPaneIdPayload(
1203                    scroll_to_bottom_in_pane_id_payload,
1204                )) => match scroll_to_bottom_in_pane_id_payload.pane_id {
1205                    Some(pane_id) => Ok(PluginCommand::ScrollToBottomInPaneId(pane_id.try_into()?)),
1206                    _ => Err("Malformed scroll_to_bottom_in_pane_id_payload payload"),
1207                },
1208                _ => Err("Mismatched payload for ScrollToBottomInPaneId"),
1209            },
1210            Some(CommandName::PageScrollUpInPaneId) => match protobuf_plugin_command.payload {
1211                Some(Payload::PageScrollUpInPaneIdPayload(page_scroll_up_in_pane_id_payload)) => {
1212                    match page_scroll_up_in_pane_id_payload.pane_id {
1213                        Some(pane_id) => {
1214                            Ok(PluginCommand::PageScrollUpInPaneId(pane_id.try_into()?))
1215                        },
1216                        _ => Err("Malformed page_scroll_up_in_pane_id_payload payload"),
1217                    }
1218                },
1219                _ => Err("Mismatched payload for PageScrollUpInPaneId"),
1220            },
1221            Some(CommandName::PageScrollDownInPaneId) => match protobuf_plugin_command.payload {
1222                Some(Payload::PageScrollDownInPaneIdPayload(
1223                    page_scroll_down_in_pane_id_payload,
1224                )) => match page_scroll_down_in_pane_id_payload.pane_id {
1225                    Some(pane_id) => Ok(PluginCommand::PageScrollDownInPaneId(pane_id.try_into()?)),
1226                    _ => Err("Malformed page_scroll_down_in_pane_id_payload payload"),
1227                },
1228                _ => Err("Mismatched payload for PageScrollDownInPaneId"),
1229            },
1230            Some(CommandName::TogglePaneIdFullscreen) => match protobuf_plugin_command.payload {
1231                Some(Payload::TogglePaneIdFullscreenPayload(toggle_pane_id_fullscreen_payload)) => {
1232                    match toggle_pane_id_fullscreen_payload.pane_id {
1233                        Some(pane_id) => {
1234                            Ok(PluginCommand::TogglePaneIdFullscreen(pane_id.try_into()?))
1235                        },
1236                        _ => Err("Malformed toggle_pane_id_fullscreen_payload payload"),
1237                    }
1238                },
1239                _ => Err("Mismatched payload for TogglePaneIdFullscreen"),
1240            },
1241            Some(CommandName::TogglePaneEmbedOrEjectForPaneId) => {
1242                match protobuf_plugin_command.payload {
1243                    Some(Payload::TogglePaneEmbedOrEjectForPaneIdPayload(
1244                        toggle_pane_embed_or_eject_payload,
1245                    )) => match toggle_pane_embed_or_eject_payload.pane_id {
1246                        Some(pane_id) => Ok(PluginCommand::TogglePaneEmbedOrEjectForPaneId(
1247                            pane_id.try_into()?,
1248                        )),
1249                        _ => Err("Malformed toggle_pane_embed_or_eject_payload payload"),
1250                    },
1251                    _ => Err("Mismatched payload for TogglePaneEmbedOrEjectForPaneId"),
1252                }
1253            },
1254            Some(CommandName::CloseTabWithIndex) => match protobuf_plugin_command.payload {
1255                Some(Payload::CloseTabWithIndexPayload(close_tab_index_payload)) => Ok(
1256                    PluginCommand::CloseTabWithIndex(close_tab_index_payload.tab_index as usize),
1257                ),
1258                _ => Err("Mismatched payload for CloseTabWithIndex"),
1259            },
1260            Some(CommandName::BreakPanesToNewTab) => match protobuf_plugin_command.payload {
1261                Some(Payload::BreakPanesToNewTabPayload(break_panes_to_new_tab_payload)) => {
1262                    Ok(PluginCommand::BreakPanesToNewTab(
1263                        break_panes_to_new_tab_payload
1264                            .pane_ids
1265                            .into_iter()
1266                            .filter_map(|p_id| p_id.try_into().ok())
1267                            .collect(),
1268                        break_panes_to_new_tab_payload.new_tab_name,
1269                        break_panes_to_new_tab_payload.should_change_focus_to_new_tab,
1270                    ))
1271                },
1272                _ => Err("Mismatched payload for BreakPanesToNewTab"),
1273            },
1274            Some(CommandName::BreakPanesToTabWithIndex) => match protobuf_plugin_command.payload {
1275                Some(Payload::BreakPanesToTabWithIndexPayload(
1276                    break_panes_to_tab_with_index_payload,
1277                )) => Ok(PluginCommand::BreakPanesToTabWithIndex(
1278                    break_panes_to_tab_with_index_payload
1279                        .pane_ids
1280                        .into_iter()
1281                        .filter_map(|p_id| p_id.try_into().ok())
1282                        .collect(),
1283                    break_panes_to_tab_with_index_payload.tab_index as usize,
1284                    break_panes_to_tab_with_index_payload.should_change_focus_to_target_tab,
1285                )),
1286                _ => Err("Mismatched payload for BreakPanesToTabWithIndex"),
1287            },
1288            Some(CommandName::ReloadPlugin) => match protobuf_plugin_command.payload {
1289                Some(Payload::ReloadPluginPayload(reload_plugin_payload)) => {
1290                    Ok(PluginCommand::ReloadPlugin(reload_plugin_payload.plugin_id))
1291                },
1292                _ => Err("Mismatched payload for ReloadPlugin"),
1293            },
1294            Some(CommandName::LoadNewPlugin) => match protobuf_plugin_command.payload {
1295                Some(Payload::LoadNewPluginPayload(load_new_plugin_payload)) => {
1296                    Ok(PluginCommand::LoadNewPlugin {
1297                        url: load_new_plugin_payload.plugin_url,
1298                        config: load_new_plugin_payload
1299                            .plugin_config
1300                            .into_iter()
1301                            .map(|e| (e.name, e.value))
1302                            .collect(),
1303                        load_in_background: load_new_plugin_payload
1304                            .should_load_plugin_in_background,
1305                        skip_plugin_cache: load_new_plugin_payload.should_skip_plugin_cache,
1306                    })
1307                },
1308                _ => Err("Mismatched payload for LoadNewPlugin"),
1309            },
1310            Some(CommandName::RebindKeys) => match protobuf_plugin_command.payload {
1311                Some(Payload::RebindKeysPayload(rebind_keys_payload)) => {
1312                    Ok(PluginCommand::RebindKeys {
1313                        keys_to_rebind: rebind_keys_payload
1314                            .keys_to_rebind
1315                            .into_iter()
1316                            .filter_map(|k| key_to_rebind_to_plugin_command_assets(k))
1317                            .collect(),
1318                        keys_to_unbind: rebind_keys_payload
1319                            .keys_to_unbind
1320                            .into_iter()
1321                            .filter_map(|k| key_to_unbind_to_plugin_command_assets(k))
1322                            .collect(),
1323                        write_config_to_disk: rebind_keys_payload.write_config_to_disk,
1324                    })
1325                },
1326                _ => Err("Mismatched payload for RebindKeys"),
1327            },
1328            Some(CommandName::ListClients) => match protobuf_plugin_command.payload {
1329                Some(_) => Err("ListClients should have no payload, found a payload"),
1330                None => Ok(PluginCommand::ListClients),
1331            },
1332            Some(CommandName::ChangeHostFolder) => match protobuf_plugin_command.payload {
1333                Some(Payload::ChangeHostFolderPayload(change_host_folder_payload)) => {
1334                    Ok(PluginCommand::ChangeHostFolder(PathBuf::from(
1335                        change_host_folder_payload.new_host_folder,
1336                    )))
1337                },
1338                _ => Err("Mismatched payload for ChangeHostFolder"),
1339            },
1340            Some(CommandName::SetFloatingPanePinned) => match protobuf_plugin_command.payload {
1341                Some(Payload::SetFloatingPanePinnedPayload(set_floating_pane_pinned_payload)) => {
1342                    match set_floating_pane_pinned_payload
1343                        .pane_id
1344                        .and_then(|p| p.try_into().ok())
1345                    {
1346                        Some(pane_id) => Ok(PluginCommand::SetFloatingPanePinned(
1347                            pane_id,
1348                            set_floating_pane_pinned_payload.should_be_pinned,
1349                        )),
1350                        None => Err("PaneId not found!"),
1351                    }
1352                },
1353                _ => Err("Mismatched payload for SetFloatingPanePinned"),
1354            },
1355            Some(CommandName::StackPanes) => match protobuf_plugin_command.payload {
1356                Some(Payload::StackPanesPayload(stack_panes_payload)) => {
1357                    Ok(PluginCommand::StackPanes(
1358                        stack_panes_payload
1359                            .pane_ids
1360                            .into_iter()
1361                            .filter_map(|p_id| p_id.try_into().ok())
1362                            .collect(),
1363                    ))
1364                },
1365                _ => Err("Mismatched payload for StackPanes"),
1366            },
1367            Some(CommandName::ChangeFloatingPanesCoordinates) => {
1368                match protobuf_plugin_command.payload {
1369                    Some(Payload::ChangeFloatingPanesCoordinatesPayload(
1370                        change_floating_panes_coordinates_payload,
1371                    )) => Ok(PluginCommand::ChangeFloatingPanesCoordinates(
1372                        change_floating_panes_coordinates_payload
1373                            .pane_ids_and_floating_panes_coordinates
1374                            .into_iter()
1375                            .filter_map(|p_id_a_fp| {
1376                                let pane_id: PaneId = p_id_a_fp.pane_id?.try_into().ok()?;
1377                                let floating_pane_coordinates: FloatingPaneCoordinates =
1378                                    p_id_a_fp.floating_pane_coordinates?.try_into().ok()?;
1379                                Some((pane_id, floating_pane_coordinates))
1380                            })
1381                            .collect(),
1382                    )),
1383                    _ => Err("Mismatched payload for ChangeFloatingPanesCoordinates"),
1384                }
1385            },
1386            Some(CommandName::OpenCommandPaneNearPlugin) => match protobuf_plugin_command.payload {
1387                Some(Payload::OpenCommandPaneNearPluginPayload(command_to_run_payload)) => {
1388                    match command_to_run_payload.command_to_run {
1389                        Some(command_to_run) => {
1390                            let context: BTreeMap<String, String> = command_to_run_payload
1391                                .context
1392                                .into_iter()
1393                                .map(|e| (e.name, e.value))
1394                                .collect();
1395                            Ok(PluginCommand::OpenCommandPaneNearPlugin(
1396                                command_to_run.try_into()?,
1397                                context,
1398                            ))
1399                        },
1400                        None => Err("Malformed open command pane near plugin payload"),
1401                    }
1402                },
1403                _ => Err("Mismatched payload for OpenCommandPaneNearPlugin"),
1404            },
1405            Some(CommandName::OpenTerminalNearPlugin) => match protobuf_plugin_command.payload {
1406                Some(Payload::OpenTerminalNearPluginPayload(open_terminal_near_plugin_payload)) => {
1407                    match open_terminal_near_plugin_payload.file_to_open {
1408                        Some(file_to_open) => Ok(PluginCommand::OpenTerminalNearPlugin(
1409                            file_to_open.try_into()?,
1410                        )),
1411                        None => Err("Malformed open terminal near plugin payload"),
1412                    }
1413                },
1414                _ => Err("Mismatched payload for OpenTerminalNearPluginPayload"),
1415            },
1416            Some(CommandName::OpenTerminalFloatingNearPlugin) => match protobuf_plugin_command
1417                .payload
1418            {
1419                Some(Payload::OpenTerminalFloatingNearPluginPayload(
1420                    open_terminal_floating_near_plugin_payload,
1421                )) => {
1422                    let floating_pane_coordinates = open_terminal_floating_near_plugin_payload
1423                        .floating_pane_coordinates
1424                        .map(|f| f.into());
1425                    match open_terminal_floating_near_plugin_payload.file_to_open {
1426                        Some(file_to_open) => Ok(PluginCommand::OpenTerminalFloatingNearPlugin(
1427                            file_to_open.try_into()?,
1428                            floating_pane_coordinates,
1429                        )),
1430                        None => Err("Malformed open terminal floating near plugin payload"),
1431                    }
1432                },
1433                _ => Err("Mismatched payload for OpenTerminalFloatingNearPlugin"),
1434            },
1435            Some(CommandName::OpenTerminalInPlaceOfPlugin) => match protobuf_plugin_command.payload
1436            {
1437                Some(Payload::OpenTerminalInPlaceOfPluginPayload(
1438                    open_terminal_in_place_of_plugin_payload,
1439                )) => match open_terminal_in_place_of_plugin_payload.file_to_open {
1440                    Some(file_to_open) => Ok(PluginCommand::OpenTerminalInPlaceOfPlugin(
1441                        file_to_open.try_into()?,
1442                        open_terminal_in_place_of_plugin_payload.close_plugin_after_replace,
1443                    )),
1444                    None => Err("Malformed open terminal in place of plugin payload"),
1445                },
1446                _ => Err("Mismatched payload for OpenTerminalInPlaceOfPlugin"),
1447            },
1448            Some(CommandName::OpenCommandPaneFloatingNearPlugin) => {
1449                match protobuf_plugin_command.payload {
1450                    Some(Payload::OpenCommandPaneFloatingNearPluginPayload(
1451                        open_command_pane_floating_near_plugin,
1452                    )) => match open_command_pane_floating_near_plugin.command_to_run {
1453                        Some(command_to_run) => {
1454                            let context: BTreeMap<String, String> =
1455                                open_command_pane_floating_near_plugin
1456                                    .context
1457                                    .into_iter()
1458                                    .map(|e| (e.name, e.value))
1459                                    .collect();
1460                            let floating_pane_coordinates = open_command_pane_floating_near_plugin
1461                                .floating_pane_coordinates
1462                                .map(|f| f.into());
1463                            Ok(PluginCommand::OpenCommandPaneFloatingNearPlugin(
1464                                command_to_run.try_into()?,
1465                                floating_pane_coordinates,
1466                                context,
1467                            ))
1468                        },
1469                        None => Err("Malformed open command pane floating near plugin payload"),
1470                    },
1471                    _ => Err("Mismatched payload for OpenCommandPaneFloatingNearPlugin"),
1472                }
1473            },
1474            Some(CommandName::OpenCommandPaneInPlaceOfPlugin) => {
1475                match protobuf_plugin_command.payload {
1476                    Some(Payload::OpenCommandPaneInPlaceOfPluginPayload(
1477                        open_command_pane_in_place_of_plugin_payload,
1478                    )) => match open_command_pane_in_place_of_plugin_payload.command_to_run {
1479                        Some(command_to_run) => {
1480                            let context: BTreeMap<String, String> =
1481                                open_command_pane_in_place_of_plugin_payload
1482                                    .context
1483                                    .into_iter()
1484                                    .map(|e| (e.name, e.value))
1485                                    .collect();
1486                            Ok(PluginCommand::OpenCommandPaneInPlaceOfPlugin(
1487                                command_to_run.try_into()?,
1488                                open_command_pane_in_place_of_plugin_payload
1489                                    .close_plugin_after_replace,
1490                                context,
1491                            ))
1492                        },
1493                        None => Err("Malformed open command pane in place of plugin payload"),
1494                    },
1495                    _ => Err("Mismatched payload for OpenCommandPaneInPlaceOfPlugin"),
1496                }
1497            },
1498            Some(CommandName::OpenFileNearPlugin) => match protobuf_plugin_command.payload {
1499                Some(Payload::OpenFileNearPluginPayload(file_to_open_payload)) => {
1500                    match file_to_open_payload.file_to_open {
1501                        Some(file_to_open) => {
1502                            let context: BTreeMap<String, String> = file_to_open_payload
1503                                .context
1504                                .into_iter()
1505                                .map(|e| (e.name, e.value))
1506                                .collect();
1507                            Ok(PluginCommand::OpenFileNearPlugin(
1508                                file_to_open.try_into()?,
1509                                context,
1510                            ))
1511                        },
1512                        None => Err("Malformed open file payload"),
1513                    }
1514                },
1515                _ => Err("Mismatched payload for OpenFileNearPlugin"),
1516            },
1517            Some(CommandName::OpenFileFloatingNearPlugin) => {
1518                match protobuf_plugin_command.payload {
1519                    Some(Payload::OpenFileFloatingNearPluginPayload(file_to_open_payload)) => {
1520                        let floating_pane_coordinates = file_to_open_payload
1521                            .floating_pane_coordinates
1522                            .map(|f| f.into());
1523                        let context: BTreeMap<String, String> = file_to_open_payload
1524                            .context
1525                            .into_iter()
1526                            .map(|e| (e.name, e.value))
1527                            .collect();
1528                        match file_to_open_payload.file_to_open {
1529                            Some(file_to_open) => Ok(PluginCommand::OpenFileFloatingNearPlugin(
1530                                file_to_open.try_into()?,
1531                                floating_pane_coordinates,
1532                                context,
1533                            )),
1534                            None => Err("Malformed open file payload"),
1535                        }
1536                    },
1537                    _ => Err("Mismatched payload for OpenFileFloatingNearPlugin"),
1538                }
1539            },
1540            Some(CommandName::OpenFileInPlaceOfPlugin) => match protobuf_plugin_command.payload {
1541                Some(Payload::OpenFileInPlaceOfPluginPayload(file_to_open_payload)) => {
1542                    match file_to_open_payload.file_to_open {
1543                        Some(file_to_open) => {
1544                            let context: BTreeMap<String, String> = file_to_open_payload
1545                                .context
1546                                .into_iter()
1547                                .map(|e| (e.name, e.value))
1548                                .collect();
1549                            Ok(PluginCommand::OpenFileInPlaceOfPlugin(
1550                                file_to_open.try_into()?,
1551                                file_to_open_payload.close_plugin_after_replace,
1552                                context,
1553                            ))
1554                        },
1555                        None => Err("Malformed open file in place payload"),
1556                    }
1557                },
1558                _ => Err("Mismatched payload for OpenFileInPlaceOfPlugin"),
1559            },
1560            Some(CommandName::StartWebServer) => {
1561                if protobuf_plugin_command.payload.is_some() {
1562                    Err("StartWebServer should not have a payload")
1563                } else {
1564                    Ok(PluginCommand::StartWebServer)
1565                }
1566            },
1567            Some(CommandName::StopWebServer) => {
1568                if protobuf_plugin_command.payload.is_some() {
1569                    Err("StopWebServer should not have a payload")
1570                } else {
1571                    Ok(PluginCommand::StopWebServer)
1572                }
1573            },
1574            Some(CommandName::QueryWebServerStatus) => {
1575                if protobuf_plugin_command.payload.is_some() {
1576                    Err("QueryWebServerStatus should not have a payload")
1577                } else {
1578                    Ok(PluginCommand::QueryWebServerStatus)
1579                }
1580            },
1581            Some(CommandName::GroupAndUngroupPanes) => match protobuf_plugin_command.payload {
1582                Some(Payload::GroupAndUngroupPanesPayload(group_and_ungroup_panes_payload)) => {
1583                    Ok(PluginCommand::GroupAndUngroupPanes(
1584                        group_and_ungroup_panes_payload
1585                            .pane_ids_to_group
1586                            .into_iter()
1587                            .filter_map(|p| p.try_into().ok())
1588                            .collect(),
1589                        group_and_ungroup_panes_payload
1590                            .pane_ids_to_ungroup
1591                            .into_iter()
1592                            .filter_map(|p| p.try_into().ok())
1593                            .collect(),
1594                        group_and_ungroup_panes_payload.for_all_clients,
1595                    ))
1596                },
1597                _ => Err("Mismatched payload for GroupAndUngroupPanes"),
1598            },
1599            Some(CommandName::HighlightAndUnhighlightPanes) => {
1600                match protobuf_plugin_command.payload {
1601                    Some(Payload::HighlightAndUnhighlightPanesPayload(
1602                        highlight_and_unhighlight_panes_payload,
1603                    )) => Ok(PluginCommand::HighlightAndUnhighlightPanes(
1604                        highlight_and_unhighlight_panes_payload
1605                            .pane_ids_to_highlight
1606                            .into_iter()
1607                            .filter_map(|p| p.try_into().ok())
1608                            .collect(),
1609                        highlight_and_unhighlight_panes_payload
1610                            .pane_ids_to_unhighlight
1611                            .into_iter()
1612                            .filter_map(|p| p.try_into().ok())
1613                            .collect(),
1614                    )),
1615                    _ => Err("Mismatched payload for HighlightAndUnhighlightPanes"),
1616                }
1617            },
1618            Some(CommandName::CloseMultiplePanes) => match protobuf_plugin_command.payload {
1619                Some(Payload::CloseMultiplePanesPayload(close_multiple_panes_payload)) => {
1620                    Ok(PluginCommand::CloseMultiplePanes(
1621                        close_multiple_panes_payload
1622                            .pane_ids
1623                            .into_iter()
1624                            .filter_map(|p| p.try_into().ok())
1625                            .collect(),
1626                    ))
1627                },
1628                _ => Err("Mismatched payload for CloseMultiplePanes"),
1629            },
1630            Some(CommandName::FloatMultiplePanes) => match protobuf_plugin_command.payload {
1631                Some(Payload::FloatMultiplePanesPayload(float_multiple_panes_payload)) => {
1632                    Ok(PluginCommand::FloatMultiplePanes(
1633                        float_multiple_panes_payload
1634                            .pane_ids
1635                            .into_iter()
1636                            .filter_map(|p| p.try_into().ok())
1637                            .collect(),
1638                    ))
1639                },
1640                _ => Err("Mismatched payload for FloatMultiplePanes"),
1641            },
1642            Some(CommandName::EmbedMultiplePanes) => match protobuf_plugin_command.payload {
1643                Some(Payload::EmbedMultiplePanesPayload(embed_multiple_panes_payload)) => {
1644                    Ok(PluginCommand::EmbedMultiplePanes(
1645                        embed_multiple_panes_payload
1646                            .pane_ids
1647                            .into_iter()
1648                            .filter_map(|p| p.try_into().ok())
1649                            .collect(),
1650                    ))
1651                },
1652                _ => Err("Mismatched payload for EmbedMultiplePanes"),
1653            },
1654            Some(CommandName::ShareCurrentSession) => {
1655                if protobuf_plugin_command.payload.is_some() {
1656                    Err("ShareCurrentSession should not have a payload")
1657                } else {
1658                    Ok(PluginCommand::ShareCurrentSession)
1659                }
1660            },
1661            Some(CommandName::StopSharingCurrentSession) => {
1662                if protobuf_plugin_command.payload.is_some() {
1663                    Err("StopSharingCurrentSession should not have a payload")
1664                } else {
1665                    Ok(PluginCommand::StopSharingCurrentSession)
1666                }
1667            },
1668            Some(CommandName::SetSelfMouseSelectionSupport) => {
1669                match protobuf_plugin_command.payload {
1670                    Some(Payload::SetSelfMouseSelectionSupportPayload(
1671                        set_self_mouse_selection_support_payload,
1672                    )) => Ok(PluginCommand::SetSelfMouseSelectionSupport(
1673                        set_self_mouse_selection_support_payload.support_mouse_selection,
1674                    )),
1675                    _ => Err("SetSelfMouseSelectionSupport requires a payload"),
1676                }
1677            },
1678            Some(CommandName::GenerateWebLoginToken) => match protobuf_plugin_command.payload {
1679                Some(Payload::GenerateWebLoginTokenPayload(generate_web_login_token_payload)) => {
1680                    Ok(PluginCommand::GenerateWebLoginToken(
1681                        generate_web_login_token_payload.token_label,
1682                    ))
1683                },
1684                _ => Err("GenerateWebLoginToken requires a payload"),
1685            },
1686            Some(CommandName::RevokeWebLoginToken) => match protobuf_plugin_command.payload {
1687                Some(Payload::RevokeWebLoginTokenPayload(revoke_web_login_token_payload)) => Ok(
1688                    PluginCommand::RevokeWebLoginToken(revoke_web_login_token_payload.token_label),
1689                ),
1690                _ => Err("RevokeWebLoginToken requires a payload"),
1691            },
1692            Some(CommandName::ListWebLoginTokens) => {
1693                if protobuf_plugin_command.payload.is_some() {
1694                    Err("ListWebLoginTokens should not have a payload")
1695                } else {
1696                    Ok(PluginCommand::ListWebLoginTokens)
1697                }
1698            },
1699            Some(CommandName::RevokeAllWebLoginTokens) => {
1700                if protobuf_plugin_command.payload.is_some() {
1701                    Err("RevokeAllWebLoginTokens should not have a payload")
1702                } else {
1703                    Ok(PluginCommand::RevokeAllWebLoginTokens)
1704                }
1705            },
1706            Some(CommandName::RenameWebLoginToken) => match protobuf_plugin_command.payload {
1707                Some(Payload::RenameWebLoginTokenPayload(rename_web_login_token_payload)) => {
1708                    Ok(PluginCommand::RenameWebLoginToken(
1709                        rename_web_login_token_payload.old_name,
1710                        rename_web_login_token_payload.new_name,
1711                    ))
1712                },
1713                _ => Err("RenameWebLoginToken requires a payload"),
1714            },
1715            Some(CommandName::InterceptKeyPresses) => match protobuf_plugin_command.payload {
1716                Some(_) => Err("InterceptKeyPresses should have no payload, found a payload"),
1717                None => Ok(PluginCommand::InterceptKeyPresses),
1718            },
1719            Some(CommandName::ClearKeyPressesIntercepts) => match protobuf_plugin_command.payload {
1720                Some(_) => Err("ClearKeyPressesIntercepts should have no payload, found a payload"),
1721                None => Ok(PluginCommand::ClearKeyPressesIntercepts),
1722            },
1723            Some(CommandName::ReplacePaneWithExistingPane) => match protobuf_plugin_command.payload
1724            {
1725                Some(Payload::ReplacePaneWithExistingPanePayload(
1726                    replace_pane_with_other_pane_payload,
1727                )) => Ok(PluginCommand::ReplacePaneWithExistingPane(
1728                    replace_pane_with_other_pane_payload
1729                        .pane_id_to_replace
1730                        .and_then(|p_id| PaneId::try_from(p_id).ok())
1731                        .ok_or("Failed to parse ReplacePaneWithExistingPanePayload")?,
1732                    replace_pane_with_other_pane_payload
1733                        .existing_pane_id
1734                        .and_then(|p_id| PaneId::try_from(p_id).ok())
1735                        .ok_or("Failed to parse ReplacePaneWithExistingPanePayload")?,
1736                )),
1737                _ => Err("Mismatched payload for ReplacePaneWithExistingPane"),
1738            },
1739            None => Err("Unrecognized plugin command"),
1740        }
1741    }
1742}
1743
1744impl TryFrom<PluginCommand> for ProtobufPluginCommand {
1745    type Error = &'static str;
1746    fn try_from(plugin_command: PluginCommand) -> Result<Self, &'static str> {
1747        match plugin_command {
1748            PluginCommand::Subscribe(subscriptions) => {
1749                let subscriptions: ProtobufEventNameList = subscriptions.try_into()?;
1750                Ok(ProtobufPluginCommand {
1751                    name: CommandName::Subscribe as i32,
1752                    payload: Some(Payload::SubscribePayload(SubscribePayload {
1753                        subscriptions: Some(subscriptions),
1754                    })),
1755                })
1756            },
1757            PluginCommand::Unsubscribe(subscriptions) => {
1758                let subscriptions: ProtobufEventNameList = subscriptions.try_into()?;
1759                Ok(ProtobufPluginCommand {
1760                    name: CommandName::Unsubscribe as i32,
1761                    payload: Some(Payload::UnsubscribePayload(UnsubscribePayload {
1762                        subscriptions: Some(subscriptions),
1763                    })),
1764                })
1765            },
1766            PluginCommand::SetSelectable(should_be_selectable) => Ok(ProtobufPluginCommand {
1767                name: CommandName::SetSelectable as i32,
1768                payload: Some(Payload::SetSelectablePayload(should_be_selectable)),
1769            }),
1770            PluginCommand::GetPluginIds => Ok(ProtobufPluginCommand {
1771                name: CommandName::GetPluginIds as i32,
1772                payload: None,
1773            }),
1774            PluginCommand::GetZellijVersion => Ok(ProtobufPluginCommand {
1775                name: CommandName::GetZellijVersion as i32,
1776                payload: None,
1777            }),
1778            PluginCommand::OpenFile(file_to_open, context) => Ok(ProtobufPluginCommand {
1779                name: CommandName::OpenFile as i32,
1780                payload: Some(Payload::OpenFilePayload(OpenFilePayload {
1781                    file_to_open: Some(file_to_open.try_into()?),
1782                    floating_pane_coordinates: None,
1783                    context: context
1784                        .into_iter()
1785                        .map(|(name, value)| ContextItem { name, value })
1786                        .collect(),
1787                })),
1788            }),
1789            PluginCommand::OpenFileFloating(file_to_open, floating_pane_coordinates, context) => {
1790                Ok(ProtobufPluginCommand {
1791                    name: CommandName::OpenFileFloating as i32,
1792                    payload: Some(Payload::OpenFileFloatingPayload(OpenFilePayload {
1793                        file_to_open: Some(file_to_open.try_into()?),
1794                        floating_pane_coordinates: floating_pane_coordinates.map(|f| f.into()),
1795                        context: context
1796                            .into_iter()
1797                            .map(|(name, value)| ContextItem { name, value })
1798                            .collect(),
1799                    })),
1800                })
1801            },
1802            PluginCommand::OpenTerminal(cwd) => Ok(ProtobufPluginCommand {
1803                name: CommandName::OpenTerminal as i32,
1804                payload: Some(Payload::OpenTerminalPayload(OpenFilePayload {
1805                    file_to_open: Some(cwd.try_into()?),
1806                    floating_pane_coordinates: None,
1807                    context: vec![], // will be added in the future
1808                })),
1809            }),
1810            PluginCommand::OpenTerminalFloating(cwd, floating_pane_coordinates) => {
1811                Ok(ProtobufPluginCommand {
1812                    name: CommandName::OpenTerminalFloating as i32,
1813                    payload: Some(Payload::OpenTerminalFloatingPayload(OpenFilePayload {
1814                        file_to_open: Some(cwd.try_into()?),
1815                        floating_pane_coordinates: floating_pane_coordinates.map(|f| f.into()),
1816                        context: vec![], // will be added in the future
1817                    })),
1818                })
1819            },
1820            PluginCommand::OpenCommandPane(command_to_run, context) => {
1821                let context: Vec<_> = context
1822                    .into_iter()
1823                    .map(|(name, value)| ContextItem { name, value })
1824                    .collect();
1825                Ok(ProtobufPluginCommand {
1826                    name: CommandName::OpenCommandPane as i32,
1827                    payload: Some(Payload::OpenCommandPanePayload(OpenCommandPanePayload {
1828                        command_to_run: Some(command_to_run.try_into()?),
1829                        floating_pane_coordinates: None,
1830                        context,
1831                    })),
1832                })
1833            },
1834            PluginCommand::OpenCommandPaneFloating(
1835                command_to_run,
1836                floating_pane_coordinates,
1837                context,
1838            ) => {
1839                let context: Vec<_> = context
1840                    .into_iter()
1841                    .map(|(name, value)| ContextItem { name, value })
1842                    .collect();
1843                Ok(ProtobufPluginCommand {
1844                    name: CommandName::OpenCommandPaneFloating as i32,
1845                    payload: Some(Payload::OpenCommandPaneFloatingPayload(
1846                        OpenCommandPanePayload {
1847                            command_to_run: Some(command_to_run.try_into()?),
1848                            floating_pane_coordinates: floating_pane_coordinates.map(|f| f.into()),
1849                            context,
1850                        },
1851                    )),
1852                })
1853            },
1854            PluginCommand::SwitchTabTo(tab_index) => Ok(ProtobufPluginCommand {
1855                name: CommandName::SwitchTabTo as i32,
1856                payload: Some(Payload::SwitchTabToPayload(SwitchTabToPayload {
1857                    tab_index: tab_index,
1858                })),
1859            }),
1860            PluginCommand::SetTimeout(seconds) => Ok(ProtobufPluginCommand {
1861                name: CommandName::SetTimeout as i32,
1862                payload: Some(Payload::SetTimeoutPayload(SetTimeoutPayload { seconds })),
1863            }),
1864            PluginCommand::ExecCmd(command_line) => Ok(ProtobufPluginCommand {
1865                name: CommandName::ExecCmd as i32,
1866                payload: Some(Payload::ExecCmdPayload(ExecCmdPayload { command_line })),
1867            }),
1868            PluginCommand::PostMessageTo(plugin_message) => Ok(ProtobufPluginCommand {
1869                name: CommandName::PostMessageTo as i32,
1870                payload: Some(Payload::PostMessageToPayload(PluginMessagePayload {
1871                    message: Some(plugin_message.try_into()?),
1872                })),
1873            }),
1874            PluginCommand::PostMessageToPlugin(plugin_message) => Ok(ProtobufPluginCommand {
1875                name: CommandName::PostMessageToPlugin as i32,
1876                payload: Some(Payload::PostMessageToPluginPayload(PluginMessagePayload {
1877                    message: Some(plugin_message.try_into()?),
1878                })),
1879            }),
1880            PluginCommand::HideSelf => Ok(ProtobufPluginCommand {
1881                name: CommandName::HideSelf as i32,
1882                payload: None,
1883            }),
1884            PluginCommand::ShowSelf(should_float_if_hidden) => Ok(ProtobufPluginCommand {
1885                name: CommandName::ShowSelf as i32,
1886                payload: Some(Payload::ShowSelfPayload(should_float_if_hidden)),
1887            }),
1888            PluginCommand::SwitchToMode(input_mode) => Ok(ProtobufPluginCommand {
1889                name: CommandName::SwitchToMode as i32,
1890                payload: Some(Payload::SwitchToModePayload(SwitchToModePayload {
1891                    input_mode: ProtobufInputMode::try_from(input_mode)? as i32,
1892                })),
1893            }),
1894            PluginCommand::NewTabsWithLayout(raw_layout) => Ok(ProtobufPluginCommand {
1895                name: CommandName::NewTabsWithLayout as i32,
1896                payload: Some(Payload::NewTabsWithLayoutPayload(raw_layout)),
1897            }),
1898            PluginCommand::NewTab { name, cwd } => Ok(ProtobufPluginCommand {
1899                name: CommandName::NewTab as i32,
1900                payload: Some(Payload::NewTabPayload(NewTabPayload { name, cwd })),
1901            }),
1902            PluginCommand::GoToNextTab => Ok(ProtobufPluginCommand {
1903                name: CommandName::GoToNextTab as i32,
1904                payload: None,
1905            }),
1906            PluginCommand::GoToPreviousTab => Ok(ProtobufPluginCommand {
1907                name: CommandName::GoToPreviousTab as i32,
1908                payload: None,
1909            }),
1910            PluginCommand::Resize(resize) => Ok(ProtobufPluginCommand {
1911                name: CommandName::Resize as i32,
1912                payload: Some(Payload::ResizePayload(ResizePayload {
1913                    resize: Some(resize.try_into()?),
1914                })),
1915            }),
1916            PluginCommand::ResizeWithDirection(resize) => Ok(ProtobufPluginCommand {
1917                name: CommandName::ResizeWithDirection as i32,
1918                payload: Some(Payload::ResizeWithDirectionPayload(ResizePayload {
1919                    resize: Some(resize.try_into()?),
1920                })),
1921            }),
1922            PluginCommand::FocusNextPane => Ok(ProtobufPluginCommand {
1923                name: CommandName::FocusNextPane as i32,
1924                payload: None,
1925            }),
1926            PluginCommand::FocusPreviousPane => Ok(ProtobufPluginCommand {
1927                name: CommandName::FocusPreviousPane as i32,
1928                payload: None,
1929            }),
1930            PluginCommand::MoveFocus(direction) => Ok(ProtobufPluginCommand {
1931                name: CommandName::MoveFocus as i32,
1932                payload: Some(Payload::MoveFocusPayload(MovePayload {
1933                    direction: Some(direction.try_into()?),
1934                })),
1935            }),
1936            PluginCommand::MoveFocusOrTab(direction) => Ok(ProtobufPluginCommand {
1937                name: CommandName::MoveFocusOrTab as i32,
1938                payload: Some(Payload::MoveFocusOrTabPayload(MovePayload {
1939                    direction: Some(direction.try_into()?),
1940                })),
1941            }),
1942            PluginCommand::Detach => Ok(ProtobufPluginCommand {
1943                name: CommandName::Detach as i32,
1944                payload: None,
1945            }),
1946            PluginCommand::EditScrollback => Ok(ProtobufPluginCommand {
1947                name: CommandName::EditScrollback as i32,
1948                payload: None,
1949            }),
1950            PluginCommand::Write(bytes) => Ok(ProtobufPluginCommand {
1951                name: CommandName::Write as i32,
1952                payload: Some(Payload::WritePayload(bytes)),
1953            }),
1954            PluginCommand::WriteChars(chars) => Ok(ProtobufPluginCommand {
1955                name: CommandName::WriteChars as i32,
1956                payload: Some(Payload::WriteCharsPayload(chars)),
1957            }),
1958            PluginCommand::ToggleTab => Ok(ProtobufPluginCommand {
1959                name: CommandName::ToggleTab as i32,
1960                payload: None,
1961            }),
1962            PluginCommand::MovePane => Ok(ProtobufPluginCommand {
1963                name: CommandName::MovePane as i32,
1964                payload: None,
1965            }),
1966            PluginCommand::MovePaneWithDirection(direction) => Ok(ProtobufPluginCommand {
1967                name: CommandName::MovePaneWithDirection as i32,
1968                payload: Some(Payload::MovePaneWithDirectionPayload(MovePayload {
1969                    direction: Some(direction.try_into()?),
1970                })),
1971            }),
1972            PluginCommand::ClearScreen => Ok(ProtobufPluginCommand {
1973                name: CommandName::ClearScreen as i32,
1974                payload: None,
1975            }),
1976            PluginCommand::ScrollUp => Ok(ProtobufPluginCommand {
1977                name: CommandName::ScrollUp as i32,
1978                payload: None,
1979            }),
1980            PluginCommand::ScrollDown => Ok(ProtobufPluginCommand {
1981                name: CommandName::ScrollDown as i32,
1982                payload: None,
1983            }),
1984            PluginCommand::ScrollToTop => Ok(ProtobufPluginCommand {
1985                name: CommandName::ScrollToTop as i32,
1986                payload: None,
1987            }),
1988            PluginCommand::ScrollToBottom => Ok(ProtobufPluginCommand {
1989                name: CommandName::ScrollToBottom as i32,
1990                payload: None,
1991            }),
1992            PluginCommand::PageScrollUp => Ok(ProtobufPluginCommand {
1993                name: CommandName::PageScrollUp as i32,
1994                payload: None,
1995            }),
1996            PluginCommand::PageScrollDown => Ok(ProtobufPluginCommand {
1997                name: CommandName::PageScrollDown as i32,
1998                payload: None,
1999            }),
2000            PluginCommand::ToggleFocusFullscreen => Ok(ProtobufPluginCommand {
2001                name: CommandName::ToggleFocusFullscreen as i32,
2002                payload: None,
2003            }),
2004            PluginCommand::TogglePaneFrames => Ok(ProtobufPluginCommand {
2005                name: CommandName::TogglePaneFrames as i32,
2006                payload: None,
2007            }),
2008            PluginCommand::TogglePaneEmbedOrEject => Ok(ProtobufPluginCommand {
2009                name: CommandName::TogglePaneEmbedOrEject as i32,
2010                payload: None,
2011            }),
2012            PluginCommand::UndoRenamePane => Ok(ProtobufPluginCommand {
2013                name: CommandName::UndoRenamePane as i32,
2014                payload: None,
2015            }),
2016            PluginCommand::CloseFocus => Ok(ProtobufPluginCommand {
2017                name: CommandName::CloseFocus as i32,
2018                payload: None,
2019            }),
2020            PluginCommand::ToggleActiveTabSync => Ok(ProtobufPluginCommand {
2021                name: CommandName::ToggleActiveTabSync as i32,
2022                payload: None,
2023            }),
2024            PluginCommand::CloseFocusedTab => Ok(ProtobufPluginCommand {
2025                name: CommandName::CloseFocusedTab as i32,
2026                payload: None,
2027            }),
2028            PluginCommand::UndoRenameTab => Ok(ProtobufPluginCommand {
2029                name: CommandName::UndoRenameTab as i32,
2030                payload: None,
2031            }),
2032            PluginCommand::QuitZellij => Ok(ProtobufPluginCommand {
2033                name: CommandName::QuitZellij as i32,
2034                payload: None,
2035            }),
2036            PluginCommand::PreviousSwapLayout => Ok(ProtobufPluginCommand {
2037                name: CommandName::PreviousSwapLayout as i32,
2038                payload: None,
2039            }),
2040            PluginCommand::NextSwapLayout => Ok(ProtobufPluginCommand {
2041                name: CommandName::NextSwapLayout as i32,
2042                payload: None,
2043            }),
2044            PluginCommand::GoToTabName(tab_name) => Ok(ProtobufPluginCommand {
2045                name: CommandName::GoToTabName as i32,
2046                payload: Some(Payload::GoToTabNamePayload(tab_name)),
2047            }),
2048            PluginCommand::FocusOrCreateTab(tab_name) => Ok(ProtobufPluginCommand {
2049                name: CommandName::FocusOrCreateTab as i32,
2050                payload: Some(Payload::FocusOrCreateTabPayload(tab_name)),
2051            }),
2052            PluginCommand::GoToTab(tab_index) => Ok(ProtobufPluginCommand {
2053                name: CommandName::GoToTab as i32,
2054                payload: Some(Payload::GoToTabPayload(tab_index)),
2055            }),
2056            PluginCommand::StartOrReloadPlugin(url) => Ok(ProtobufPluginCommand {
2057                name: CommandName::StartOrReloadPlugin as i32,
2058                payload: Some(Payload::StartOrReloadPluginPayload(url)),
2059            }),
2060            PluginCommand::CloseTerminalPane(pane_id) => Ok(ProtobufPluginCommand {
2061                name: CommandName::CloseTerminalPane as i32,
2062                payload: Some(Payload::CloseTerminalPanePayload(pane_id)),
2063            }),
2064            PluginCommand::ClosePluginPane(pane_id) => Ok(ProtobufPluginCommand {
2065                name: CommandName::ClosePluginPane as i32,
2066                payload: Some(Payload::ClosePluginPanePayload(pane_id)),
2067            }),
2068            PluginCommand::FocusTerminalPane(pane_id, should_float_if_hidden) => {
2069                Ok(ProtobufPluginCommand {
2070                    name: CommandName::FocusTerminalPane as i32,
2071                    payload: Some(Payload::FocusTerminalPanePayload(PaneIdAndShouldFloat {
2072                        pane_id: pane_id,
2073                        should_float: should_float_if_hidden,
2074                    })),
2075                })
2076            },
2077            PluginCommand::FocusPluginPane(pane_id, should_float_if_hidden) => {
2078                Ok(ProtobufPluginCommand {
2079                    name: CommandName::FocusPluginPane as i32,
2080                    payload: Some(Payload::FocusPluginPanePayload(PaneIdAndShouldFloat {
2081                        pane_id: pane_id,
2082                        should_float: should_float_if_hidden,
2083                    })),
2084                })
2085            },
2086            PluginCommand::RenameTerminalPane(pane_id, new_name) => Ok(ProtobufPluginCommand {
2087                name: CommandName::RenameTerminalPane as i32,
2088                payload: Some(Payload::RenameTerminalPanePayload(IdAndNewName {
2089                    id: pane_id,
2090                    new_name,
2091                })),
2092            }),
2093            PluginCommand::RenamePluginPane(pane_id, new_name) => Ok(ProtobufPluginCommand {
2094                name: CommandName::RenamePluginPane as i32,
2095                payload: Some(Payload::RenamePluginPanePayload(IdAndNewName {
2096                    id: pane_id,
2097                    new_name,
2098                })),
2099            }),
2100            PluginCommand::RenameTab(tab_index, new_name) => Ok(ProtobufPluginCommand {
2101                name: CommandName::RenameTab as i32,
2102                payload: Some(Payload::RenameTabPayload(IdAndNewName {
2103                    id: tab_index,
2104                    new_name,
2105                })),
2106            }),
2107            PluginCommand::ReportPanic(payload) => Ok(ProtobufPluginCommand {
2108                name: CommandName::ReportCrash as i32,
2109                payload: Some(Payload::ReportCrashPayload(payload)),
2110            }),
2111            PluginCommand::RequestPluginPermissions(permissions) => Ok(ProtobufPluginCommand {
2112                name: CommandName::RequestPluginPermissions as i32,
2113                payload: Some(Payload::RequestPluginPermissionPayload(
2114                    RequestPluginPermissionPayload {
2115                        permissions: permissions
2116                            .iter()
2117                            .filter_map(|p| ProtobufPermissionType::try_from(*p).ok())
2118                            .map(|p| p as i32)
2119                            .collect(),
2120                    },
2121                )),
2122            }),
2123            PluginCommand::SwitchSession(switch_to_session) => Ok(ProtobufPluginCommand {
2124                name: CommandName::SwitchSession as i32,
2125                payload: Some(Payload::SwitchSessionPayload(SwitchSessionPayload {
2126                    name: switch_to_session.name,
2127                    tab_position: switch_to_session.tab_position.map(|t| t as u32),
2128                    pane_id: switch_to_session.pane_id.map(|p| p.0),
2129                    pane_id_is_plugin: switch_to_session.pane_id.map(|p| p.1),
2130                    layout: switch_to_session.layout.and_then(|l| l.try_into().ok()),
2131                    cwd: switch_to_session.cwd.map(|c| c.display().to_string()),
2132                })),
2133            }),
2134            PluginCommand::OpenTerminalInPlace(cwd) => Ok(ProtobufPluginCommand {
2135                name: CommandName::OpenTerminalInPlace as i32,
2136                payload: Some(Payload::OpenTerminalInPlacePayload(OpenFilePayload {
2137                    file_to_open: Some(cwd.try_into()?),
2138                    floating_pane_coordinates: None,
2139                    context: vec![], // will be added in the future
2140                })),
2141            }),
2142            PluginCommand::OpenFileInPlace(file_to_open, context) => Ok(ProtobufPluginCommand {
2143                name: CommandName::OpenFileInPlace as i32,
2144                payload: Some(Payload::OpenFileInPlacePayload(OpenFilePayload {
2145                    file_to_open: Some(file_to_open.try_into()?),
2146                    floating_pane_coordinates: None,
2147                    context: context
2148                        .into_iter()
2149                        .map(|(name, value)| ContextItem { name, value })
2150                        .collect(),
2151                })),
2152            }),
2153            PluginCommand::OpenCommandPaneInPlace(command_to_run, context) => {
2154                let context: Vec<_> = context
2155                    .into_iter()
2156                    .map(|(name, value)| ContextItem { name, value })
2157                    .collect();
2158                Ok(ProtobufPluginCommand {
2159                    name: CommandName::OpenCommandInPlace as i32,
2160                    payload: Some(Payload::OpenCommandPaneInPlacePayload(
2161                        OpenCommandPanePayload {
2162                            command_to_run: Some(command_to_run.try_into()?),
2163                            floating_pane_coordinates: None,
2164                            context,
2165                        },
2166                    )),
2167                })
2168            },
2169            PluginCommand::RunCommand(command_line, env_variables, cwd, context) => {
2170                let env_variables: Vec<_> = env_variables
2171                    .into_iter()
2172                    .map(|(name, value)| EnvVariable { name, value })
2173                    .collect();
2174                let context: Vec<_> = context
2175                    .into_iter()
2176                    .map(|(name, value)| ContextItem { name, value })
2177                    .collect();
2178                let cwd = cwd.display().to_string();
2179                Ok(ProtobufPluginCommand {
2180                    name: CommandName::RunCommand as i32,
2181                    payload: Some(Payload::RunCommandPayload(RunCommandPayload {
2182                        command_line,
2183                        env_variables,
2184                        cwd,
2185                        context,
2186                    })),
2187                })
2188            },
2189            PluginCommand::WebRequest(url, verb, headers, body, context) => {
2190                let context: Vec<_> = context
2191                    .into_iter()
2192                    .map(|(name, value)| ContextItem { name, value })
2193                    .collect();
2194                let headers: Vec<_> = headers
2195                    .into_iter()
2196                    .map(|(name, value)| Header { name, value })
2197                    .collect();
2198                let verb: ProtobufHttpVerb = verb.into();
2199                Ok(ProtobufPluginCommand {
2200                    name: CommandName::WebRequest as i32,
2201                    payload: Some(Payload::WebRequestPayload(WebRequestPayload {
2202                        url,
2203                        verb: verb as i32,
2204                        body,
2205                        headers,
2206                        context,
2207                    })),
2208                })
2209            },
2210            PluginCommand::DeleteDeadSession(dead_session_name) => Ok(ProtobufPluginCommand {
2211                name: CommandName::DeleteDeadSession as i32,
2212                payload: Some(Payload::DeleteDeadSessionPayload(dead_session_name)),
2213            }),
2214            PluginCommand::DeleteAllDeadSessions => Ok(ProtobufPluginCommand {
2215                name: CommandName::DeleteAllDeadSessions as i32,
2216                payload: None,
2217            }),
2218            PluginCommand::RenameSession(new_session_name) => Ok(ProtobufPluginCommand {
2219                name: CommandName::RenameSession as i32,
2220                payload: Some(Payload::RenameSessionPayload(new_session_name)),
2221            }),
2222            PluginCommand::UnblockCliPipeInput(pipe_name) => Ok(ProtobufPluginCommand {
2223                name: CommandName::UnblockCliPipeInput as i32,
2224                payload: Some(Payload::UnblockCliPipeInputPayload(pipe_name)),
2225            }),
2226            PluginCommand::BlockCliPipeInput(pipe_name) => Ok(ProtobufPluginCommand {
2227                name: CommandName::BlockCliPipeInput as i32,
2228                payload: Some(Payload::BlockCliPipeInputPayload(pipe_name)),
2229            }),
2230            PluginCommand::CliPipeOutput(pipe_name, output) => Ok(ProtobufPluginCommand {
2231                name: CommandName::CliPipeOutput as i32,
2232                payload: Some(Payload::CliPipeOutputPayload(CliPipeOutputPayload {
2233                    pipe_name,
2234                    output,
2235                })),
2236            }),
2237            PluginCommand::MessageToPlugin(message_to_plugin) => {
2238                let plugin_config: Vec<_> = message_to_plugin
2239                    .plugin_config
2240                    .into_iter()
2241                    .map(|(name, value)| ContextItem { name, value })
2242                    .collect();
2243                let message_args: Vec<_> = message_to_plugin
2244                    .message_args
2245                    .into_iter()
2246                    .map(|(name, value)| ContextItem { name, value })
2247                    .collect();
2248                Ok(ProtobufPluginCommand {
2249                    name: CommandName::MessageToPlugin as i32,
2250                    payload: Some(Payload::MessageToPluginPayload(MessageToPluginPayload {
2251                        plugin_url: message_to_plugin.plugin_url,
2252                        plugin_config,
2253                        message_name: message_to_plugin.message_name,
2254                        message_payload: message_to_plugin.message_payload,
2255                        message_args,
2256                        new_plugin_args: message_to_plugin.new_plugin_args.map(|m_t_p| {
2257                            ProtobufNewPluginArgs {
2258                                should_float: m_t_p.should_float,
2259                                pane_id_to_replace: m_t_p
2260                                    .pane_id_to_replace
2261                                    .and_then(|p_id| ProtobufPaneId::try_from(p_id).ok()),
2262                                pane_title: m_t_p.pane_title,
2263                                cwd: m_t_p.cwd.map(|cwd| cwd.display().to_string()),
2264                                skip_cache: m_t_p.skip_cache,
2265                                should_focus: m_t_p.should_focus,
2266                            }
2267                        }),
2268                        destination_plugin_id: message_to_plugin.destination_plugin_id,
2269                        floating_pane_coordinates: message_to_plugin
2270                            .floating_pane_coordinates
2271                            .and_then(|f| f.try_into().ok()),
2272                    })),
2273                })
2274            },
2275            PluginCommand::DisconnectOtherClients => Ok(ProtobufPluginCommand {
2276                name: CommandName::DisconnectOtherClients as i32,
2277                payload: None,
2278            }),
2279            PluginCommand::KillSessions(session_names) => Ok(ProtobufPluginCommand {
2280                name: CommandName::KillSessions as i32,
2281                payload: Some(Payload::KillSessionsPayload(KillSessionsPayload {
2282                    session_names,
2283                })),
2284            }),
2285            PluginCommand::ScanHostFolder(folder_to_scan) => Ok(ProtobufPluginCommand {
2286                name: CommandName::ScanHostFolder as i32,
2287                payload: Some(Payload::ScanHostFolderPayload(
2288                    folder_to_scan.display().to_string(),
2289                )),
2290            }),
2291            PluginCommand::WatchFilesystem => Ok(ProtobufPluginCommand {
2292                name: CommandName::WatchFilesystem as i32,
2293                payload: None,
2294            }),
2295            PluginCommand::DumpSessionLayout => Ok(ProtobufPluginCommand {
2296                name: CommandName::DumpSessionLayout as i32,
2297                payload: None,
2298            }),
2299            PluginCommand::CloseSelf => Ok(ProtobufPluginCommand {
2300                name: CommandName::CloseSelf as i32,
2301                payload: None,
2302            }),
2303            PluginCommand::NewTabsWithLayoutInfo(new_tabs_with_layout_info_payload) => {
2304                Ok(ProtobufPluginCommand {
2305                    name: CommandName::NewTabsWithLayoutInfo as i32,
2306                    payload: Some(Payload::NewTabsWithLayoutInfoPayload(
2307                        NewTabsWithLayoutInfoPayload {
2308                            layout_info: new_tabs_with_layout_info_payload.try_into().ok(),
2309                        },
2310                    )),
2311                })
2312            },
2313            PluginCommand::Reconfigure(config, write_to_disk) => Ok(ProtobufPluginCommand {
2314                name: CommandName::Reconfigure as i32,
2315                payload: Some(Payload::ReconfigurePayload(ReconfigurePayload {
2316                    config,
2317                    write_to_disk,
2318                })),
2319            }),
2320            PluginCommand::HidePaneWithId(pane_id_to_hide) => Ok(ProtobufPluginCommand {
2321                name: CommandName::HidePaneWithId as i32,
2322                payload: Some(Payload::HidePaneWithIdPayload(HidePaneWithIdPayload {
2323                    pane_id: ProtobufPaneId::try_from(pane_id_to_hide).ok(),
2324                })),
2325            }),
2326            PluginCommand::ShowPaneWithId(pane_id_to_show, should_float_if_hidden) => {
2327                Ok(ProtobufPluginCommand {
2328                    name: CommandName::ShowPaneWithId as i32,
2329                    payload: Some(Payload::ShowPaneWithIdPayload(ShowPaneWithIdPayload {
2330                        pane_id: ProtobufPaneId::try_from(pane_id_to_show).ok(),
2331                        should_float_if_hidden,
2332                    })),
2333                })
2334            },
2335            PluginCommand::OpenCommandPaneBackground(command_to_run, context) => {
2336                let context: Vec<_> = context
2337                    .into_iter()
2338                    .map(|(name, value)| ContextItem { name, value })
2339                    .collect();
2340                Ok(ProtobufPluginCommand {
2341                    name: CommandName::OpenCommandPaneBackground as i32,
2342                    payload: Some(Payload::OpenCommandPaneBackgroundPayload(
2343                        OpenCommandPanePayload {
2344                            command_to_run: Some(command_to_run.try_into()?),
2345                            floating_pane_coordinates: None,
2346                            context,
2347                        },
2348                    )),
2349                })
2350            },
2351            PluginCommand::RerunCommandPane(terminal_pane_id) => Ok(ProtobufPluginCommand {
2352                name: CommandName::RerunCommandPane as i32,
2353                payload: Some(Payload::RerunCommandPanePayload(RerunCommandPanePayload {
2354                    terminal_pane_id,
2355                })),
2356            }),
2357            PluginCommand::ResizePaneIdWithDirection(resize, pane_id) => {
2358                Ok(ProtobufPluginCommand {
2359                    name: CommandName::ResizePaneIdWithDirection as i32,
2360                    payload: Some(Payload::ResizePaneIdWithDirectionPayload(
2361                        ResizePaneIdWithDirectionPayload {
2362                            resize: Some(resize.try_into()?),
2363                            pane_id: Some(pane_id.try_into()?),
2364                        },
2365                    )),
2366                })
2367            },
2368            PluginCommand::EditScrollbackForPaneWithId(pane_id) => Ok(ProtobufPluginCommand {
2369                name: CommandName::EditScrollbackForPaneWithId as i32,
2370                payload: Some(Payload::EditScrollbackForPaneWithIdPayload(
2371                    EditScrollbackForPaneWithIdPayload {
2372                        pane_id: Some(pane_id.try_into()?),
2373                    },
2374                )),
2375            }),
2376            PluginCommand::WriteToPaneId(bytes_to_write, pane_id) => Ok(ProtobufPluginCommand {
2377                name: CommandName::WriteToPaneId as i32,
2378                payload: Some(Payload::WriteToPaneIdPayload(WriteToPaneIdPayload {
2379                    bytes_to_write,
2380                    pane_id: Some(pane_id.try_into()?),
2381                })),
2382            }),
2383            PluginCommand::WriteCharsToPaneId(chars_to_write, pane_id) => {
2384                Ok(ProtobufPluginCommand {
2385                    name: CommandName::WriteCharsToPaneId as i32,
2386                    payload: Some(Payload::WriteCharsToPaneIdPayload(
2387                        WriteCharsToPaneIdPayload {
2388                            chars_to_write,
2389                            pane_id: Some(pane_id.try_into()?),
2390                        },
2391                    )),
2392                })
2393            },
2394            PluginCommand::MovePaneWithPaneId(pane_id) => Ok(ProtobufPluginCommand {
2395                name: CommandName::MovePaneWithPaneId as i32,
2396                payload: Some(Payload::MovePaneWithPaneIdPayload(
2397                    MovePaneWithPaneIdPayload {
2398                        pane_id: Some(pane_id.try_into()?),
2399                    },
2400                )),
2401            }),
2402            PluginCommand::MovePaneWithPaneIdInDirection(pane_id, direction) => {
2403                Ok(ProtobufPluginCommand {
2404                    name: CommandName::MovePaneWithPaneIdInDirection as i32,
2405                    payload: Some(Payload::MovePaneWithPaneIdInDirectionPayload(
2406                        MovePaneWithPaneIdInDirectionPayload {
2407                            pane_id: Some(pane_id.try_into()?),
2408                            direction: Some(direction.try_into()?),
2409                        },
2410                    )),
2411                })
2412            },
2413            PluginCommand::ClearScreenForPaneId(pane_id) => Ok(ProtobufPluginCommand {
2414                name: CommandName::ClearScreenForPaneId as i32,
2415                payload: Some(Payload::ClearScreenForPaneIdPayload(
2416                    ClearScreenForPaneIdPayload {
2417                        pane_id: Some(pane_id.try_into()?),
2418                    },
2419                )),
2420            }),
2421            PluginCommand::ScrollUpInPaneId(pane_id) => Ok(ProtobufPluginCommand {
2422                name: CommandName::ScrollUpInPaneId as i32,
2423                payload: Some(Payload::ScrollUpInPaneIdPayload(ScrollUpInPaneIdPayload {
2424                    pane_id: Some(pane_id.try_into()?),
2425                })),
2426            }),
2427            PluginCommand::ScrollDownInPaneId(pane_id) => Ok(ProtobufPluginCommand {
2428                name: CommandName::ScrollDownInPaneId as i32,
2429                payload: Some(Payload::ScrollDownInPaneIdPayload(
2430                    ScrollDownInPaneIdPayload {
2431                        pane_id: Some(pane_id.try_into()?),
2432                    },
2433                )),
2434            }),
2435            PluginCommand::ScrollToTopInPaneId(pane_id) => Ok(ProtobufPluginCommand {
2436                name: CommandName::ScrollToTopInPaneId as i32,
2437                payload: Some(Payload::ScrollToTopInPaneIdPayload(
2438                    ScrollToTopInPaneIdPayload {
2439                        pane_id: Some(pane_id.try_into()?),
2440                    },
2441                )),
2442            }),
2443            PluginCommand::ScrollToBottomInPaneId(pane_id) => Ok(ProtobufPluginCommand {
2444                name: CommandName::ScrollToBottomInPaneId as i32,
2445                payload: Some(Payload::ScrollToBottomInPaneIdPayload(
2446                    ScrollToBottomInPaneIdPayload {
2447                        pane_id: Some(pane_id.try_into()?),
2448                    },
2449                )),
2450            }),
2451            PluginCommand::PageScrollUpInPaneId(pane_id) => Ok(ProtobufPluginCommand {
2452                name: CommandName::PageScrollUpInPaneId as i32,
2453                payload: Some(Payload::PageScrollUpInPaneIdPayload(
2454                    PageScrollUpInPaneIdPayload {
2455                        pane_id: Some(pane_id.try_into()?),
2456                    },
2457                )),
2458            }),
2459            PluginCommand::PageScrollDownInPaneId(pane_id) => Ok(ProtobufPluginCommand {
2460                name: CommandName::PageScrollDownInPaneId as i32,
2461                payload: Some(Payload::PageScrollDownInPaneIdPayload(
2462                    PageScrollDownInPaneIdPayload {
2463                        pane_id: Some(pane_id.try_into()?),
2464                    },
2465                )),
2466            }),
2467            PluginCommand::TogglePaneIdFullscreen(pane_id) => Ok(ProtobufPluginCommand {
2468                name: CommandName::TogglePaneIdFullscreen as i32,
2469                payload: Some(Payload::TogglePaneIdFullscreenPayload(
2470                    TogglePaneIdFullscreenPayload {
2471                        pane_id: Some(pane_id.try_into()?),
2472                    },
2473                )),
2474            }),
2475            PluginCommand::TogglePaneEmbedOrEjectForPaneId(pane_id) => Ok(ProtobufPluginCommand {
2476                name: CommandName::TogglePaneEmbedOrEjectForPaneId as i32,
2477                payload: Some(Payload::TogglePaneEmbedOrEjectForPaneIdPayload(
2478                    TogglePaneEmbedOrEjectForPaneIdPayload {
2479                        pane_id: Some(pane_id.try_into()?),
2480                    },
2481                )),
2482            }),
2483            PluginCommand::CloseTabWithIndex(tab_index) => Ok(ProtobufPluginCommand {
2484                name: CommandName::CloseTabWithIndex as i32,
2485                payload: Some(Payload::CloseTabWithIndexPayload(
2486                    CloseTabWithIndexPayload {
2487                        tab_index: tab_index as u32,
2488                    },
2489                )),
2490            }),
2491            PluginCommand::BreakPanesToNewTab(
2492                pane_ids,
2493                new_tab_name,
2494                should_change_focus_to_new_tab,
2495            ) => Ok(ProtobufPluginCommand {
2496                name: CommandName::BreakPanesToNewTab as i32,
2497                payload: Some(Payload::BreakPanesToNewTabPayload(
2498                    BreakPanesToNewTabPayload {
2499                        pane_ids: pane_ids
2500                            .into_iter()
2501                            .filter_map(|p_id| p_id.try_into().ok())
2502                            .collect(),
2503                        should_change_focus_to_new_tab,
2504                        new_tab_name,
2505                    },
2506                )),
2507            }),
2508            PluginCommand::BreakPanesToTabWithIndex(
2509                pane_ids,
2510                tab_index,
2511                should_change_focus_to_target_tab,
2512            ) => Ok(ProtobufPluginCommand {
2513                name: CommandName::BreakPanesToTabWithIndex as i32,
2514                payload: Some(Payload::BreakPanesToTabWithIndexPayload(
2515                    BreakPanesToTabWithIndexPayload {
2516                        pane_ids: pane_ids
2517                            .into_iter()
2518                            .filter_map(|p_id| p_id.try_into().ok())
2519                            .collect(),
2520                        tab_index: tab_index as u32,
2521                        should_change_focus_to_target_tab,
2522                    },
2523                )),
2524            }),
2525            PluginCommand::ReloadPlugin(plugin_id) => Ok(ProtobufPluginCommand {
2526                name: CommandName::ReloadPlugin as i32,
2527                payload: Some(Payload::ReloadPluginPayload(ReloadPluginPayload {
2528                    plugin_id,
2529                })),
2530            }),
2531            PluginCommand::LoadNewPlugin {
2532                url,
2533                config,
2534                load_in_background,
2535                skip_plugin_cache,
2536            } => Ok(ProtobufPluginCommand {
2537                name: CommandName::LoadNewPlugin as i32,
2538                payload: Some(Payload::LoadNewPluginPayload(LoadNewPluginPayload {
2539                    plugin_url: url,
2540                    plugin_config: config
2541                        .into_iter()
2542                        .map(|(name, value)| ContextItem { name, value })
2543                        .collect(),
2544                    should_skip_plugin_cache: skip_plugin_cache,
2545                    should_load_plugin_in_background: load_in_background,
2546                })),
2547            }),
2548            PluginCommand::RebindKeys {
2549                keys_to_rebind,
2550                keys_to_unbind,
2551                write_config_to_disk,
2552            } => Ok(ProtobufPluginCommand {
2553                name: CommandName::RebindKeys as i32,
2554                payload: Some(Payload::RebindKeysPayload(RebindKeysPayload {
2555                    keys_to_rebind: keys_to_rebind
2556                        .into_iter()
2557                        .filter_map(|k| k.try_into().ok())
2558                        .collect(),
2559                    keys_to_unbind: keys_to_unbind
2560                        .into_iter()
2561                        .filter_map(|k| k.try_into().ok())
2562                        .collect(),
2563                    write_config_to_disk,
2564                })),
2565            }),
2566            PluginCommand::ListClients => Ok(ProtobufPluginCommand {
2567                name: CommandName::ListClients as i32,
2568                payload: None,
2569            }),
2570            PluginCommand::ChangeHostFolder(new_host_folder) => Ok(ProtobufPluginCommand {
2571                name: CommandName::ChangeHostFolder as i32,
2572                payload: Some(Payload::ChangeHostFolderPayload(ChangeHostFolderPayload {
2573                    new_host_folder: new_host_folder.display().to_string(), // TODO: not accurate?
2574                })),
2575            }),
2576            PluginCommand::SetFloatingPanePinned(pane_id, should_be_pinned) => {
2577                Ok(ProtobufPluginCommand {
2578                    name: CommandName::SetFloatingPanePinned as i32,
2579                    payload: Some(Payload::SetFloatingPanePinnedPayload(
2580                        SetFloatingPanePinnedPayload {
2581                            pane_id: pane_id.try_into().ok(),
2582                            should_be_pinned,
2583                        },
2584                    )),
2585                })
2586            },
2587            PluginCommand::StackPanes(pane_ids) => Ok(ProtobufPluginCommand {
2588                name: CommandName::StackPanes as i32,
2589                payload: Some(Payload::StackPanesPayload(StackPanesPayload {
2590                    pane_ids: pane_ids
2591                        .into_iter()
2592                        .filter_map(|p_id| p_id.try_into().ok())
2593                        .collect(),
2594                })),
2595            }),
2596            PluginCommand::ChangeFloatingPanesCoordinates(
2597                pane_ids_and_floating_panes_coordinates,
2598            ) => Ok(ProtobufPluginCommand {
2599                name: CommandName::ChangeFloatingPanesCoordinates as i32,
2600                payload: Some(Payload::ChangeFloatingPanesCoordinatesPayload(
2601                    ChangeFloatingPanesCoordinatesPayload {
2602                        pane_ids_and_floating_panes_coordinates:
2603                            pane_ids_and_floating_panes_coordinates
2604                                .into_iter()
2605                                .filter_map(|(p_id, floating_pane_coordinates)| {
2606                                    Some(PaneIdAndFloatingPaneCoordinates {
2607                                        pane_id: Some(p_id.try_into().ok()?),
2608                                        floating_pane_coordinates: Some(
2609                                            floating_pane_coordinates.try_into().ok()?,
2610                                        ),
2611                                    })
2612                                })
2613                                .collect(),
2614                    },
2615                )),
2616            }),
2617            PluginCommand::OpenCommandPaneNearPlugin(command_to_run, context) => {
2618                let context: Vec<_> = context
2619                    .into_iter()
2620                    .map(|(name, value)| ContextItem { name, value })
2621                    .collect();
2622                Ok(ProtobufPluginCommand {
2623                    name: CommandName::OpenCommandPaneNearPlugin as i32,
2624                    payload: Some(Payload::OpenCommandPaneNearPluginPayload(
2625                        OpenCommandPaneNearPluginPayload {
2626                            command_to_run: Some(command_to_run.try_into()?),
2627                            floating_pane_coordinates: None,
2628                            context,
2629                        },
2630                    )),
2631                })
2632            },
2633            PluginCommand::OpenCommandPaneFloatingNearPlugin(
2634                command_to_run,
2635                floating_pane_coordinates,
2636                context,
2637            ) => {
2638                let context: Vec<_> = context
2639                    .into_iter()
2640                    .map(|(name, value)| ContextItem { name, value })
2641                    .collect();
2642                Ok(ProtobufPluginCommand {
2643                    name: CommandName::OpenCommandPaneFloatingNearPlugin as i32,
2644                    payload: Some(Payload::OpenCommandPaneFloatingNearPluginPayload(
2645                        OpenCommandPaneFloatingNearPluginPayload {
2646                            command_to_run: Some(command_to_run.try_into()?),
2647                            floating_pane_coordinates: floating_pane_coordinates.map(|f| f.into()),
2648                            context,
2649                        },
2650                    )),
2651                })
2652            },
2653            PluginCommand::OpenTerminalNearPlugin(cwd) => Ok(ProtobufPluginCommand {
2654                name: CommandName::OpenTerminalNearPlugin as i32,
2655                payload: Some(Payload::OpenTerminalNearPluginPayload(
2656                    OpenTerminalNearPluginPayload {
2657                        file_to_open: Some(cwd.try_into()?),
2658                        context: vec![], // will be added in the future
2659                    },
2660                )),
2661            }),
2662            PluginCommand::OpenTerminalFloatingNearPlugin(cwd, floating_pane_coordinates) => {
2663                Ok(ProtobufPluginCommand {
2664                    name: CommandName::OpenTerminalFloatingNearPlugin as i32,
2665                    payload: Some(Payload::OpenTerminalFloatingNearPluginPayload(
2666                        OpenTerminalFloatingNearPluginPayload {
2667                            file_to_open: Some(cwd.try_into()?),
2668                            floating_pane_coordinates: floating_pane_coordinates.map(|f| f.into()),
2669                            context: vec![], // will be added in the future
2670                        },
2671                    )),
2672                })
2673            },
2674            PluginCommand::OpenTerminalInPlaceOfPlugin(cwd, close_plugin_after_replace) => {
2675                Ok(ProtobufPluginCommand {
2676                    name: CommandName::OpenTerminalInPlaceOfPlugin as i32,
2677                    payload: Some(Payload::OpenTerminalInPlaceOfPluginPayload(
2678                        OpenTerminalInPlaceOfPluginPayload {
2679                            file_to_open: Some(cwd.try_into()?),
2680                            close_plugin_after_replace,
2681                            context: vec![], // will be added in the future
2682                        },
2683                    )),
2684                })
2685            },
2686            PluginCommand::OpenCommandPaneInPlaceOfPlugin(
2687                command_to_run,
2688                close_plugin_after_replace,
2689                context,
2690            ) => {
2691                let context: Vec<_> = context
2692                    .into_iter()
2693                    .map(|(name, value)| ContextItem { name, value })
2694                    .collect();
2695                Ok(ProtobufPluginCommand {
2696                    name: CommandName::OpenCommandPaneInPlaceOfPlugin as i32,
2697                    payload: Some(Payload::OpenCommandPaneInPlaceOfPluginPayload(
2698                        OpenCommandPaneInPlaceOfPluginPayload {
2699                            command_to_run: Some(command_to_run.try_into()?),
2700                            close_plugin_after_replace,
2701                            context,
2702                        },
2703                    )),
2704                })
2705            },
2706            PluginCommand::OpenFileNearPlugin(file_to_open, context) => Ok(ProtobufPluginCommand {
2707                name: CommandName::OpenFileNearPlugin as i32,
2708                payload: Some(Payload::OpenFileNearPluginPayload(
2709                    OpenFileNearPluginPayload {
2710                        file_to_open: Some(file_to_open.try_into()?),
2711                        floating_pane_coordinates: None,
2712                        context: context
2713                            .into_iter()
2714                            .map(|(name, value)| ContextItem { name, value })
2715                            .collect(),
2716                    },
2717                )),
2718            }),
2719            PluginCommand::OpenFileFloatingNearPlugin(
2720                file_to_open,
2721                floating_pane_coordinates,
2722                context,
2723            ) => Ok(ProtobufPluginCommand {
2724                name: CommandName::OpenFileFloatingNearPlugin as i32,
2725                payload: Some(Payload::OpenFileFloatingNearPluginPayload(
2726                    OpenFileFloatingNearPluginPayload {
2727                        file_to_open: Some(file_to_open.try_into()?),
2728                        floating_pane_coordinates: floating_pane_coordinates.map(|f| f.into()),
2729                        context: context
2730                            .into_iter()
2731                            .map(|(name, value)| ContextItem { name, value })
2732                            .collect(),
2733                    },
2734                )),
2735            }),
2736            PluginCommand::OpenFileInPlaceOfPlugin(
2737                file_to_open,
2738                close_plugin_after_replace,
2739                context,
2740            ) => Ok(ProtobufPluginCommand {
2741                name: CommandName::OpenFileInPlaceOfPlugin as i32,
2742                payload: Some(Payload::OpenFileInPlaceOfPluginPayload(
2743                    OpenFileInPlaceOfPluginPayload {
2744                        file_to_open: Some(file_to_open.try_into()?),
2745                        floating_pane_coordinates: None,
2746                        close_plugin_after_replace,
2747                        context: context
2748                            .into_iter()
2749                            .map(|(name, value)| ContextItem { name, value })
2750                            .collect(),
2751                    },
2752                )),
2753            }),
2754            PluginCommand::GroupAndUngroupPanes(
2755                panes_to_group,
2756                panes_to_ungroup,
2757                for_all_clients,
2758            ) => Ok(ProtobufPluginCommand {
2759                name: CommandName::GroupAndUngroupPanes as i32,
2760                payload: Some(Payload::GroupAndUngroupPanesPayload(
2761                    GroupAndUngroupPanesPayload {
2762                        pane_ids_to_group: panes_to_group
2763                            .iter()
2764                            .filter_map(|&p| p.try_into().ok())
2765                            .collect(),
2766                        pane_ids_to_ungroup: panes_to_ungroup
2767                            .iter()
2768                            .filter_map(|&p| p.try_into().ok())
2769                            .collect(),
2770                        for_all_clients,
2771                    },
2772                )),
2773            }),
2774            PluginCommand::StartWebServer => Ok(ProtobufPluginCommand {
2775                name: CommandName::StartWebServer as i32,
2776                payload: None,
2777            }),
2778            PluginCommand::StopWebServer => Ok(ProtobufPluginCommand {
2779                name: CommandName::StopWebServer as i32,
2780                payload: None,
2781            }),
2782            PluginCommand::QueryWebServerStatus => Ok(ProtobufPluginCommand {
2783                name: CommandName::QueryWebServerStatus as i32,
2784                payload: None,
2785            }),
2786            PluginCommand::HighlightAndUnhighlightPanes(
2787                panes_to_highlight,
2788                panes_to_unhighlight,
2789            ) => Ok(ProtobufPluginCommand {
2790                name: CommandName::HighlightAndUnhighlightPanes as i32,
2791                payload: Some(Payload::HighlightAndUnhighlightPanesPayload(
2792                    HighlightAndUnhighlightPanesPayload {
2793                        pane_ids_to_highlight: panes_to_highlight
2794                            .iter()
2795                            .filter_map(|&p| p.try_into().ok())
2796                            .collect(),
2797                        pane_ids_to_unhighlight: panes_to_unhighlight
2798                            .iter()
2799                            .filter_map(|&p| p.try_into().ok())
2800                            .collect(),
2801                    },
2802                )),
2803            }),
2804            PluginCommand::CloseMultiplePanes(pane_ids) => Ok(ProtobufPluginCommand {
2805                name: CommandName::CloseMultiplePanes as i32,
2806                payload: Some(Payload::CloseMultiplePanesPayload(
2807                    CloseMultiplePanesPayload {
2808                        pane_ids: pane_ids.iter().filter_map(|&p| p.try_into().ok()).collect(),
2809                    },
2810                )),
2811            }),
2812            PluginCommand::FloatMultiplePanes(pane_ids) => Ok(ProtobufPluginCommand {
2813                name: CommandName::FloatMultiplePanes as i32,
2814                payload: Some(Payload::FloatMultiplePanesPayload(
2815                    FloatMultiplePanesPayload {
2816                        pane_ids: pane_ids.iter().filter_map(|&p| p.try_into().ok()).collect(),
2817                    },
2818                )),
2819            }),
2820            PluginCommand::EmbedMultiplePanes(pane_ids) => Ok(ProtobufPluginCommand {
2821                name: CommandName::EmbedMultiplePanes as i32,
2822                payload: Some(Payload::EmbedMultiplePanesPayload(
2823                    EmbedMultiplePanesPayload {
2824                        pane_ids: pane_ids.iter().filter_map(|&p| p.try_into().ok()).collect(),
2825                    },
2826                )),
2827            }),
2828            PluginCommand::ShareCurrentSession => Ok(ProtobufPluginCommand {
2829                name: CommandName::ShareCurrentSession as i32,
2830                payload: None,
2831            }),
2832            PluginCommand::StopSharingCurrentSession => Ok(ProtobufPluginCommand {
2833                name: CommandName::StopSharingCurrentSession as i32,
2834                payload: None,
2835            }),
2836            PluginCommand::SetSelfMouseSelectionSupport(support_mouse_selection) => {
2837                Ok(ProtobufPluginCommand {
2838                    name: CommandName::SetSelfMouseSelectionSupport as i32,
2839                    payload: Some(Payload::SetSelfMouseSelectionSupportPayload(
2840                        SetSelfMouseSelectionSupportPayload {
2841                            support_mouse_selection,
2842                        },
2843                    )),
2844                })
2845            },
2846            PluginCommand::GenerateWebLoginToken(token_label) => Ok(ProtobufPluginCommand {
2847                name: CommandName::GenerateWebLoginToken as i32,
2848                payload: Some(Payload::GenerateWebLoginTokenPayload(
2849                    GenerateWebLoginTokenPayload { token_label },
2850                )),
2851            }),
2852            PluginCommand::RevokeWebLoginToken(token_label) => Ok(ProtobufPluginCommand {
2853                name: CommandName::RevokeWebLoginToken as i32,
2854                payload: Some(Payload::RevokeWebLoginTokenPayload(
2855                    RevokeWebLoginTokenPayload { token_label },
2856                )),
2857            }),
2858            PluginCommand::ListWebLoginTokens => Ok(ProtobufPluginCommand {
2859                name: CommandName::ListWebLoginTokens as i32,
2860                payload: None,
2861            }),
2862            PluginCommand::RevokeAllWebLoginTokens => Ok(ProtobufPluginCommand {
2863                name: CommandName::RevokeAllWebLoginTokens as i32,
2864                payload: None,
2865            }),
2866            PluginCommand::RenameWebLoginToken(old_name, new_name) => Ok(ProtobufPluginCommand {
2867                name: CommandName::RenameWebLoginToken as i32,
2868                payload: Some(Payload::RenameWebLoginTokenPayload(
2869                    RenameWebLoginTokenPayload { old_name, new_name },
2870                )),
2871            }),
2872            PluginCommand::InterceptKeyPresses => Ok(ProtobufPluginCommand {
2873                name: CommandName::InterceptKeyPresses as i32,
2874                payload: None,
2875            }),
2876            PluginCommand::ClearKeyPressesIntercepts => Ok(ProtobufPluginCommand {
2877                name: CommandName::ClearKeyPressesIntercepts as i32,
2878                payload: None,
2879            }),
2880            PluginCommand::ReplacePaneWithExistingPane(pane_id_to_replace, existing_pane_id) => {
2881                Ok(ProtobufPluginCommand {
2882                    name: CommandName::ReplacePaneWithExistingPane as i32,
2883                    payload: Some(Payload::ReplacePaneWithExistingPanePayload(
2884                        ReplacePaneWithExistingPanePayload {
2885                            pane_id_to_replace: ProtobufPaneId::try_from(pane_id_to_replace).ok(),
2886                            existing_pane_id: ProtobufPaneId::try_from(existing_pane_id).ok(),
2887                        },
2888                    )),
2889                })
2890            },
2891        }
2892    }
2893}