Skip to main content

zellij_utils/plugin_api/
plugin_command.rs

1pub use super::generated_api::api::{
2    action::{Action as ProtobufAction, PaneIdAndShouldFloat, SwitchToModePayload},
3    event::{EventNameList as ProtobufEventNameList, Header},
4    input_mode::InputMode as ProtobufInputMode,
5    plugin_command::{
6        break_panes_to_new_tab_response, break_panes_to_tab_with_id_response,
7        break_panes_to_tab_with_index_response, delete_layout_response, dump_layout_response,
8        dump_session_layout_response, edit_layout_response, focus_or_create_tab_response,
9        get_focused_pane_info_response, get_pane_cwd_response, get_pane_pid_response,
10        get_pane_running_command_response, hide_floating_panes_response,
11        highlight_style::Style as ProtobufHighlightStyleVariant, new_tab_response,
12        parse_layout_response, plugin_command::Payload, rename_layout_response,
13        save_layout_response, save_session_response, show_floating_panes_response,
14        BreakPanesToNewTabPayload,
15        BreakPanesToNewTabResponse as ProtobufBreakPanesToNewTabResponse,
16        BreakPanesToTabWithIdPayload,
17        BreakPanesToTabWithIdResponse as ProtobufBreakPanesToTabWithIdResponse,
18        BreakPanesToTabWithIndexPayload,
19        BreakPanesToTabWithIndexResponse as ProtobufBreakPanesToTabWithIndexResponse,
20        ChangeFloatingPanesCoordinatesPayload, ChangeHostFolderPayload, ClearPaneHighlightsPayload,
21        ClearScreenForPaneIdPayload, CliPipeOutputPayload, CloseMultiplePanesPayload,
22        CloseTabWithIdPayload, CloseTabWithIndexPayload, CommandName, ContextItem,
23        CopyToClipboardPayload, CreateTokenResponse as ProtobufCreateTokenResponse,
24        CreateTokenResponse, CurrentSessionLastSavedTimePayload,
25        CurrentSessionLastSavedTimeResponse as ProtobufCurrentSessionLastSavedTimeResponse,
26        CursorPosition, CustomIndexHighlight as ProtobufCustomIndexHighlight,
27        CustomRgbHighlight as ProtobufCustomRgbHighlight, DeleteLayoutPayload,
28        DeleteLayoutResponse as ProtobufDeleteLayoutResponse, DumpLayoutPayload,
29        DumpLayoutResponse as ProtobufDumpLayoutResponse, DumpSessionLayoutPayload,
30        DumpSessionLayoutResponse as ProtobufDumpSessionLayoutResponse, EditLayoutPayload,
31        EditLayoutResponse as ProtobufEditLayoutResponse, EditScrollbackForPaneWithIdPayload,
32        EmbedMultiplePanesPayload, EnvVariable, ExecCmdPayload,
33        FixedOrPercent as ProtobufFixedOrPercent,
34        FixedOrPercentValue as ProtobufFixedOrPercentValue, FloatMultiplePanesPayload,
35        FloatingPaneCoordinates as ProtobufFloatingPaneCoordinates,
36        FocusOrCreateTabResponse as ProtobufFocusOrCreateTabResponse, FocusedPaneInfo,
37        GenerateRandomNamePayload,
38        GenerateRandomNameResponse as ProtobufGenerateRandomNameResponse,
39        GenerateWebLoginTokenPayload, GetFocusedPaneInfoPayload,
40        GetFocusedPaneInfoResponse as ProtobufGetFocusedPaneInfoResponse, GetLayoutDirPayload,
41        GetLayoutDirResponse as ProtobufGetLayoutDirResponse,
42        GetPaneCwdPayload as ProtobufGetPaneCwdPayload,
43        GetPaneCwdResponse as ProtobufGetPaneCwdResponse, GetPaneInfoPayload,
44        GetPaneInfoResponse as ProtobufGetPaneInfoResponse, GetPanePidPayload,
45        GetPanePidResponse as ProtobufGetPanePidResponse,
46        GetPaneRunningCommandPayload as ProtobufGetPaneRunningCommandPayload,
47        GetPaneRunningCommandResponse as ProtobufGetPaneRunningCommandResponse,
48        GetPaneScrollbackPayload,
49        GetSessionEnvironmentVariablesPayload as ProtobufGetSessionEnvironmentVariablesPayload,
50        GetSessionEnvironmentVariablesResponse as ProtobufGetSessionEnvironmentVariablesResponse,
51        GetTabInfoPayload, GetTabInfoResponse as ProtobufGetTabInfoResponse, GoToTabWithIdPayload,
52        GroupAndUngroupPanesPayload, HideFloatingPanesPayload as ProtobufHideFloatingPanesPayload,
53        HideFloatingPanesResponse as ProtobufHideFloatingPanesResponse, HidePaneWithIdPayload,
54        HighlightAndUnhighlightPanesPayload, HighlightLayer as ProtobufHighlightLayer,
55        HighlightStyle as ProtobufHighlightStyle, HttpVerb as ProtobufHttpVerb, IdAndNewName,
56        KeyToRebind, KeyToUnbind, KillSessionsPayload, ListTokensResponse, LoadNewPluginPayload,
57        MessageToPluginPayload, MovePaneWithPaneIdInDirectionPayload, MovePaneWithPaneIdPayload,
58        MovePayload, NewPluginArgs as ProtobufNewPluginArgs, NewTabPayload,
59        NewTabResponse as ProtobufNewTabResponse, NewTabsResponse as ProtobufNewTabsResponse,
60        NewTabsWithLayoutInfoPayload,
61        OpenCommandPaneBackgroundResponse as ProtobufOpenCommandPaneBackgroundResponse,
62        OpenCommandPaneFloatingNearPluginPayload,
63        OpenCommandPaneFloatingNearPluginResponse as ProtobufOpenCommandPaneFloatingNearPluginResponse,
64        OpenCommandPaneFloatingResponse as ProtobufOpenCommandPaneFloatingResponse,
65        OpenCommandPaneInPlaceOfPaneIdPayload,
66        OpenCommandPaneInPlaceOfPaneIdResponse as ProtobufOpenCommandPaneInPlaceOfPaneIdResponse,
67        OpenCommandPaneInPlaceOfPluginPayload,
68        OpenCommandPaneInPlaceOfPluginResponse as ProtobufOpenCommandPaneInPlaceOfPluginResponse,
69        OpenCommandPaneInPlaceResponse as ProtobufOpenCommandPaneInPlaceResponse,
70        OpenCommandPaneNearPluginPayload,
71        OpenCommandPaneNearPluginResponse as ProtobufOpenCommandPaneNearPluginResponse,
72        OpenCommandPanePayload, OpenCommandPaneResponse as ProtobufOpenCommandPaneResponse,
73        OpenEditPaneInPlaceOfPaneIdPayload,
74        OpenEditPaneInPlaceOfPaneIdResponse as ProtobufOpenEditPaneInPlaceOfPaneIdResponse,
75        OpenFileFloatingNearPluginPayload,
76        OpenFileFloatingNearPluginResponse as ProtobufOpenFileFloatingNearPluginResponse,
77        OpenFileFloatingResponse as ProtobufOpenFileFloatingResponse,
78        OpenFileInPlaceOfPluginPayload,
79        OpenFileInPlaceOfPluginResponse as ProtobufOpenFileInPlaceOfPluginResponse,
80        OpenFileInPlaceResponse as ProtobufOpenFileInPlaceResponse, OpenFileNearPluginPayload,
81        OpenFileNearPluginResponse as ProtobufOpenFileNearPluginResponse, OpenFilePayload,
82        OpenFileResponse as ProtobufOpenFileResponse,
83        OpenPaneInNewTabResponse as ProtobufOpenPaneInNewTabResponse,
84        OpenPluginPaneFloatingPayload,
85        OpenPluginPaneFloatingResponse as ProtobufOpenPluginPaneFloatingResponse,
86        OpenPluginPaneInNewTabPayload as ProtobufOpenPluginPaneInNewTabPayload,
87        OpenTerminalFloatingNearPluginPayload,
88        OpenTerminalFloatingNearPluginResponse as ProtobufOpenTerminalFloatingNearPluginResponse,
89        OpenTerminalFloatingResponse as ProtobufOpenTerminalFloatingResponse,
90        OpenTerminalInPlaceOfPluginPayload,
91        OpenTerminalInPlaceOfPluginResponse as ProtobufOpenTerminalInPlaceOfPluginResponse,
92        OpenTerminalInPlaceResponse as ProtobufOpenTerminalInPlaceResponse,
93        OpenTerminalNearPluginPayload,
94        OpenTerminalNearPluginResponse as ProtobufOpenTerminalNearPluginResponse,
95        OpenTerminalPaneInPlaceOfPaneIdPayload,
96        OpenTerminalPaneInPlaceOfPaneIdResponse as ProtobufOpenTerminalPaneInPlaceOfPaneIdResponse,
97        OpenTerminalResponse as ProtobufOpenTerminalResponse, OverrideLayoutPayload,
98        PageScrollDownInPaneIdPayload, PageScrollUpInPaneIdPayload, PaneId as ProtobufPaneId,
99        PaneIdAndFloatingPaneCoordinates, PaneType as ProtobufPaneType, ParseLayoutPayload,
100        ParseLayoutResponse as ProtobufParseLayoutResponse, PluginCommand as ProtobufPluginCommand,
101        PluginMessagePayload, RebindKeysPayload, ReconfigurePayload,
102        RegexHighlight as ProtobufRegexHighlight, ReloadPluginPayload, RenameLayoutPayload,
103        RenameLayoutResponse as ProtobufRenameLayoutResponse, RenameTabWithIdPayload,
104        RenameWebLoginTokenPayload, RenameWebTokenResponse, ReplacePaneWithExistingPanePayload,
105        RequestPluginPermissionPayload, RerunCommandPanePayload, ResizePaneIdWithDirectionPayload,
106        ResizePayload, RevokeAllWebTokensResponse, RevokeTokenResponse, RevokeWebLoginTokenPayload,
107        RunActionPayload, RunCommandPayload, RunningCommand as ProtobufRunningCommand,
108        SaveLayoutPayload, SaveLayoutResponse as ProtobufSaveLayoutResponse, SaveSessionPayload,
109        SaveSessionResponse as ProtobufSaveSessionResponse, ScrollDownInPaneIdPayload,
110        ScrollToBottomInPaneIdPayload, ScrollToTopInPaneIdPayload, ScrollUpInPaneIdPayload,
111        SetFloatingPanePinnedPayload, SetPaneBorderlessPayload, SetPaneColorPayload,
112        SetPaneRegexHighlightsPayload, SetSelfMouseSelectionSupportPayload, SetTimeoutPayload,
113        ShowCursorPayload, ShowFloatingPanesPayload as ProtobufShowFloatingPanesPayload,
114        ShowFloatingPanesResponse as ProtobufShowFloatingPanesResponse, ShowPaneWithIdPayload,
115        StackPanesPayload, SubscribePayload, SwitchSessionPayload, SwitchTabToIdPayload,
116        SwitchTabToPayload, TogglePaneBorderlessPayload, TogglePaneEmbedOrEjectForPaneIdPayload,
117        TogglePaneIdFullscreenPayload, UnsubscribePayload, WebRequestPayload,
118        WriteCharsToPaneIdPayload, WriteToPaneIdPayload,
119    },
120    plugin_permission::PermissionType as ProtobufPermissionType,
121    resize::ResizeAction as ProtobufResizeAction,
122};
123
124use crate::data::{
125    ConnectToSession, DeleteLayoutResponse, EditLayoutResponse, FloatingPaneCoordinates,
126    GetFocusedPaneInfoResponse, GetPaneCwdResponse, GetPanePidResponse,
127    GetPaneRunningCommandResponse, HighlightLayer, HighlightStyle, HttpVerb, InputMode,
128    KeyWithModifier, MessageToPlugin, NewPluginArgs, PaneId, PermissionType, PluginCommand,
129    RegexHighlight, RenameLayoutResponse, SaveLayoutResponse,
130};
131use crate::input::actions::Action;
132use crate::input::layout::PercentOrFixed;
133
134use std::collections::BTreeMap;
135use std::convert::TryFrom;
136use std::path::PathBuf;
137
138impl Into<FloatingPaneCoordinates> for ProtobufFloatingPaneCoordinates {
139    fn into(self) -> FloatingPaneCoordinates {
140        FloatingPaneCoordinates {
141            x: self
142                .x
143                .and_then(|x| match ProtobufFixedOrPercent::from_i32(x.r#type) {
144                    Some(ProtobufFixedOrPercent::Percent) => {
145                        Some(PercentOrFixed::Percent(x.value as usize))
146                    },
147                    Some(ProtobufFixedOrPercent::Fixed) => {
148                        Some(PercentOrFixed::Fixed(x.value as usize))
149                    },
150                    None => None,
151                }),
152            y: self
153                .y
154                .and_then(|y| match ProtobufFixedOrPercent::from_i32(y.r#type) {
155                    Some(ProtobufFixedOrPercent::Percent) => {
156                        Some(PercentOrFixed::Percent(y.value as usize))
157                    },
158                    Some(ProtobufFixedOrPercent::Fixed) => {
159                        Some(PercentOrFixed::Fixed(y.value as usize))
160                    },
161                    None => None,
162                }),
163            width: self.width.and_then(|width| {
164                match ProtobufFixedOrPercent::from_i32(width.r#type) {
165                    Some(ProtobufFixedOrPercent::Percent) => {
166                        Some(PercentOrFixed::Percent(width.value as usize))
167                    },
168                    Some(ProtobufFixedOrPercent::Fixed) => {
169                        Some(PercentOrFixed::Fixed(width.value as usize))
170                    },
171                    None => None,
172                }
173            }),
174            height: self.height.and_then(|height| {
175                match ProtobufFixedOrPercent::from_i32(height.r#type) {
176                    Some(ProtobufFixedOrPercent::Percent) => {
177                        Some(PercentOrFixed::Percent(height.value as usize))
178                    },
179                    Some(ProtobufFixedOrPercent::Fixed) => {
180                        Some(PercentOrFixed::Fixed(height.value as usize))
181                    },
182                    None => None,
183                }
184            }),
185            pinned: self.pinned,
186            borderless: self.borderless,
187        }
188    }
189}
190
191impl Into<ProtobufFloatingPaneCoordinates> for FloatingPaneCoordinates {
192    fn into(self) -> ProtobufFloatingPaneCoordinates {
193        ProtobufFloatingPaneCoordinates {
194            x: match self.x {
195                Some(PercentOrFixed::Percent(percent)) => Some(ProtobufFixedOrPercentValue {
196                    r#type: ProtobufFixedOrPercent::Percent as i32,
197                    value: percent as u32,
198                }),
199                Some(PercentOrFixed::Fixed(fixed)) => Some(ProtobufFixedOrPercentValue {
200                    r#type: ProtobufFixedOrPercent::Fixed as i32,
201                    value: fixed as u32,
202                }),
203                None => None,
204            },
205            y: match self.y {
206                Some(PercentOrFixed::Percent(percent)) => Some(ProtobufFixedOrPercentValue {
207                    r#type: ProtobufFixedOrPercent::Percent as i32,
208                    value: percent as u32,
209                }),
210                Some(PercentOrFixed::Fixed(fixed)) => Some(ProtobufFixedOrPercentValue {
211                    r#type: ProtobufFixedOrPercent::Fixed as i32,
212                    value: fixed as u32,
213                }),
214                None => None,
215            },
216            width: match self.width {
217                Some(PercentOrFixed::Percent(percent)) => Some(ProtobufFixedOrPercentValue {
218                    r#type: ProtobufFixedOrPercent::Percent as i32,
219                    value: percent as u32,
220                }),
221                Some(PercentOrFixed::Fixed(fixed)) => Some(ProtobufFixedOrPercentValue {
222                    r#type: ProtobufFixedOrPercent::Fixed as i32,
223                    value: fixed as u32,
224                }),
225                None => None,
226            },
227            height: match self.height {
228                Some(PercentOrFixed::Percent(percent)) => Some(ProtobufFixedOrPercentValue {
229                    r#type: ProtobufFixedOrPercent::Percent as i32,
230                    value: percent as u32,
231                }),
232                Some(PercentOrFixed::Fixed(fixed)) => Some(ProtobufFixedOrPercentValue {
233                    r#type: ProtobufFixedOrPercent::Fixed as i32,
234                    value: fixed as u32,
235                }),
236                None => None,
237            },
238            pinned: self.pinned,
239            borderless: self.borderless,
240        }
241    }
242}
243
244impl Into<HttpVerb> for ProtobufHttpVerb {
245    fn into(self) -> HttpVerb {
246        match self {
247            ProtobufHttpVerb::Get => HttpVerb::Get,
248            ProtobufHttpVerb::Post => HttpVerb::Post,
249            ProtobufHttpVerb::Put => HttpVerb::Put,
250            ProtobufHttpVerb::Delete => HttpVerb::Delete,
251        }
252    }
253}
254
255impl Into<ProtobufHttpVerb> for HttpVerb {
256    fn into(self) -> ProtobufHttpVerb {
257        match self {
258            HttpVerb::Get => ProtobufHttpVerb::Get,
259            HttpVerb::Post => ProtobufHttpVerb::Post,
260            HttpVerb::Put => ProtobufHttpVerb::Put,
261            HttpVerb::Delete => ProtobufHttpVerb::Delete,
262        }
263    }
264}
265
266impl TryFrom<ProtobufPaneId> for PaneId {
267    type Error = &'static str;
268    fn try_from(protobuf_pane_id: ProtobufPaneId) -> Result<Self, &'static str> {
269        match ProtobufPaneType::from_i32(protobuf_pane_id.pane_type) {
270            Some(ProtobufPaneType::Terminal) => Ok(PaneId::Terminal(protobuf_pane_id.id)),
271            Some(ProtobufPaneType::Plugin) => Ok(PaneId::Plugin(protobuf_pane_id.id)),
272            None => Err("Failed to convert PaneId"),
273        }
274    }
275}
276
277impl TryFrom<PaneId> for ProtobufPaneId {
278    type Error = &'static str;
279    fn try_from(pane_id: PaneId) -> Result<Self, &'static str> {
280        match pane_id {
281            PaneId::Terminal(id) => Ok(ProtobufPaneId {
282                pane_type: ProtobufPaneType::Terminal as i32,
283                id,
284            }),
285            PaneId::Plugin(id) => Ok(ProtobufPaneId {
286                pane_type: ProtobufPaneType::Plugin as i32,
287                id,
288            }),
289        }
290    }
291}
292
293impl TryFrom<ProtobufGetPanePidResponse> for GetPanePidResponse {
294    type Error = &'static str;
295    fn try_from(protobuf_response: ProtobufGetPanePidResponse) -> Result<Self, &'static str> {
296        match protobuf_response.result {
297            Some(get_pane_pid_response::Result::Pid(pid)) => Ok(GetPanePidResponse::Ok(pid)),
298            Some(get_pane_pid_response::Result::Error(error)) => Ok(GetPanePidResponse::Err(error)),
299            None => Err("Empty GetPanePidResponse"),
300        }
301    }
302}
303
304impl From<GetPanePidResponse> for ProtobufGetPanePidResponse {
305    fn from(response: GetPanePidResponse) -> Self {
306        match response {
307            GetPanePidResponse::Ok(pid) => ProtobufGetPanePidResponse {
308                result: Some(get_pane_pid_response::Result::Pid(pid)),
309            },
310            GetPanePidResponse::Err(error) => ProtobufGetPanePidResponse {
311                result: Some(get_pane_pid_response::Result::Error(error)),
312            },
313        }
314    }
315}
316
317impl From<GetPaneRunningCommandResponse> for ProtobufGetPaneRunningCommandResponse {
318    fn from(response: GetPaneRunningCommandResponse) -> Self {
319        match response {
320            GetPaneRunningCommandResponse::Ok(args) => ProtobufGetPaneRunningCommandResponse {
321                result: Some(get_pane_running_command_response::Result::Command(
322                    ProtobufRunningCommand { args },
323                )),
324            },
325            GetPaneRunningCommandResponse::Err(err) => ProtobufGetPaneRunningCommandResponse {
326                result: Some(get_pane_running_command_response::Result::Error(err)),
327            },
328        }
329    }
330}
331
332impl From<GetPaneCwdResponse> for ProtobufGetPaneCwdResponse {
333    fn from(response: GetPaneCwdResponse) -> Self {
334        match response {
335            GetPaneCwdResponse::Ok(path) => {
336                let cwd_string = path
337                    .to_str()
338                    .map(|s| s.to_string())
339                    .unwrap_or_else(|| String::from(""));
340                ProtobufGetPaneCwdResponse {
341                    result: Some(get_pane_cwd_response::Result::Cwd(cwd_string)),
342                }
343            },
344            GetPaneCwdResponse::Err(err) => ProtobufGetPaneCwdResponse {
345                result: Some(get_pane_cwd_response::Result::Error(err)),
346            },
347        }
348    }
349}
350
351impl From<GetFocusedPaneInfoResponse> for ProtobufGetFocusedPaneInfoResponse {
352    fn from(response: GetFocusedPaneInfoResponse) -> Self {
353        match response {
354            GetFocusedPaneInfoResponse::Ok { tab_index, pane_id } => {
355                let protobuf_pane_id = ProtobufPaneId {
356                    pane_type: match pane_id {
357                        PaneId::Terminal(_) => ProtobufPaneType::Terminal as i32,
358                        PaneId::Plugin(_) => ProtobufPaneType::Plugin as i32,
359                    },
360                    id: match pane_id {
361                        PaneId::Terminal(id) | PaneId::Plugin(id) => id,
362                    },
363                };
364                ProtobufGetFocusedPaneInfoResponse {
365                    result: Some(get_focused_pane_info_response::Result::FocusedPaneInfo(
366                        FocusedPaneInfo {
367                            focused_tab_index: tab_index as u32,
368                            focused_pane_id: Some(protobuf_pane_id),
369                        },
370                    )),
371                }
372            },
373            GetFocusedPaneInfoResponse::Err(err) => ProtobufGetFocusedPaneInfoResponse {
374                result: Some(get_focused_pane_info_response::Result::Error(err)),
375            },
376        }
377    }
378}
379
380impl TryFrom<ProtobufSaveLayoutResponse> for SaveLayoutResponse {
381    type Error = &'static str;
382    fn try_from(protobuf_response: ProtobufSaveLayoutResponse) -> Result<Self, &'static str> {
383        match protobuf_response.result {
384            Some(save_layout_response::Result::Success(_)) => Ok(SaveLayoutResponse::Ok(())),
385            Some(save_layout_response::Result::Error(error)) => Ok(SaveLayoutResponse::Err(error)),
386            None => Err("Empty SaveLayoutResponse"),
387        }
388    }
389}
390
391impl From<SaveLayoutResponse> for ProtobufSaveLayoutResponse {
392    fn from(response: SaveLayoutResponse) -> Self {
393        match response {
394            SaveLayoutResponse::Ok(_) => ProtobufSaveLayoutResponse {
395                result: Some(save_layout_response::Result::Success(true)),
396            },
397            SaveLayoutResponse::Err(error) => ProtobufSaveLayoutResponse {
398                result: Some(save_layout_response::Result::Error(error)),
399            },
400        }
401    }
402}
403
404impl TryFrom<ProtobufDeleteLayoutResponse> for DeleteLayoutResponse {
405    type Error = &'static str;
406    fn try_from(protobuf_response: ProtobufDeleteLayoutResponse) -> Result<Self, &'static str> {
407        match protobuf_response.result {
408            Some(delete_layout_response::Result::Success(_)) => Ok(DeleteLayoutResponse::Ok(())),
409            Some(delete_layout_response::Result::Error(error)) => {
410                Ok(DeleteLayoutResponse::Err(error))
411            },
412            None => Err("Empty DeleteLayoutResponse"),
413        }
414    }
415}
416
417impl From<DeleteLayoutResponse> for ProtobufDeleteLayoutResponse {
418    fn from(response: DeleteLayoutResponse) -> Self {
419        match response {
420            DeleteLayoutResponse::Ok(_) => ProtobufDeleteLayoutResponse {
421                result: Some(delete_layout_response::Result::Success(true)),
422            },
423            DeleteLayoutResponse::Err(error) => ProtobufDeleteLayoutResponse {
424                result: Some(delete_layout_response::Result::Error(error)),
425            },
426        }
427    }
428}
429
430impl TryFrom<ProtobufRenameLayoutResponse> for RenameLayoutResponse {
431    type Error = &'static str;
432    fn try_from(protobuf_response: ProtobufRenameLayoutResponse) -> Result<Self, &'static str> {
433        match protobuf_response.result {
434            Some(rename_layout_response::Result::Success(_)) => Ok(RenameLayoutResponse::Ok(())),
435            Some(rename_layout_response::Result::Error(error)) => {
436                Ok(RenameLayoutResponse::Err(error))
437            },
438            None => Err("Empty RenameLayoutResponse"),
439        }
440    }
441}
442
443impl From<RenameLayoutResponse> for ProtobufRenameLayoutResponse {
444    fn from(response: RenameLayoutResponse) -> Self {
445        match response {
446            RenameLayoutResponse::Ok(_) => ProtobufRenameLayoutResponse {
447                result: Some(rename_layout_response::Result::Success(true)),
448            },
449            RenameLayoutResponse::Err(error) => ProtobufRenameLayoutResponse {
450                result: Some(rename_layout_response::Result::Error(error)),
451            },
452        }
453    }
454}
455
456impl TryFrom<ProtobufEditLayoutResponse> for EditLayoutResponse {
457    type Error = &'static str;
458    fn try_from(protobuf_response: ProtobufEditLayoutResponse) -> Result<Self, &'static str> {
459        match protobuf_response.result {
460            Some(edit_layout_response::Result::Success(_)) => Ok(EditLayoutResponse::Ok(())),
461            Some(edit_layout_response::Result::Error(error)) => Ok(EditLayoutResponse::Err(error)),
462            None => Err("Empty EditLayoutResponse"),
463        }
464    }
465}
466
467impl From<EditLayoutResponse> for ProtobufEditLayoutResponse {
468    fn from(response: EditLayoutResponse) -> Self {
469        match response {
470            EditLayoutResponse::Ok(_) => ProtobufEditLayoutResponse {
471                result: Some(edit_layout_response::Result::Success(true)),
472            },
473            EditLayoutResponse::Err(error) => ProtobufEditLayoutResponse {
474                result: Some(edit_layout_response::Result::Error(error)),
475            },
476        }
477    }
478}
479
480impl TryFrom<(InputMode, KeyWithModifier, Vec<Action>)> for KeyToRebind {
481    type Error = &'static str;
482    fn try_from(
483        key_to_rebind: (InputMode, KeyWithModifier, Vec<Action>),
484    ) -> Result<Self, &'static str> {
485        Ok(KeyToRebind {
486            input_mode: key_to_rebind.0 as i32,
487            key: Some(key_to_rebind.1.try_into()?),
488            actions: key_to_rebind
489                .2
490                .into_iter()
491                .filter_map(|a| a.try_into().ok())
492                .collect(),
493        })
494    }
495}
496
497impl TryFrom<(InputMode, KeyWithModifier)> for KeyToUnbind {
498    type Error = &'static str;
499    fn try_from(key_to_unbind: (InputMode, KeyWithModifier)) -> Result<Self, &'static str> {
500        Ok(KeyToUnbind {
501            input_mode: key_to_unbind.0 as i32,
502            key: Some(key_to_unbind.1.try_into()?),
503        })
504    }
505}
506
507fn key_to_rebind_to_plugin_command_assets(
508    key_to_rebind: KeyToRebind,
509) -> Option<(InputMode, KeyWithModifier, Vec<Action>)> {
510    Some((
511        ProtobufInputMode::from_i32(key_to_rebind.input_mode)?
512            .try_into()
513            .ok()?,
514        key_to_rebind.key?.try_into().ok()?,
515        key_to_rebind
516            .actions
517            .into_iter()
518            .filter_map(|a| a.try_into().ok())
519            .collect(),
520    ))
521}
522
523fn key_to_unbind_to_plugin_command_assets(
524    key_to_unbind: KeyToUnbind,
525) -> Option<(InputMode, KeyWithModifier)> {
526    Some((
527        ProtobufInputMode::from_i32(key_to_unbind.input_mode)?
528            .try_into()
529            .ok()?,
530        key_to_unbind.key?.try_into().ok()?,
531    ))
532}
533
534impl TryFrom<ProtobufPluginCommand> for PluginCommand {
535    type Error = &'static str;
536    fn try_from(protobuf_plugin_command: ProtobufPluginCommand) -> Result<Self, &'static str> {
537        match CommandName::from_i32(protobuf_plugin_command.name) {
538            Some(CommandName::Subscribe) => match protobuf_plugin_command.payload {
539                Some(Payload::SubscribePayload(subscribe_payload)) => {
540                    let protobuf_event_list = subscribe_payload.subscriptions;
541                    match protobuf_event_list {
542                        Some(protobuf_event_list) => {
543                            Ok(PluginCommand::Subscribe(protobuf_event_list.try_into()?))
544                        },
545                        None => Err("malformed subscription event"),
546                    }
547                },
548                _ => Err("Mismatched payload for Subscribe"),
549            },
550            Some(CommandName::Unsubscribe) => match protobuf_plugin_command.payload {
551                Some(Payload::UnsubscribePayload(unsubscribe_payload)) => {
552                    let protobuf_event_list = unsubscribe_payload.subscriptions;
553                    match protobuf_event_list {
554                        Some(protobuf_event_list) => {
555                            Ok(PluginCommand::Unsubscribe(protobuf_event_list.try_into()?))
556                        },
557                        None => Err("malformed unsubscription event"),
558                    }
559                },
560                _ => Err("Mismatched payload for Unsubscribe"),
561            },
562            Some(CommandName::SetSelectable) => match protobuf_plugin_command.payload {
563                Some(Payload::SetSelectablePayload(should_be_selectable)) => {
564                    Ok(PluginCommand::SetSelectable(should_be_selectable))
565                },
566                _ => Err("Mismatched payload for SetSelectable"),
567            },
568            Some(CommandName::ShowCursor) => match protobuf_plugin_command.payload {
569                Some(Payload::ShowCursorPayload(payload)) => {
570                    let cursor_position =
571                        payload.position.map(|pos| (pos.x as usize, pos.y as usize));
572                    Ok(PluginCommand::ShowCursor(cursor_position))
573                },
574                _ => Err("Mismatched payload for ShowCursor"),
575            },
576            Some(CommandName::GetPluginIds) => {
577                if protobuf_plugin_command.payload.is_some() {
578                    Err("GetPluginIds should not have a payload")
579                } else {
580                    Ok(PluginCommand::GetPluginIds)
581                }
582            },
583            Some(CommandName::GetZellijVersion) => {
584                if protobuf_plugin_command.payload.is_some() {
585                    Err("GetZellijVersion should not have a payload")
586                } else {
587                    Ok(PluginCommand::GetZellijVersion)
588                }
589            },
590            Some(CommandName::OpenFile) => match protobuf_plugin_command.payload {
591                Some(Payload::OpenFilePayload(file_to_open_payload)) => {
592                    match file_to_open_payload.file_to_open {
593                        Some(file_to_open) => {
594                            let context: BTreeMap<String, String> = file_to_open_payload
595                                .context
596                                .into_iter()
597                                .map(|e| (e.name, e.value))
598                                .collect();
599                            Ok(PluginCommand::OpenFile(file_to_open.try_into()?, context))
600                        },
601                        None => Err("Malformed open file payload"),
602                    }
603                },
604                _ => Err("Mismatched payload for OpenFile"),
605            },
606            Some(CommandName::OpenFileFloating) => match protobuf_plugin_command.payload {
607                Some(Payload::OpenFileFloatingPayload(file_to_open_payload)) => {
608                    let floating_pane_coordinates = file_to_open_payload
609                        .floating_pane_coordinates
610                        .map(|f| f.into());
611                    let context: BTreeMap<String, String> = file_to_open_payload
612                        .context
613                        .into_iter()
614                        .map(|e| (e.name, e.value))
615                        .collect();
616                    match file_to_open_payload.file_to_open {
617                        Some(file_to_open) => Ok(PluginCommand::OpenFileFloating(
618                            file_to_open.try_into()?,
619                            floating_pane_coordinates,
620                            context,
621                        )),
622                        None => Err("Malformed open file payload"),
623                    }
624                },
625                _ => Err("Mismatched payload for OpenFileFloating"),
626            },
627            Some(CommandName::OpenTerminal) => match protobuf_plugin_command.payload {
628                Some(Payload::OpenTerminalPayload(file_to_open_payload)) => {
629                    match file_to_open_payload.file_to_open {
630                        Some(file_to_open) => {
631                            Ok(PluginCommand::OpenTerminal(file_to_open.try_into()?))
632                        },
633                        None => Err("Malformed open terminal payload"),
634                    }
635                },
636                _ => Err("Mismatched payload for OpenTerminal"),
637            },
638            Some(CommandName::OpenTerminalFloating) => match protobuf_plugin_command.payload {
639                Some(Payload::OpenTerminalFloatingPayload(file_to_open_payload)) => {
640                    let floating_pane_coordinates = file_to_open_payload
641                        .floating_pane_coordinates
642                        .map(|f| f.into());
643                    match file_to_open_payload.file_to_open {
644                        Some(file_to_open) => Ok(PluginCommand::OpenTerminalFloating(
645                            file_to_open.try_into()?,
646                            floating_pane_coordinates,
647                        )),
648                        None => Err("Malformed open terminal floating payload"),
649                    }
650                },
651                _ => Err("Mismatched payload for OpenTerminalFloating"),
652            },
653            Some(CommandName::OpenCommandPane) => match protobuf_plugin_command.payload {
654                Some(Payload::OpenCommandPanePayload(command_to_run_payload)) => {
655                    match command_to_run_payload.command_to_run {
656                        Some(command_to_run) => {
657                            let context: BTreeMap<String, String> = command_to_run_payload
658                                .context
659                                .into_iter()
660                                .map(|e| (e.name, e.value))
661                                .collect();
662                            Ok(PluginCommand::OpenCommandPane(
663                                command_to_run.try_into()?,
664                                context,
665                            ))
666                        },
667                        None => Err("Malformed open open command pane payload"),
668                    }
669                },
670                _ => Err("Mismatched payload for OpenCommandPane"),
671            },
672            Some(CommandName::OpenCommandPaneFloating) => match protobuf_plugin_command.payload {
673                Some(Payload::OpenCommandPaneFloatingPayload(command_to_run_payload)) => {
674                    let floating_pane_coordinates = command_to_run_payload
675                        .floating_pane_coordinates
676                        .map(|f| f.into());
677                    match command_to_run_payload.command_to_run {
678                        Some(command_to_run) => {
679                            let context: BTreeMap<String, String> = command_to_run_payload
680                                .context
681                                .into_iter()
682                                .map(|e| (e.name, e.value))
683                                .collect();
684                            Ok(PluginCommand::OpenCommandPaneFloating(
685                                command_to_run.try_into()?,
686                                floating_pane_coordinates,
687                                context,
688                            ))
689                        },
690                        None => Err("Malformed open command pane floating payload"),
691                    }
692                },
693                _ => Err("Mismatched payload for OpenCommandPaneFloating"),
694            },
695            Some(CommandName::SwitchTabTo) => match protobuf_plugin_command.payload {
696                Some(Payload::SwitchTabToPayload(switch_to_tab_payload)) => Ok(
697                    PluginCommand::SwitchTabTo(switch_to_tab_payload.tab_index as u32),
698                ),
699                _ => Err("Mismatched payload for SwitchToTab"),
700            },
701            Some(CommandName::SetTimeout) => match protobuf_plugin_command.payload {
702                Some(Payload::SetTimeoutPayload(set_timeout_payload)) => {
703                    Ok(PluginCommand::SetTimeout(set_timeout_payload.seconds))
704                },
705                _ => Err("Mismatched payload for SetTimeout"),
706            },
707            Some(CommandName::ExecCmd) => match protobuf_plugin_command.payload {
708                Some(Payload::ExecCmdPayload(exec_cmd_payload)) => {
709                    Ok(PluginCommand::ExecCmd(exec_cmd_payload.command_line))
710                },
711                _ => Err("Mismatched payload for ExecCmd"),
712            },
713            Some(CommandName::PostMessageTo) => match protobuf_plugin_command.payload {
714                Some(Payload::PostMessageToPayload(post_message_to_payload)) => {
715                    match post_message_to_payload.message {
716                        Some(message) => Ok(PluginCommand::PostMessageTo(message.try_into()?)),
717                        None => Err("Malformed post message to payload"),
718                    }
719                },
720                _ => Err("Mismatched payload for PostMessageTo"),
721            },
722            Some(CommandName::PostMessageToPlugin) => match protobuf_plugin_command.payload {
723                Some(Payload::PostMessageToPluginPayload(post_message_to_payload)) => {
724                    match post_message_to_payload.message {
725                        Some(message) => {
726                            Ok(PluginCommand::PostMessageToPlugin(message.try_into()?))
727                        },
728                        None => Err("Malformed post message to plugin payload"),
729                    }
730                },
731                _ => Err("Mismatched payload for PostMessageToPlugin"),
732            },
733            Some(CommandName::HideSelf) => {
734                if protobuf_plugin_command.payload.is_some() {
735                    return Err("HideSelf should not have a payload");
736                }
737                Ok(PluginCommand::HideSelf)
738            },
739            Some(CommandName::ShowSelf) => match protobuf_plugin_command.payload {
740                Some(Payload::ShowSelfPayload(should_float_if_hidden)) => {
741                    Ok(PluginCommand::ShowSelf(should_float_if_hidden))
742                },
743                _ => Err("Mismatched payload for ShowSelf"),
744            },
745            Some(CommandName::SwitchToMode) => match protobuf_plugin_command.payload {
746                Some(Payload::SwitchToModePayload(switch_to_mode_payload)) => {
747                    match ProtobufInputMode::from_i32(switch_to_mode_payload.input_mode) {
748                        Some(protobuf_input_mode) => {
749                            Ok(PluginCommand::SwitchToMode(protobuf_input_mode.try_into()?))
750                        },
751                        None => Err("Malformed switch to mode payload"),
752                    }
753                },
754                _ => Err("Mismatched payload for SwitchToMode"),
755            },
756            Some(CommandName::NewTabsWithLayout) => match protobuf_plugin_command.payload {
757                Some(Payload::NewTabsWithLayoutPayload(raw_layout)) => {
758                    Ok(PluginCommand::NewTabsWithLayout(raw_layout))
759                },
760                _ => Err("Mismatched payload for NewTabsWithLayout"),
761            },
762            Some(CommandName::NewTab) => match protobuf_plugin_command.payload {
763                Some(Payload::NewTabPayload(protobuf_new_tab_payload)) => {
764                    Ok(PluginCommand::NewTab {
765                        name: protobuf_new_tab_payload.name,
766                        cwd: protobuf_new_tab_payload.cwd,
767                    })
768                },
769                None => Ok(PluginCommand::NewTab {
770                    name: None,
771                    cwd: None,
772                }),
773                _ => Err("Mismatched payload for NewTab"),
774            },
775            Some(CommandName::GoToNextTab) => {
776                if protobuf_plugin_command.payload.is_some() {
777                    return Err("GoToNextTab should not have a payload");
778                }
779                Ok(PluginCommand::GoToNextTab)
780            },
781            Some(CommandName::GoToPreviousTab) => {
782                if protobuf_plugin_command.payload.is_some() {
783                    return Err("GoToPreviousTab should not have a payload");
784                }
785                Ok(PluginCommand::GoToPreviousTab)
786            },
787            Some(CommandName::Resize) => match protobuf_plugin_command.payload {
788                Some(Payload::ResizePayload(resize_payload)) => match resize_payload.resize {
789                    Some(resize) => Ok(PluginCommand::Resize(resize.try_into()?)),
790                    None => Err("Malformed switch resize payload"),
791                },
792                _ => Err("Mismatched payload for Resize"),
793            },
794            Some(CommandName::ResizeWithDirection) => match protobuf_plugin_command.payload {
795                Some(Payload::ResizeWithDirectionPayload(resize_with_direction_payload)) => {
796                    match resize_with_direction_payload.resize {
797                        Some(resize) => Ok(PluginCommand::ResizeWithDirection(resize.try_into()?)),
798                        None => Err("Malformed switch resize payload"),
799                    }
800                },
801                _ => Err("Mismatched payload for Resize"),
802            },
803            Some(CommandName::FocusNextPane) => {
804                if protobuf_plugin_command.payload.is_some() {
805                    return Err("FocusNextPane should not have a payload");
806                }
807                Ok(PluginCommand::FocusNextPane)
808            },
809            Some(CommandName::FocusPreviousPane) => {
810                if protobuf_plugin_command.payload.is_some() {
811                    return Err("FocusPreviousPane should not have a payload");
812                }
813                Ok(PluginCommand::FocusPreviousPane)
814            },
815            Some(CommandName::MoveFocus) => match protobuf_plugin_command.payload {
816                Some(Payload::MoveFocusPayload(move_payload)) => match move_payload.direction {
817                    Some(direction) => Ok(PluginCommand::MoveFocus(direction.try_into()?)),
818                    None => Err("Malformed move focus payload"),
819                },
820                _ => Err("Mismatched payload for MoveFocus"),
821            },
822            Some(CommandName::MoveFocusOrTab) => match protobuf_plugin_command.payload {
823                Some(Payload::MoveFocusOrTabPayload(move_payload)) => {
824                    match move_payload.direction {
825                        Some(direction) => Ok(PluginCommand::MoveFocusOrTab(direction.try_into()?)),
826                        None => Err("Malformed move focus or tab payload"),
827                    }
828                },
829                _ => Err("Mismatched payload for MoveFocusOrTab"),
830            },
831            Some(CommandName::Detach) => {
832                if protobuf_plugin_command.payload.is_some() {
833                    return Err("Detach should not have a payload");
834                }
835                Ok(PluginCommand::Detach)
836            },
837            Some(CommandName::EditScrollback) => {
838                if protobuf_plugin_command.payload.is_some() {
839                    return Err("EditScrollback should not have a payload");
840                }
841                Ok(PluginCommand::EditScrollback)
842            },
843            Some(CommandName::Write) => match protobuf_plugin_command.payload {
844                Some(Payload::WritePayload(bytes)) => Ok(PluginCommand::Write(bytes)),
845                _ => Err("Mismatched payload for Write"),
846            },
847            Some(CommandName::WriteChars) => match protobuf_plugin_command.payload {
848                Some(Payload::WriteCharsPayload(chars)) => Ok(PluginCommand::WriteChars(chars)),
849                _ => Err("Mismatched payload for WriteChars"),
850            },
851            Some(CommandName::ToggleTab) => {
852                if protobuf_plugin_command.payload.is_some() {
853                    return Err("ToggleTab should not have a payload");
854                }
855                Ok(PluginCommand::ToggleTab)
856            },
857            Some(CommandName::MovePane) => {
858                if protobuf_plugin_command.payload.is_some() {
859                    return Err("MovePane should not have a payload");
860                }
861                Ok(PluginCommand::MovePane)
862            },
863            Some(CommandName::MovePaneWithDirection) => match protobuf_plugin_command.payload {
864                Some(Payload::MovePaneWithDirectionPayload(move_payload)) => {
865                    match move_payload.direction {
866                        Some(direction) => {
867                            Ok(PluginCommand::MovePaneWithDirection(direction.try_into()?))
868                        },
869                        None => Err("Malformed MovePaneWithDirection payload"),
870                    }
871                },
872                _ => Err("Mismatched payload for MovePaneWithDirection"),
873            },
874            Some(CommandName::ClearScreen) => {
875                if protobuf_plugin_command.payload.is_some() {
876                    return Err("ClearScreen should not have a payload");
877                }
878                Ok(PluginCommand::ClearScreen)
879            },
880            Some(CommandName::ScrollUp) => {
881                if protobuf_plugin_command.payload.is_some() {
882                    return Err("ScrollUp should not have a payload");
883                }
884                Ok(PluginCommand::ScrollUp)
885            },
886            Some(CommandName::ScrollDown) => {
887                if protobuf_plugin_command.payload.is_some() {
888                    return Err("ScrollDown should not have a payload");
889                }
890                Ok(PluginCommand::ScrollDown)
891            },
892            Some(CommandName::ScrollToTop) => {
893                if protobuf_plugin_command.payload.is_some() {
894                    return Err("ScrollToTop should not have a payload");
895                }
896                Ok(PluginCommand::ScrollToTop)
897            },
898            Some(CommandName::ScrollToBottom) => {
899                if protobuf_plugin_command.payload.is_some() {
900                    return Err("ScrollToBottom should not have a payload");
901                }
902                Ok(PluginCommand::ScrollToBottom)
903            },
904            Some(CommandName::PageScrollUp) => {
905                if protobuf_plugin_command.payload.is_some() {
906                    return Err("PageScrollUp should not have a payload");
907                }
908                Ok(PluginCommand::PageScrollUp)
909            },
910            Some(CommandName::PageScrollDown) => {
911                if protobuf_plugin_command.payload.is_some() {
912                    return Err("PageScrollDown should not have a payload");
913                }
914                Ok(PluginCommand::PageScrollDown)
915            },
916            Some(CommandName::ToggleFocusFullscreen) => {
917                if protobuf_plugin_command.payload.is_some() {
918                    return Err("ToggleFocusFullscreen should not have a payload");
919                }
920                Ok(PluginCommand::ToggleFocusFullscreen)
921            },
922            Some(CommandName::TogglePaneFrames) => {
923                if protobuf_plugin_command.payload.is_some() {
924                    return Err("TogglePaneFrames should not have a payload");
925                }
926                Ok(PluginCommand::TogglePaneFrames)
927            },
928            Some(CommandName::TogglePaneEmbedOrEject) => {
929                if protobuf_plugin_command.payload.is_some() {
930                    return Err("TogglePaneEmbedOrEject should not have a payload");
931                }
932                Ok(PluginCommand::TogglePaneEmbedOrEject)
933            },
934            Some(CommandName::UndoRenamePane) => {
935                if protobuf_plugin_command.payload.is_some() {
936                    return Err("UndoRenamePane should not have a payload");
937                }
938                Ok(PluginCommand::UndoRenamePane)
939            },
940            Some(CommandName::CloseFocus) => {
941                if protobuf_plugin_command.payload.is_some() {
942                    return Err("CloseFocus should not have a payload");
943                }
944                Ok(PluginCommand::CloseFocus)
945            },
946            Some(CommandName::ToggleActiveTabSync) => {
947                if protobuf_plugin_command.payload.is_some() {
948                    return Err("ToggleActiveTabSync should not have a payload");
949                }
950                Ok(PluginCommand::ToggleActiveTabSync)
951            },
952            Some(CommandName::CloseFocusedTab) => {
953                if protobuf_plugin_command.payload.is_some() {
954                    return Err("CloseFocusedTab should not have a payload");
955                }
956                Ok(PluginCommand::CloseFocusedTab)
957            },
958            Some(CommandName::UndoRenameTab) => {
959                if protobuf_plugin_command.payload.is_some() {
960                    return Err("UndoRenameTab should not have a payload");
961                }
962                Ok(PluginCommand::UndoRenameTab)
963            },
964            Some(CommandName::QuitZellij) => {
965                if protobuf_plugin_command.payload.is_some() {
966                    return Err("QuitZellij should not have a payload");
967                }
968                Ok(PluginCommand::QuitZellij)
969            },
970            Some(CommandName::PreviousSwapLayout) => {
971                if protobuf_plugin_command.payload.is_some() {
972                    return Err("PreviousSwapLayout should not have a payload");
973                }
974                Ok(PluginCommand::PreviousSwapLayout)
975            },
976            Some(CommandName::NextSwapLayout) => {
977                if protobuf_plugin_command.payload.is_some() {
978                    return Err("NextSwapLayout should not have a payload");
979                }
980                Ok(PluginCommand::NextSwapLayout)
981            },
982            Some(CommandName::GoToTabName) => match protobuf_plugin_command.payload {
983                Some(Payload::GoToTabNamePayload(tab_name)) => {
984                    Ok(PluginCommand::GoToTabName(tab_name))
985                },
986                _ => Err("Mismatched payload for GoToTabName"),
987            },
988            Some(CommandName::FocusOrCreateTab) => match protobuf_plugin_command.payload {
989                Some(Payload::FocusOrCreateTabPayload(tab_name)) => {
990                    Ok(PluginCommand::FocusOrCreateTab(tab_name))
991                },
992                _ => Err("Mismatched payload for FocusOrCreateTab"),
993            },
994            Some(CommandName::GoToTab) => match protobuf_plugin_command.payload {
995                Some(Payload::GoToTabPayload(tab_index)) => {
996                    Ok(PluginCommand::GoToTab(tab_index as u32))
997                },
998                _ => Err("Mismatched payload for GoToTab"),
999            },
1000            Some(CommandName::StartOrReloadPlugin) => match protobuf_plugin_command.payload {
1001                Some(Payload::StartOrReloadPluginPayload(url)) => {
1002                    Ok(PluginCommand::StartOrReloadPlugin(url))
1003                },
1004                _ => Err("Mismatched payload for StartOrReloadPlugin"),
1005            },
1006            Some(CommandName::CloseTerminalPane) => match protobuf_plugin_command.payload {
1007                Some(Payload::CloseTerminalPanePayload(pane_id)) => {
1008                    Ok(PluginCommand::CloseTerminalPane(pane_id as u32))
1009                },
1010                _ => Err("Mismatched payload for CloseTerminalPane"),
1011            },
1012            Some(CommandName::ClosePluginPane) => match protobuf_plugin_command.payload {
1013                Some(Payload::ClosePluginPanePayload(pane_id)) => {
1014                    Ok(PluginCommand::ClosePluginPane(pane_id as u32))
1015                },
1016                _ => Err("Mismatched payload for ClosePluginPane"),
1017            },
1018            Some(CommandName::FocusTerminalPane) => match protobuf_plugin_command.payload {
1019                Some(Payload::FocusTerminalPanePayload(payload)) => {
1020                    let pane_id = payload.pane_id as u32;
1021                    let should_float = payload.should_float;
1022                    let should_be_in_place = payload.should_be_in_place;
1023                    Ok(PluginCommand::FocusTerminalPane(
1024                        pane_id,
1025                        should_float,
1026                        should_be_in_place,
1027                    ))
1028                },
1029                _ => Err("Mismatched payload for ClosePluginPane"),
1030            },
1031            Some(CommandName::FocusPluginPane) => match protobuf_plugin_command.payload {
1032                Some(Payload::FocusPluginPanePayload(payload)) => {
1033                    let pane_id = payload.pane_id as u32;
1034                    let should_float = payload.should_float;
1035                    let should_be_in_place = payload.should_be_in_place;
1036                    Ok(PluginCommand::FocusPluginPane(
1037                        pane_id,
1038                        should_float,
1039                        should_be_in_place,
1040                    ))
1041                },
1042                _ => Err("Mismatched payload for ClosePluginPane"),
1043            },
1044            Some(CommandName::RenameTerminalPane) => match protobuf_plugin_command.payload {
1045                Some(Payload::RenameTerminalPanePayload(payload)) => {
1046                    let pane_id = payload.id as u32;
1047                    let new_name = payload.new_name;
1048                    Ok(PluginCommand::RenameTerminalPane(pane_id, new_name))
1049                },
1050                _ => Err("Mismatched payload for RenameTerminalPane"),
1051            },
1052            Some(CommandName::RenamePluginPane) => match protobuf_plugin_command.payload {
1053                Some(Payload::RenamePluginPanePayload(payload)) => {
1054                    let pane_id = payload.id as u32;
1055                    let new_name = payload.new_name;
1056                    Ok(PluginCommand::RenamePluginPane(pane_id, new_name))
1057                },
1058                _ => Err("Mismatched payload for RenamePluginPane"),
1059            },
1060            Some(CommandName::RenameTab) => match protobuf_plugin_command.payload {
1061                Some(Payload::RenameTabPayload(payload)) => {
1062                    let tab_index = payload.id as u32;
1063                    let name = payload.new_name;
1064                    Ok(PluginCommand::RenameTab(tab_index, name))
1065                },
1066                _ => Err("Mismatched payload for RenameTab"),
1067            },
1068            Some(CommandName::ReportCrash) => match protobuf_plugin_command.payload {
1069                Some(Payload::ReportCrashPayload(payload)) => {
1070                    Ok(PluginCommand::ReportPanic(payload))
1071                },
1072                _ => Err("Mismatched payload for ReportCrash"),
1073            },
1074            Some(CommandName::RequestPluginPermissions) => match protobuf_plugin_command.payload {
1075                Some(Payload::RequestPluginPermissionPayload(payload)) => {
1076                    Ok(PluginCommand::RequestPluginPermissions(
1077                        payload
1078                            .permissions
1079                            .iter()
1080                            .filter_map(|p| ProtobufPermissionType::from_i32(*p))
1081                            .filter_map(|p| PermissionType::try_from(p).ok())
1082                            .collect(),
1083                    ))
1084                },
1085                _ => Err("Mismatched payload for RequestPluginPermission"),
1086            },
1087            Some(CommandName::SwitchSession) => match protobuf_plugin_command.payload {
1088                Some(Payload::SwitchSessionPayload(payload)) => {
1089                    let pane_id = match (payload.pane_id, payload.pane_id_is_plugin) {
1090                        (Some(pane_id), Some(is_plugin)) => Some((pane_id, is_plugin)),
1091                        (None, None) => None,
1092                        _ => {
1093                            return Err("Malformed payload for SwitchSession, 'pane_id' and 'is_plugin' must be included together or not at all")
1094                        }
1095                    };
1096                    Ok(PluginCommand::SwitchSession(ConnectToSession {
1097                        name: payload.name,
1098                        tab_position: payload.tab_position.map(|p| p as usize),
1099                        pane_id,
1100                        layout: payload.layout.and_then(|l| l.try_into().ok()),
1101                        cwd: payload.cwd.map(|c| PathBuf::from(c)),
1102                    }))
1103                },
1104                _ => Err("Mismatched payload for SwitchSession"),
1105            },
1106            Some(CommandName::OpenTerminalInPlace) => match protobuf_plugin_command.payload {
1107                Some(Payload::OpenTerminalInPlacePayload(file_to_open_payload)) => {
1108                    match file_to_open_payload.file_to_open {
1109                        Some(file_to_open) => {
1110                            Ok(PluginCommand::OpenTerminalInPlace(file_to_open.try_into()?))
1111                        },
1112                        None => Err("Malformed open terminal in-place payload"),
1113                    }
1114                },
1115                _ => Err("Mismatched payload for OpenTerminalInPlace"),
1116            },
1117            Some(CommandName::OpenFileInPlace) => match protobuf_plugin_command.payload {
1118                Some(Payload::OpenFileInPlacePayload(file_to_open_payload)) => {
1119                    match file_to_open_payload.file_to_open {
1120                        Some(file_to_open) => {
1121                            let context: BTreeMap<String, String> = file_to_open_payload
1122                                .context
1123                                .into_iter()
1124                                .map(|e| (e.name, e.value))
1125                                .collect();
1126                            Ok(PluginCommand::OpenFileInPlace(
1127                                file_to_open.try_into()?,
1128                                context,
1129                            ))
1130                        },
1131                        None => Err("Malformed open file in place payload"),
1132                    }
1133                },
1134                _ => Err("Mismatched payload for OpenFileInPlace"),
1135            },
1136            Some(CommandName::OpenCommandInPlace) => match protobuf_plugin_command.payload {
1137                Some(Payload::OpenCommandPaneInPlacePayload(command_to_run_payload)) => {
1138                    match command_to_run_payload.command_to_run {
1139                        Some(command_to_run) => {
1140                            let context: BTreeMap<String, String> = command_to_run_payload
1141                                .context
1142                                .into_iter()
1143                                .map(|e| (e.name, e.value))
1144                                .collect();
1145                            Ok(PluginCommand::OpenCommandPaneInPlace(
1146                                command_to_run.try_into()?,
1147                                context,
1148                            ))
1149                        },
1150                        None => Err("Malformed open command pane in-place payload"),
1151                    }
1152                },
1153                _ => Err("Mismatched payload for OpenCommandPaneInPlace"),
1154            },
1155            Some(CommandName::RunCommand) => match protobuf_plugin_command.payload {
1156                Some(Payload::RunCommandPayload(run_command_payload)) => {
1157                    let env_variables: BTreeMap<String, String> = run_command_payload
1158                        .env_variables
1159                        .into_iter()
1160                        .map(|e| (e.name, e.value))
1161                        .collect();
1162                    let context: BTreeMap<String, String> = run_command_payload
1163                        .context
1164                        .into_iter()
1165                        .map(|e| (e.name, e.value))
1166                        .collect();
1167                    Ok(PluginCommand::RunCommand(
1168                        run_command_payload.command_line,
1169                        env_variables,
1170                        PathBuf::from(run_command_payload.cwd),
1171                        context,
1172                    ))
1173                },
1174                _ => Err("Mismatched payload for RunCommand"),
1175            },
1176            Some(CommandName::WebRequest) => match protobuf_plugin_command.payload {
1177                Some(Payload::WebRequestPayload(web_request_payload)) => {
1178                    let context: BTreeMap<String, String> = web_request_payload
1179                        .context
1180                        .into_iter()
1181                        .map(|e| (e.name, e.value))
1182                        .collect();
1183                    let headers: BTreeMap<String, String> = web_request_payload
1184                        .headers
1185                        .into_iter()
1186                        .map(|e| (e.name, e.value))
1187                        .collect();
1188                    let verb = match ProtobufHttpVerb::from_i32(web_request_payload.verb) {
1189                        Some(verb) => verb.into(),
1190                        None => {
1191                            return Err("Unrecognized http verb");
1192                        },
1193                    };
1194                    Ok(PluginCommand::WebRequest(
1195                        web_request_payload.url,
1196                        verb,
1197                        headers,
1198                        web_request_payload.body,
1199                        context,
1200                    ))
1201                },
1202                _ => Err("Mismatched payload for WebRequest"),
1203            },
1204            Some(CommandName::DeleteDeadSession) => match protobuf_plugin_command.payload {
1205                Some(Payload::DeleteDeadSessionPayload(dead_session_name)) => {
1206                    Ok(PluginCommand::DeleteDeadSession(dead_session_name))
1207                },
1208                _ => Err("Mismatched payload for DeleteDeadSession"),
1209            },
1210            Some(CommandName::DeleteAllDeadSessions) => Ok(PluginCommand::DeleteAllDeadSessions),
1211            Some(CommandName::RenameSession) => match protobuf_plugin_command.payload {
1212                Some(Payload::RenameSessionPayload(new_session_name)) => {
1213                    Ok(PluginCommand::RenameSession(new_session_name))
1214                },
1215                _ => Err("Mismatched payload for RenameSession"),
1216            },
1217            Some(CommandName::UnblockCliPipeInput) => match protobuf_plugin_command.payload {
1218                Some(Payload::UnblockCliPipeInputPayload(pipe_name)) => {
1219                    Ok(PluginCommand::UnblockCliPipeInput(pipe_name))
1220                },
1221                _ => Err("Mismatched payload for UnblockPipeInput"),
1222            },
1223            Some(CommandName::BlockCliPipeInput) => match protobuf_plugin_command.payload {
1224                Some(Payload::BlockCliPipeInputPayload(pipe_name)) => {
1225                    Ok(PluginCommand::BlockCliPipeInput(pipe_name))
1226                },
1227                _ => Err("Mismatched payload for BlockPipeInput"),
1228            },
1229            Some(CommandName::CliPipeOutput) => match protobuf_plugin_command.payload {
1230                Some(Payload::CliPipeOutputPayload(CliPipeOutputPayload { pipe_name, output })) => {
1231                    Ok(PluginCommand::CliPipeOutput(pipe_name, output))
1232                },
1233                _ => Err("Mismatched payload for PipeOutput"),
1234            },
1235            Some(CommandName::MessageToPlugin) => match protobuf_plugin_command.payload {
1236                Some(Payload::MessageToPluginPayload(MessageToPluginPayload {
1237                    plugin_url,
1238                    plugin_config,
1239                    message_name,
1240                    message_payload,
1241                    message_args,
1242                    new_plugin_args,
1243                    destination_plugin_id,
1244                    floating_pane_coordinates,
1245                })) => {
1246                    let plugin_config: BTreeMap<String, String> = plugin_config
1247                        .into_iter()
1248                        .map(|e| (e.name, e.value))
1249                        .collect();
1250                    let message_args: BTreeMap<String, String> = message_args
1251                        .into_iter()
1252                        .map(|e| (e.name, e.value))
1253                        .collect();
1254                    Ok(PluginCommand::MessageToPlugin(MessageToPlugin {
1255                        plugin_url,
1256                        plugin_config,
1257                        message_name,
1258                        message_payload,
1259                        message_args,
1260                        new_plugin_args: new_plugin_args.and_then(|protobuf_new_plugin_args| {
1261                            Some(NewPluginArgs {
1262                                should_float: protobuf_new_plugin_args.should_float,
1263                                pane_id_to_replace: protobuf_new_plugin_args
1264                                    .pane_id_to_replace
1265                                    .and_then(|p_id| PaneId::try_from(p_id).ok()),
1266                                pane_title: protobuf_new_plugin_args.pane_title,
1267                                cwd: protobuf_new_plugin_args.cwd.map(|cwd| PathBuf::from(cwd)),
1268                                skip_cache: protobuf_new_plugin_args.skip_cache,
1269                                should_focus: protobuf_new_plugin_args.should_focus,
1270                            })
1271                        }),
1272                        destination_plugin_id,
1273                        floating_pane_coordinates: floating_pane_coordinates
1274                            .and_then(|f| f.try_into().ok()),
1275                    }))
1276                },
1277                _ => Err("Mismatched payload for MessageToPlugin"),
1278            },
1279            Some(CommandName::DisconnectOtherClients) => match protobuf_plugin_command.payload {
1280                None => Ok(PluginCommand::DisconnectOtherClients),
1281                _ => Err("Mismatched payload for DisconnectOtherClients"),
1282            },
1283            Some(CommandName::KillSessions) => match protobuf_plugin_command.payload {
1284                Some(Payload::KillSessionsPayload(KillSessionsPayload { session_names })) => {
1285                    Ok(PluginCommand::KillSessions(session_names))
1286                },
1287                _ => Err("Mismatched payload for KillSessions"),
1288            },
1289            Some(CommandName::ScanHostFolder) => match protobuf_plugin_command.payload {
1290                Some(Payload::ScanHostFolderPayload(folder_to_scan)) => {
1291                    Ok(PluginCommand::ScanHostFolder(PathBuf::from(folder_to_scan)))
1292                },
1293                _ => Err("Mismatched payload for ScanHostFolder"),
1294            },
1295            Some(CommandName::WatchFilesystem) => match protobuf_plugin_command.payload {
1296                Some(_) => Err("WatchFilesystem should have no payload, found a payload"),
1297                None => Ok(PluginCommand::WatchFilesystem),
1298            },
1299            Some(CommandName::ListWindowsVolumes) => match protobuf_plugin_command.payload {
1300                Some(_) => Err("ListWindowsVolumes should have no payload"),
1301                None => Ok(PluginCommand::ListWindowsVolumes),
1302            },
1303            Some(CommandName::DumpSessionLayout) => match protobuf_plugin_command.payload {
1304                Some(Payload::DumpSessionLayoutPayload(payload)) => {
1305                    Ok(PluginCommand::DumpSessionLayout {
1306                        tab_index: payload.tab_index.map(|i| i as usize),
1307                    })
1308                },
1309                None => Ok(PluginCommand::DumpSessionLayout { tab_index: None }),
1310                _ => Err("Mismatched payload for DumpSessionLayout"),
1311            },
1312            Some(CommandName::CloseSelf) => match protobuf_plugin_command.payload {
1313                Some(_) => Err("CloseSelf should have no payload, found a payload"),
1314                None => Ok(PluginCommand::CloseSelf),
1315            },
1316            Some(CommandName::NewTabsWithLayoutInfo) => match protobuf_plugin_command.payload {
1317                Some(Payload::NewTabsWithLayoutInfoPayload(new_tabs_with_layout_info_payload)) => {
1318                    new_tabs_with_layout_info_payload
1319                        .layout_info
1320                        .and_then(|layout_info| {
1321                            Some(PluginCommand::NewTabsWithLayoutInfo(
1322                                layout_info.try_into().ok()?,
1323                            ))
1324                        })
1325                        .ok_or("Failed to parse NewTabsWithLayoutInfo command")
1326                },
1327                _ => Err("Mismatched payload for NewTabsWithLayoutInfo"),
1328            },
1329            Some(CommandName::Reconfigure) => match protobuf_plugin_command.payload {
1330                Some(Payload::ReconfigurePayload(reconfigure_payload)) => {
1331                    Ok(PluginCommand::Reconfigure(
1332                        reconfigure_payload.config,
1333                        reconfigure_payload.write_to_disk,
1334                    ))
1335                },
1336                _ => Err("Mismatched payload for Reconfigure"),
1337            },
1338            Some(CommandName::HidePaneWithId) => match protobuf_plugin_command.payload {
1339                Some(Payload::HidePaneWithIdPayload(hide_pane_with_id_payload)) => {
1340                    let pane_id = hide_pane_with_id_payload
1341                        .pane_id
1342                        .and_then(|p_id| PaneId::try_from(p_id).ok())
1343                        .ok_or("Failed to parse HidePaneWithId command")?;
1344                    Ok(PluginCommand::HidePaneWithId(pane_id))
1345                },
1346                _ => Err("Mismatched payload for HidePaneWithId"),
1347            },
1348            Some(CommandName::ShowPaneWithId) => match protobuf_plugin_command.payload {
1349                Some(Payload::ShowPaneWithIdPayload(show_pane_with_id_payload)) => {
1350                    let pane_id = show_pane_with_id_payload
1351                        .pane_id
1352                        .and_then(|p_id| PaneId::try_from(p_id).ok())
1353                        .ok_or("Failed to parse ShowPaneWithId command")?;
1354                    let should_float_if_hidden = show_pane_with_id_payload.should_float_if_hidden;
1355                    let should_focus_pane = show_pane_with_id_payload.should_focus_pane;
1356                    Ok(PluginCommand::ShowPaneWithId(
1357                        pane_id,
1358                        should_float_if_hidden,
1359                        should_focus_pane,
1360                    ))
1361                },
1362                _ => Err("Mismatched payload for ShowPaneWithId"),
1363            },
1364            Some(CommandName::OpenCommandPaneBackground) => match protobuf_plugin_command.payload {
1365                Some(Payload::OpenCommandPaneBackgroundPayload(command_to_run_payload)) => {
1366                    match command_to_run_payload.command_to_run {
1367                        Some(command_to_run) => {
1368                            let context: BTreeMap<String, String> = command_to_run_payload
1369                                .context
1370                                .into_iter()
1371                                .map(|e| (e.name, e.value))
1372                                .collect();
1373                            Ok(PluginCommand::OpenCommandPaneBackground(
1374                                command_to_run.try_into()?,
1375                                context,
1376                            ))
1377                        },
1378                        None => Err("Malformed open command pane background payload"),
1379                    }
1380                },
1381                _ => Err("Mismatched payload for OpenCommandPaneBackground"),
1382            },
1383            Some(CommandName::RerunCommandPane) => match protobuf_plugin_command.payload {
1384                Some(Payload::RerunCommandPanePayload(rerun_command_pane_payload)) => Ok(
1385                    PluginCommand::RerunCommandPane(rerun_command_pane_payload.terminal_pane_id),
1386                ),
1387                _ => Err("Mismatched payload for RerunCommandPane"),
1388            },
1389            Some(CommandName::ResizePaneIdWithDirection) => match protobuf_plugin_command.payload {
1390                Some(Payload::ResizePaneIdWithDirectionPayload(resize_with_direction_payload)) => {
1391                    match (
1392                        resize_with_direction_payload.resize,
1393                        resize_with_direction_payload.pane_id,
1394                    ) {
1395                        (Some(resize), Some(pane_id)) => {
1396                            Ok(PluginCommand::ResizePaneIdWithDirection(
1397                                resize.try_into()?,
1398                                pane_id.try_into()?,
1399                            ))
1400                        },
1401                        _ => Err("Malformed resize_pane_with_id payload"),
1402                    }
1403                },
1404                _ => Err("Mismatched payload for Resize"),
1405            },
1406            Some(CommandName::EditScrollbackForPaneWithId) => match protobuf_plugin_command.payload
1407            {
1408                Some(Payload::EditScrollbackForPaneWithIdPayload(
1409                    edit_scrollback_for_pane_with_id_payload,
1410                )) => match edit_scrollback_for_pane_with_id_payload.pane_id {
1411                    Some(pane_id) => Ok(PluginCommand::EditScrollbackForPaneWithId(
1412                        pane_id.try_into()?,
1413                    )),
1414                    _ => Err("Malformed edit_scrollback_for_pane_with_id payload"),
1415                },
1416                _ => Err("Mismatched payload for EditScrollback"),
1417            },
1418            Some(CommandName::GetPaneScrollback) => match protobuf_plugin_command.payload {
1419                Some(Payload::GetPaneScrollbackPayload(get_pane_scrollback_payload)) => {
1420                    match get_pane_scrollback_payload.pane_id {
1421                        Some(pane_id) => Ok(PluginCommand::GetPaneScrollback {
1422                            pane_id: pane_id.try_into()?,
1423                            get_full_scrollback: get_pane_scrollback_payload.get_full_scrollback,
1424                        }),
1425                        _ => Err("Malformed get_pane_scrollback_payload"),
1426                    }
1427                },
1428                _ => Err("Mismatched payload for GetPaneScrollback"),
1429            },
1430            Some(CommandName::WriteToPaneId) => match protobuf_plugin_command.payload {
1431                Some(Payload::WriteToPaneIdPayload(write_to_pane_id_payload)) => {
1432                    match write_to_pane_id_payload.pane_id {
1433                        Some(pane_id) => Ok(PluginCommand::WriteToPaneId(
1434                            write_to_pane_id_payload.bytes_to_write,
1435                            pane_id.try_into()?,
1436                        )),
1437                        _ => Err("Malformed write_to_pane_id payload"),
1438                    }
1439                },
1440                _ => Err("Mismatched payload for WriteToPaneId"),
1441            },
1442            Some(CommandName::WriteCharsToPaneId) => match protobuf_plugin_command.payload {
1443                Some(Payload::WriteCharsToPaneIdPayload(write_chars_to_pane_id_payload)) => {
1444                    match write_chars_to_pane_id_payload.pane_id {
1445                        Some(pane_id) => Ok(PluginCommand::WriteCharsToPaneId(
1446                            write_chars_to_pane_id_payload.chars_to_write,
1447                            pane_id.try_into()?,
1448                        )),
1449                        _ => Err("Malformed write_chars_to_pane_id payload"),
1450                    }
1451                },
1452                _ => Err("Mismatched payload for WriteCharsCharsToPaneId"),
1453            },
1454            Some(CommandName::SendSigintToPaneId) => match protobuf_plugin_command.payload {
1455                Some(Payload::SendSigintToPaneIdPayload(pane_id)) => {
1456                    Ok(PluginCommand::SendSigintToPaneId(pane_id.try_into()?))
1457                },
1458                _ => Err("Mismatched payload for SendSigintToPaneId"),
1459            },
1460            Some(CommandName::SendSigkillToPaneId) => match protobuf_plugin_command.payload {
1461                Some(Payload::SendSigkillToPaneIdPayload(pane_id)) => {
1462                    Ok(PluginCommand::SendSigkillToPaneId(pane_id.try_into()?))
1463                },
1464                _ => Err("Mismatched payload for SendSigkillToPaneId"),
1465            },
1466            Some(CommandName::GetPanePid) => match protobuf_plugin_command.payload {
1467                Some(Payload::GetPanePidPayload(get_pane_pid_payload)) => {
1468                    match get_pane_pid_payload.pane_id {
1469                        Some(pane_id) => Ok(PluginCommand::GetPanePid {
1470                            pane_id: pane_id.try_into()?,
1471                        }),
1472                        _ => Err("Malformed get_pane_pid payload"),
1473                    }
1474                },
1475                _ => Err("Mismatched payload for GetPanePid"),
1476            },
1477            Some(CommandName::GetPaneRunningCommand) => match protobuf_plugin_command.payload {
1478                Some(Payload::GetPaneRunningCommandPayload(payload)) => {
1479                    Ok(PluginCommand::GetPaneRunningCommand {
1480                        pane_id: payload
1481                            .pane_id
1482                            .ok_or("Malformed GetPaneRunningCommand: missing pane_id")?
1483                            .try_into()?,
1484                    })
1485                },
1486                _ => Err("Malformed GetPaneRunningCommand payload"),
1487            },
1488            Some(CommandName::GetPaneCwd) => match protobuf_plugin_command.payload {
1489                Some(Payload::GetPaneCwdPayload(payload)) => Ok(PluginCommand::GetPaneCwd {
1490                    pane_id: payload
1491                        .pane_id
1492                        .ok_or("Malformed GetPaneCwd: missing pane_id")?
1493                        .try_into()?,
1494                }),
1495                _ => Err("Malformed GetPaneCwd payload"),
1496            },
1497            Some(CommandName::OverrideLayout) => match protobuf_plugin_command.payload {
1498                Some(Payload::OverrideLayoutPayload(override_layout_payload)) => {
1499                    let layout_info = override_layout_payload
1500                        .layout_info
1501                        .ok_or("OverrideLayout missing layout_info")?
1502                        .try_into()
1503                        .map_err(|_| "Failed to parse LayoutInfo")?;
1504                    let context = override_layout_payload
1505                        .context
1506                        .into_iter()
1507                        .map(|c| (c.name, c.value))
1508                        .collect();
1509                    Ok(PluginCommand::OverrideLayout(
1510                        layout_info,
1511                        override_layout_payload.retain_existing_terminal_panes,
1512                        override_layout_payload.retain_existing_plugin_panes,
1513                        override_layout_payload.apply_only_to_active_tab,
1514                        context,
1515                    ))
1516                },
1517                _ => Err("Mismatched payload for OverrideLayout"),
1518            },
1519            Some(CommandName::SaveLayout) => match protobuf_plugin_command.payload {
1520                Some(Payload::SaveLayoutPayload(save_layout_payload)) => {
1521                    Ok(PluginCommand::SaveLayout {
1522                        layout_name: save_layout_payload.layout_name,
1523                        layout_kdl: save_layout_payload.layout_kdl,
1524                        overwrite: save_layout_payload.overwrite,
1525                    })
1526                },
1527                _ => Err("Mismatched payload for SaveLayout"),
1528            },
1529            Some(CommandName::DeleteLayout) => match protobuf_plugin_command.payload {
1530                Some(Payload::DeleteLayoutPayload(delete_layout_payload)) => {
1531                    Ok(PluginCommand::DeleteLayout {
1532                        layout_name: delete_layout_payload.layout_name,
1533                    })
1534                },
1535                _ => Err("Mismatched payload for DeleteLayout"),
1536            },
1537            Some(CommandName::RenameLayout) => match protobuf_plugin_command.payload {
1538                Some(Payload::RenameLayoutPayload(rename_layout_payload)) => {
1539                    Ok(PluginCommand::RenameLayout {
1540                        old_layout_name: rename_layout_payload.old_layout_name,
1541                        new_layout_name: rename_layout_payload.new_layout_name,
1542                    })
1543                },
1544                _ => Err("Mismatched payload for RenameLayout"),
1545            },
1546            Some(CommandName::EditLayout) => match protobuf_plugin_command.payload {
1547                Some(Payload::EditLayoutPayload(edit_layout_payload)) => {
1548                    let layout_name = edit_layout_payload.layout_name;
1549                    let context: BTreeMap<String, String> = edit_layout_payload
1550                        .context
1551                        .into_iter()
1552                        .map(|e| (e.name, e.value))
1553                        .collect();
1554                    Ok(PluginCommand::EditLayout {
1555                        layout_name,
1556                        context,
1557                    })
1558                },
1559                _ => Err("Mismatched payload for EditLayout"),
1560            },
1561            Some(CommandName::MovePaneWithPaneId) => match protobuf_plugin_command.payload {
1562                Some(Payload::MovePaneWithPaneIdPayload(move_pane_with_pane_id_payload)) => {
1563                    match move_pane_with_pane_id_payload.pane_id {
1564                        Some(pane_id) => Ok(PluginCommand::MovePaneWithPaneId(pane_id.try_into()?)),
1565                        _ => Err("Malformed move_pane_with_pane_id payload"),
1566                    }
1567                },
1568                _ => Err("Mismatched payload for MovePaneWithPaneId"),
1569            },
1570            Some(CommandName::MovePaneWithPaneIdInDirection) => {
1571                match protobuf_plugin_command.payload {
1572                    Some(Payload::MovePaneWithPaneIdInDirectionPayload(move_payload)) => {
1573                        match (move_payload.direction, move_payload.pane_id) {
1574                            (Some(direction), Some(pane_id)) => {
1575                                Ok(PluginCommand::MovePaneWithPaneIdInDirection(
1576                                    pane_id.try_into()?,
1577                                    direction.try_into()?,
1578                                ))
1579                            },
1580                            _ => Err("Malformed MovePaneWithPaneIdInDirection payload"),
1581                        }
1582                    },
1583                    _ => Err("Mismatched payload for MovePaneWithDirection"),
1584                }
1585            },
1586            Some(CommandName::ClearScreenForPaneId) => match protobuf_plugin_command.payload {
1587                Some(Payload::ClearScreenForPaneIdPayload(clear_screen_for_pane_id_payload)) => {
1588                    match clear_screen_for_pane_id_payload.pane_id {
1589                        Some(pane_id) => {
1590                            Ok(PluginCommand::ClearScreenForPaneId(pane_id.try_into()?))
1591                        },
1592                        _ => Err("Malformed clear_screen_for_pane_id_payload payload"),
1593                    }
1594                },
1595                _ => Err("Mismatched payload for ClearScreenForPaneId"),
1596            },
1597            Some(CommandName::ScrollUpInPaneId) => match protobuf_plugin_command.payload {
1598                Some(Payload::ScrollUpInPaneIdPayload(scroll_up_in_pane_id_payload)) => {
1599                    match scroll_up_in_pane_id_payload.pane_id {
1600                        Some(pane_id) => Ok(PluginCommand::ScrollUpInPaneId(pane_id.try_into()?)),
1601                        _ => Err("Malformed scroll_up_in_pane_id_payload payload"),
1602                    }
1603                },
1604                _ => Err("Mismatched payload for ScrollUpInPaneId"),
1605            },
1606            Some(CommandName::ScrollDownInPaneId) => match protobuf_plugin_command.payload {
1607                Some(Payload::ScrollDownInPaneIdPayload(scroll_down_in_pane_id_payload)) => {
1608                    match scroll_down_in_pane_id_payload.pane_id {
1609                        Some(pane_id) => Ok(PluginCommand::ScrollDownInPaneId(pane_id.try_into()?)),
1610                        _ => Err("Malformed scroll_down_in_pane_id_payload payload"),
1611                    }
1612                },
1613                _ => Err("Mismatched payload for ScrollDownInPaneId"),
1614            },
1615            Some(CommandName::ScrollToTopInPaneId) => match protobuf_plugin_command.payload {
1616                Some(Payload::ScrollToTopInPaneIdPayload(scroll_to_top_in_pane_id_payload)) => {
1617                    match scroll_to_top_in_pane_id_payload.pane_id {
1618                        Some(pane_id) => {
1619                            Ok(PluginCommand::ScrollToTopInPaneId(pane_id.try_into()?))
1620                        },
1621                        _ => Err("Malformed scroll_to_top_in_pane_id_payload payload"),
1622                    }
1623                },
1624                _ => Err("Mismatched payload for ScrollToTopInPaneId"),
1625            },
1626            Some(CommandName::ScrollToBottomInPaneId) => match protobuf_plugin_command.payload {
1627                Some(Payload::ScrollToBottomInPaneIdPayload(
1628                    scroll_to_bottom_in_pane_id_payload,
1629                )) => match scroll_to_bottom_in_pane_id_payload.pane_id {
1630                    Some(pane_id) => Ok(PluginCommand::ScrollToBottomInPaneId(pane_id.try_into()?)),
1631                    _ => Err("Malformed scroll_to_bottom_in_pane_id_payload payload"),
1632                },
1633                _ => Err("Mismatched payload for ScrollToBottomInPaneId"),
1634            },
1635            Some(CommandName::PageScrollUpInPaneId) => match protobuf_plugin_command.payload {
1636                Some(Payload::PageScrollUpInPaneIdPayload(page_scroll_up_in_pane_id_payload)) => {
1637                    match page_scroll_up_in_pane_id_payload.pane_id {
1638                        Some(pane_id) => {
1639                            Ok(PluginCommand::PageScrollUpInPaneId(pane_id.try_into()?))
1640                        },
1641                        _ => Err("Malformed page_scroll_up_in_pane_id_payload payload"),
1642                    }
1643                },
1644                _ => Err("Mismatched payload for PageScrollUpInPaneId"),
1645            },
1646            Some(CommandName::PageScrollDownInPaneId) => match protobuf_plugin_command.payload {
1647                Some(Payload::PageScrollDownInPaneIdPayload(
1648                    page_scroll_down_in_pane_id_payload,
1649                )) => match page_scroll_down_in_pane_id_payload.pane_id {
1650                    Some(pane_id) => Ok(PluginCommand::PageScrollDownInPaneId(pane_id.try_into()?)),
1651                    _ => Err("Malformed page_scroll_down_in_pane_id_payload payload"),
1652                },
1653                _ => Err("Mismatched payload for PageScrollDownInPaneId"),
1654            },
1655            Some(CommandName::TogglePaneIdFullscreen) => match protobuf_plugin_command.payload {
1656                Some(Payload::TogglePaneIdFullscreenPayload(toggle_pane_id_fullscreen_payload)) => {
1657                    match toggle_pane_id_fullscreen_payload.pane_id {
1658                        Some(pane_id) => {
1659                            Ok(PluginCommand::TogglePaneIdFullscreen(pane_id.try_into()?))
1660                        },
1661                        _ => Err("Malformed toggle_pane_id_fullscreen_payload payload"),
1662                    }
1663                },
1664                _ => Err("Mismatched payload for TogglePaneIdFullscreen"),
1665            },
1666            Some(CommandName::TogglePaneEmbedOrEjectForPaneId) => {
1667                match protobuf_plugin_command.payload {
1668                    Some(Payload::TogglePaneEmbedOrEjectForPaneIdPayload(
1669                        toggle_pane_embed_or_eject_payload,
1670                    )) => match toggle_pane_embed_or_eject_payload.pane_id {
1671                        Some(pane_id) => Ok(PluginCommand::TogglePaneEmbedOrEjectForPaneId(
1672                            pane_id.try_into()?,
1673                        )),
1674                        _ => Err("Malformed toggle_pane_embed_or_eject_payload payload"),
1675                    },
1676                    _ => Err("Mismatched payload for TogglePaneEmbedOrEjectForPaneId"),
1677                }
1678            },
1679            Some(CommandName::CloseTabWithIndex) => match protobuf_plugin_command.payload {
1680                Some(Payload::CloseTabWithIndexPayload(close_tab_index_payload)) => Ok(
1681                    PluginCommand::CloseTabWithIndex(close_tab_index_payload.tab_index as usize),
1682                ),
1683                _ => Err("Mismatched payload for CloseTabWithIndex"),
1684            },
1685            Some(CommandName::BreakPanesToNewTab) => match protobuf_plugin_command.payload {
1686                Some(Payload::BreakPanesToNewTabPayload(break_panes_to_new_tab_payload)) => {
1687                    Ok(PluginCommand::BreakPanesToNewTab(
1688                        break_panes_to_new_tab_payload
1689                            .pane_ids
1690                            .into_iter()
1691                            .filter_map(|p_id| p_id.try_into().ok())
1692                            .collect(),
1693                        break_panes_to_new_tab_payload.new_tab_name,
1694                        break_panes_to_new_tab_payload.should_change_focus_to_new_tab,
1695                    ))
1696                },
1697                _ => Err("Mismatched payload for BreakPanesToNewTab"),
1698            },
1699            Some(CommandName::BreakPanesToTabWithIndex) => match protobuf_plugin_command.payload {
1700                Some(Payload::BreakPanesToTabWithIndexPayload(
1701                    break_panes_to_tab_with_index_payload,
1702                )) => Ok(PluginCommand::BreakPanesToTabWithIndex(
1703                    break_panes_to_tab_with_index_payload
1704                        .pane_ids
1705                        .into_iter()
1706                        .filter_map(|p_id| p_id.try_into().ok())
1707                        .collect(),
1708                    break_panes_to_tab_with_index_payload.tab_index as usize,
1709                    break_panes_to_tab_with_index_payload.should_change_focus_to_target_tab,
1710                )),
1711                _ => Err("Mismatched payload for BreakPanesToTabWithIndex"),
1712            },
1713            Some(CommandName::SwitchTabToId) => match protobuf_plugin_command.payload {
1714                Some(Payload::SwitchTabToIdPayload(payload)) => {
1715                    Ok(PluginCommand::SwitchTabToId(payload.tab_id))
1716                },
1717                _ => Err("Mismatched payload for SwitchTabToId"),
1718            },
1719            Some(CommandName::GoToTabWithId) => match protobuf_plugin_command.payload {
1720                Some(Payload::GoToTabWithIdPayload(payload)) => {
1721                    Ok(PluginCommand::GoToTabWithId(payload.tab_id))
1722                },
1723                _ => Err("Mismatched payload for GoToTabWithId"),
1724            },
1725            Some(CommandName::CloseTabWithId) => match protobuf_plugin_command.payload {
1726                Some(Payload::CloseTabWithIdPayload(payload)) => {
1727                    Ok(PluginCommand::CloseTabWithId(payload.tab_id))
1728                },
1729                _ => Err("Mismatched payload for CloseTabWithId"),
1730            },
1731            Some(CommandName::RenameTabWithId) => match protobuf_plugin_command.payload {
1732                Some(Payload::RenameTabWithIdPayload(payload)) => Ok(
1733                    PluginCommand::RenameTabWithId(payload.tab_id, payload.new_name),
1734                ),
1735                _ => Err("Mismatched payload for RenameTabWithId"),
1736            },
1737            Some(CommandName::BreakPanesToTabWithId) => match protobuf_plugin_command.payload {
1738                Some(Payload::BreakPanesToTabWithIdPayload(payload)) => {
1739                    Ok(PluginCommand::BreakPanesToTabWithId(
1740                        payload
1741                            .pane_ids
1742                            .into_iter()
1743                            .filter_map(|p_id| p_id.try_into().ok())
1744                            .collect(),
1745                        payload.tab_id,
1746                        payload.should_change_focus_to_target_tab,
1747                    ))
1748                },
1749                _ => Err("Mismatched payload for BreakPanesToTabWithId"),
1750            },
1751            Some(CommandName::ReloadPlugin) => match protobuf_plugin_command.payload {
1752                Some(Payload::ReloadPluginPayload(reload_plugin_payload)) => {
1753                    Ok(PluginCommand::ReloadPlugin(reload_plugin_payload.plugin_id))
1754                },
1755                _ => Err("Mismatched payload for ReloadPlugin"),
1756            },
1757            Some(CommandName::LoadNewPlugin) => match protobuf_plugin_command.payload {
1758                Some(Payload::LoadNewPluginPayload(load_new_plugin_payload)) => {
1759                    Ok(PluginCommand::LoadNewPlugin {
1760                        url: load_new_plugin_payload.plugin_url,
1761                        config: load_new_plugin_payload
1762                            .plugin_config
1763                            .into_iter()
1764                            .map(|e| (e.name, e.value))
1765                            .collect(),
1766                        load_in_background: load_new_plugin_payload
1767                            .should_load_plugin_in_background,
1768                        skip_plugin_cache: load_new_plugin_payload.should_skip_plugin_cache,
1769                    })
1770                },
1771                _ => Err("Mismatched payload for LoadNewPlugin"),
1772            },
1773            Some(CommandName::RebindKeys) => match protobuf_plugin_command.payload {
1774                Some(Payload::RebindKeysPayload(rebind_keys_payload)) => {
1775                    Ok(PluginCommand::RebindKeys {
1776                        keys_to_rebind: rebind_keys_payload
1777                            .keys_to_rebind
1778                            .into_iter()
1779                            .filter_map(|k| key_to_rebind_to_plugin_command_assets(k))
1780                            .collect(),
1781                        keys_to_unbind: rebind_keys_payload
1782                            .keys_to_unbind
1783                            .into_iter()
1784                            .filter_map(|k| key_to_unbind_to_plugin_command_assets(k))
1785                            .collect(),
1786                        write_config_to_disk: rebind_keys_payload.write_config_to_disk,
1787                    })
1788                },
1789                _ => Err("Mismatched payload for RebindKeys"),
1790            },
1791            Some(CommandName::ListClients) => match protobuf_plugin_command.payload {
1792                Some(_) => Err("ListClients should have no payload, found a payload"),
1793                None => Ok(PluginCommand::ListClients),
1794            },
1795            Some(CommandName::ChangeHostFolder) => match protobuf_plugin_command.payload {
1796                Some(Payload::ChangeHostFolderPayload(change_host_folder_payload)) => {
1797                    Ok(PluginCommand::ChangeHostFolder(PathBuf::from(
1798                        change_host_folder_payload.new_host_folder,
1799                    )))
1800                },
1801                _ => Err("Mismatched payload for ChangeHostFolder"),
1802            },
1803            Some(CommandName::SetFloatingPanePinned) => match protobuf_plugin_command.payload {
1804                Some(Payload::SetFloatingPanePinnedPayload(set_floating_pane_pinned_payload)) => {
1805                    match set_floating_pane_pinned_payload
1806                        .pane_id
1807                        .and_then(|p| p.try_into().ok())
1808                    {
1809                        Some(pane_id) => Ok(PluginCommand::SetFloatingPanePinned(
1810                            pane_id,
1811                            set_floating_pane_pinned_payload.should_be_pinned,
1812                        )),
1813                        None => Err("PaneId not found!"),
1814                    }
1815                },
1816                _ => Err("Mismatched payload for SetFloatingPanePinned"),
1817            },
1818            Some(CommandName::StackPanes) => match protobuf_plugin_command.payload {
1819                Some(Payload::StackPanesPayload(stack_panes_payload)) => {
1820                    Ok(PluginCommand::StackPanes(
1821                        stack_panes_payload
1822                            .pane_ids
1823                            .into_iter()
1824                            .filter_map(|p_id| p_id.try_into().ok())
1825                            .collect(),
1826                    ))
1827                },
1828                _ => Err("Mismatched payload for StackPanes"),
1829            },
1830            Some(CommandName::ChangeFloatingPanesCoordinates) => {
1831                match protobuf_plugin_command.payload {
1832                    Some(Payload::ChangeFloatingPanesCoordinatesPayload(
1833                        change_floating_panes_coordinates_payload,
1834                    )) => Ok(PluginCommand::ChangeFloatingPanesCoordinates(
1835                        change_floating_panes_coordinates_payload
1836                            .pane_ids_and_floating_panes_coordinates
1837                            .into_iter()
1838                            .filter_map(|p_id_a_fp| {
1839                                let pane_id: PaneId = p_id_a_fp.pane_id?.try_into().ok()?;
1840                                let floating_pane_coordinates: FloatingPaneCoordinates =
1841                                    p_id_a_fp.floating_pane_coordinates?.try_into().ok()?;
1842                                Some((pane_id, floating_pane_coordinates))
1843                            })
1844                            .collect(),
1845                    )),
1846                    _ => Err("Mismatched payload for ChangeFloatingPanesCoordinates"),
1847                }
1848            },
1849            Some(CommandName::TogglePaneBorderless) => match protobuf_plugin_command.payload {
1850                Some(Payload::TogglePaneBorderlessPayload(toggle_payload)) => {
1851                    match toggle_payload.pane_id {
1852                        Some(pane_id) => {
1853                            Ok(PluginCommand::TogglePaneBorderless(pane_id.try_into()?))
1854                        },
1855                        None => Err("Malformed TogglePaneBorderless payload"),
1856                    }
1857                },
1858                _ => Err("Mismatched payload for TogglePaneBorderless"),
1859            },
1860            Some(CommandName::SetPaneBorderless) => match protobuf_plugin_command.payload {
1861                Some(Payload::SetPaneBorderlessPayload(payload)) => {
1862                    match (payload.pane_id, payload.borderless) {
1863                        (Some(pane_id), borderless) => Ok(PluginCommand::SetPaneBorderless(
1864                            pane_id.try_into()?,
1865                            borderless,
1866                        )),
1867                        _ => Err("Malformed SetPaneBorderless payload"),
1868                    }
1869                },
1870                _ => Err("Mismatched payload for SetPaneBorderless"),
1871            },
1872            Some(CommandName::OpenCommandPaneNearPlugin) => match protobuf_plugin_command.payload {
1873                Some(Payload::OpenCommandPaneNearPluginPayload(command_to_run_payload)) => {
1874                    match command_to_run_payload.command_to_run {
1875                        Some(command_to_run) => {
1876                            let context: BTreeMap<String, String> = command_to_run_payload
1877                                .context
1878                                .into_iter()
1879                                .map(|e| (e.name, e.value))
1880                                .collect();
1881                            Ok(PluginCommand::OpenCommandPaneNearPlugin(
1882                                command_to_run.try_into()?,
1883                                context,
1884                            ))
1885                        },
1886                        None => Err("Malformed open command pane near plugin payload"),
1887                    }
1888                },
1889                _ => Err("Mismatched payload for OpenCommandPaneNearPlugin"),
1890            },
1891            Some(CommandName::OpenTerminalNearPlugin) => match protobuf_plugin_command.payload {
1892                Some(Payload::OpenTerminalNearPluginPayload(open_terminal_near_plugin_payload)) => {
1893                    match open_terminal_near_plugin_payload.file_to_open {
1894                        Some(file_to_open) => Ok(PluginCommand::OpenTerminalNearPlugin(
1895                            file_to_open.try_into()?,
1896                        )),
1897                        None => Err("Malformed open terminal near plugin payload"),
1898                    }
1899                },
1900                _ => Err("Mismatched payload for OpenTerminalNearPluginPayload"),
1901            },
1902            Some(CommandName::OpenTerminalFloatingNearPlugin) => match protobuf_plugin_command
1903                .payload
1904            {
1905                Some(Payload::OpenTerminalFloatingNearPluginPayload(
1906                    open_terminal_floating_near_plugin_payload,
1907                )) => {
1908                    let floating_pane_coordinates = open_terminal_floating_near_plugin_payload
1909                        .floating_pane_coordinates
1910                        .map(|f| f.into());
1911                    match open_terminal_floating_near_plugin_payload.file_to_open {
1912                        Some(file_to_open) => Ok(PluginCommand::OpenTerminalFloatingNearPlugin(
1913                            file_to_open.try_into()?,
1914                            floating_pane_coordinates,
1915                        )),
1916                        None => Err("Malformed open terminal floating near plugin payload"),
1917                    }
1918                },
1919                _ => Err("Mismatched payload for OpenTerminalFloatingNearPlugin"),
1920            },
1921            Some(CommandName::OpenTerminalInPlaceOfPlugin) => match protobuf_plugin_command.payload
1922            {
1923                Some(Payload::OpenTerminalInPlaceOfPluginPayload(
1924                    open_terminal_in_place_of_plugin_payload,
1925                )) => match open_terminal_in_place_of_plugin_payload.file_to_open {
1926                    Some(file_to_open) => Ok(PluginCommand::OpenTerminalInPlaceOfPlugin(
1927                        file_to_open.try_into()?,
1928                        open_terminal_in_place_of_plugin_payload.close_plugin_after_replace,
1929                    )),
1930                    None => Err("Malformed open terminal in place of plugin payload"),
1931                },
1932                _ => Err("Mismatched payload for OpenTerminalInPlaceOfPlugin"),
1933            },
1934            Some(CommandName::OpenCommandPaneFloatingNearPlugin) => {
1935                match protobuf_plugin_command.payload {
1936                    Some(Payload::OpenCommandPaneFloatingNearPluginPayload(
1937                        open_command_pane_floating_near_plugin,
1938                    )) => match open_command_pane_floating_near_plugin.command_to_run {
1939                        Some(command_to_run) => {
1940                            let context: BTreeMap<String, String> =
1941                                open_command_pane_floating_near_plugin
1942                                    .context
1943                                    .into_iter()
1944                                    .map(|e| (e.name, e.value))
1945                                    .collect();
1946                            let floating_pane_coordinates = open_command_pane_floating_near_plugin
1947                                .floating_pane_coordinates
1948                                .map(|f| f.into());
1949                            Ok(PluginCommand::OpenCommandPaneFloatingNearPlugin(
1950                                command_to_run.try_into()?,
1951                                floating_pane_coordinates,
1952                                context,
1953                            ))
1954                        },
1955                        None => Err("Malformed open command pane floating near plugin payload"),
1956                    },
1957                    _ => Err("Mismatched payload for OpenCommandPaneFloatingNearPlugin"),
1958                }
1959            },
1960            Some(CommandName::OpenCommandPaneInPlaceOfPlugin) => {
1961                match protobuf_plugin_command.payload {
1962                    Some(Payload::OpenCommandPaneInPlaceOfPluginPayload(
1963                        open_command_pane_in_place_of_plugin_payload,
1964                    )) => match open_command_pane_in_place_of_plugin_payload.command_to_run {
1965                        Some(command_to_run) => {
1966                            let context: BTreeMap<String, String> =
1967                                open_command_pane_in_place_of_plugin_payload
1968                                    .context
1969                                    .into_iter()
1970                                    .map(|e| (e.name, e.value))
1971                                    .collect();
1972                            Ok(PluginCommand::OpenCommandPaneInPlaceOfPlugin(
1973                                command_to_run.try_into()?,
1974                                open_command_pane_in_place_of_plugin_payload
1975                                    .close_plugin_after_replace,
1976                                context,
1977                            ))
1978                        },
1979                        None => Err("Malformed open command pane in place of plugin payload"),
1980                    },
1981                    _ => Err("Mismatched payload for OpenCommandPaneInPlaceOfPlugin"),
1982                }
1983            },
1984            Some(CommandName::OpenFileNearPlugin) => match protobuf_plugin_command.payload {
1985                Some(Payload::OpenFileNearPluginPayload(file_to_open_payload)) => {
1986                    match file_to_open_payload.file_to_open {
1987                        Some(file_to_open) => {
1988                            let context: BTreeMap<String, String> = file_to_open_payload
1989                                .context
1990                                .into_iter()
1991                                .map(|e| (e.name, e.value))
1992                                .collect();
1993                            Ok(PluginCommand::OpenFileNearPlugin(
1994                                file_to_open.try_into()?,
1995                                context,
1996                            ))
1997                        },
1998                        None => Err("Malformed open file payload"),
1999                    }
2000                },
2001                _ => Err("Mismatched payload for OpenFileNearPlugin"),
2002            },
2003            Some(CommandName::OpenFileFloatingNearPlugin) => {
2004                match protobuf_plugin_command.payload {
2005                    Some(Payload::OpenFileFloatingNearPluginPayload(file_to_open_payload)) => {
2006                        let floating_pane_coordinates = file_to_open_payload
2007                            .floating_pane_coordinates
2008                            .map(|f| f.into());
2009                        let context: BTreeMap<String, String> = file_to_open_payload
2010                            .context
2011                            .into_iter()
2012                            .map(|e| (e.name, e.value))
2013                            .collect();
2014                        match file_to_open_payload.file_to_open {
2015                            Some(file_to_open) => Ok(PluginCommand::OpenFileFloatingNearPlugin(
2016                                file_to_open.try_into()?,
2017                                floating_pane_coordinates,
2018                                context,
2019                            )),
2020                            None => Err("Malformed open file payload"),
2021                        }
2022                    },
2023                    _ => Err("Mismatched payload for OpenFileFloatingNearPlugin"),
2024                }
2025            },
2026            Some(CommandName::OpenFileInPlaceOfPlugin) => match protobuf_plugin_command.payload {
2027                Some(Payload::OpenFileInPlaceOfPluginPayload(file_to_open_payload)) => {
2028                    match file_to_open_payload.file_to_open {
2029                        Some(file_to_open) => {
2030                            let context: BTreeMap<String, String> = file_to_open_payload
2031                                .context
2032                                .into_iter()
2033                                .map(|e| (e.name, e.value))
2034                                .collect();
2035                            Ok(PluginCommand::OpenFileInPlaceOfPlugin(
2036                                file_to_open.try_into()?,
2037                                file_to_open_payload.close_plugin_after_replace,
2038                                context,
2039                            ))
2040                        },
2041                        None => Err("Malformed open file in place payload"),
2042                    }
2043                },
2044                _ => Err("Mismatched payload for OpenFileInPlaceOfPlugin"),
2045            },
2046            Some(CommandName::StartWebServer) => {
2047                if protobuf_plugin_command.payload.is_some() {
2048                    Err("StartWebServer should not have a payload")
2049                } else {
2050                    Ok(PluginCommand::StartWebServer)
2051                }
2052            },
2053            Some(CommandName::StopWebServer) => {
2054                if protobuf_plugin_command.payload.is_some() {
2055                    Err("StopWebServer should not have a payload")
2056                } else {
2057                    Ok(PluginCommand::StopWebServer)
2058                }
2059            },
2060            Some(CommandName::QueryWebServerStatus) => {
2061                if protobuf_plugin_command.payload.is_some() {
2062                    Err("QueryWebServerStatus should not have a payload")
2063                } else {
2064                    Ok(PluginCommand::QueryWebServerStatus)
2065                }
2066            },
2067            Some(CommandName::GroupAndUngroupPanes) => match protobuf_plugin_command.payload {
2068                Some(Payload::GroupAndUngroupPanesPayload(group_and_ungroup_panes_payload)) => {
2069                    Ok(PluginCommand::GroupAndUngroupPanes(
2070                        group_and_ungroup_panes_payload
2071                            .pane_ids_to_group
2072                            .into_iter()
2073                            .filter_map(|p| p.try_into().ok())
2074                            .collect(),
2075                        group_and_ungroup_panes_payload
2076                            .pane_ids_to_ungroup
2077                            .into_iter()
2078                            .filter_map(|p| p.try_into().ok())
2079                            .collect(),
2080                        group_and_ungroup_panes_payload.for_all_clients,
2081                    ))
2082                },
2083                _ => Err("Mismatched payload for GroupAndUngroupPanes"),
2084            },
2085            Some(CommandName::HighlightAndUnhighlightPanes) => {
2086                match protobuf_plugin_command.payload {
2087                    Some(Payload::HighlightAndUnhighlightPanesPayload(
2088                        highlight_and_unhighlight_panes_payload,
2089                    )) => Ok(PluginCommand::HighlightAndUnhighlightPanes(
2090                        highlight_and_unhighlight_panes_payload
2091                            .pane_ids_to_highlight
2092                            .into_iter()
2093                            .filter_map(|p| p.try_into().ok())
2094                            .collect(),
2095                        highlight_and_unhighlight_panes_payload
2096                            .pane_ids_to_unhighlight
2097                            .into_iter()
2098                            .filter_map(|p| p.try_into().ok())
2099                            .collect(),
2100                    )),
2101                    _ => Err("Mismatched payload for HighlightAndUnhighlightPanes"),
2102                }
2103            },
2104            Some(CommandName::CloseMultiplePanes) => match protobuf_plugin_command.payload {
2105                Some(Payload::CloseMultiplePanesPayload(close_multiple_panes_payload)) => {
2106                    Ok(PluginCommand::CloseMultiplePanes(
2107                        close_multiple_panes_payload
2108                            .pane_ids
2109                            .into_iter()
2110                            .filter_map(|p| p.try_into().ok())
2111                            .collect(),
2112                    ))
2113                },
2114                _ => Err("Mismatched payload for CloseMultiplePanes"),
2115            },
2116            Some(CommandName::FloatMultiplePanes) => match protobuf_plugin_command.payload {
2117                Some(Payload::FloatMultiplePanesPayload(float_multiple_panes_payload)) => {
2118                    Ok(PluginCommand::FloatMultiplePanes(
2119                        float_multiple_panes_payload
2120                            .pane_ids
2121                            .into_iter()
2122                            .filter_map(|p| p.try_into().ok())
2123                            .collect(),
2124                    ))
2125                },
2126                _ => Err("Mismatched payload for FloatMultiplePanes"),
2127            },
2128            Some(CommandName::EmbedMultiplePanes) => match protobuf_plugin_command.payload {
2129                Some(Payload::EmbedMultiplePanesPayload(embed_multiple_panes_payload)) => {
2130                    Ok(PluginCommand::EmbedMultiplePanes(
2131                        embed_multiple_panes_payload
2132                            .pane_ids
2133                            .into_iter()
2134                            .filter_map(|p| p.try_into().ok())
2135                            .collect(),
2136                    ))
2137                },
2138                _ => Err("Mismatched payload for EmbedMultiplePanes"),
2139            },
2140            Some(CommandName::ShareCurrentSession) => {
2141                if protobuf_plugin_command.payload.is_some() {
2142                    Err("ShareCurrentSession should not have a payload")
2143                } else {
2144                    Ok(PluginCommand::ShareCurrentSession)
2145                }
2146            },
2147            Some(CommandName::StopSharingCurrentSession) => {
2148                if protobuf_plugin_command.payload.is_some() {
2149                    Err("StopSharingCurrentSession should not have a payload")
2150                } else {
2151                    Ok(PluginCommand::StopSharingCurrentSession)
2152                }
2153            },
2154            Some(CommandName::SetSelfMouseSelectionSupport) => {
2155                match protobuf_plugin_command.payload {
2156                    Some(Payload::SetSelfMouseSelectionSupportPayload(
2157                        set_self_mouse_selection_support_payload,
2158                    )) => Ok(PluginCommand::SetSelfMouseSelectionSupport(
2159                        set_self_mouse_selection_support_payload.support_mouse_selection,
2160                    )),
2161                    _ => Err("SetSelfMouseSelectionSupport requires a payload"),
2162                }
2163            },
2164            Some(CommandName::GenerateWebLoginToken) => match protobuf_plugin_command.payload {
2165                Some(Payload::GenerateWebLoginTokenPayload(generate_web_login_token_payload)) => {
2166                    Ok(PluginCommand::GenerateWebLoginToken(
2167                        generate_web_login_token_payload.token_label,
2168                        generate_web_login_token_payload.read_only.unwrap_or(false),
2169                    ))
2170                },
2171                _ => Err("GenerateWebLoginToken requires a payload"),
2172            },
2173            Some(CommandName::RevokeWebLoginToken) => match protobuf_plugin_command.payload {
2174                Some(Payload::RevokeWebLoginTokenPayload(revoke_web_login_token_payload)) => Ok(
2175                    PluginCommand::RevokeWebLoginToken(revoke_web_login_token_payload.token_label),
2176                ),
2177                _ => Err("RevokeWebLoginToken requires a payload"),
2178            },
2179            Some(CommandName::ListWebLoginTokens) => {
2180                if protobuf_plugin_command.payload.is_some() {
2181                    Err("ListWebLoginTokens should not have a payload")
2182                } else {
2183                    Ok(PluginCommand::ListWebLoginTokens)
2184                }
2185            },
2186            Some(CommandName::RevokeAllWebLoginTokens) => {
2187                if protobuf_plugin_command.payload.is_some() {
2188                    Err("RevokeAllWebLoginTokens should not have a payload")
2189                } else {
2190                    Ok(PluginCommand::RevokeAllWebLoginTokens)
2191                }
2192            },
2193            Some(CommandName::RenameWebLoginToken) => match protobuf_plugin_command.payload {
2194                Some(Payload::RenameWebLoginTokenPayload(rename_web_login_token_payload)) => {
2195                    Ok(PluginCommand::RenameWebLoginToken(
2196                        rename_web_login_token_payload.old_name,
2197                        rename_web_login_token_payload.new_name,
2198                    ))
2199                },
2200                _ => Err("RenameWebLoginToken requires a payload"),
2201            },
2202            Some(CommandName::InterceptKeyPresses) => match protobuf_plugin_command.payload {
2203                Some(_) => Err("InterceptKeyPresses should have no payload, found a payload"),
2204                None => Ok(PluginCommand::InterceptKeyPresses),
2205            },
2206            Some(CommandName::ClearKeyPressesIntercepts) => match protobuf_plugin_command.payload {
2207                Some(_) => Err("ClearKeyPressesIntercepts should have no payload, found a payload"),
2208                None => Ok(PluginCommand::ClearKeyPressesIntercepts),
2209            },
2210            Some(CommandName::ReplacePaneWithExistingPane) => match protobuf_plugin_command.payload
2211            {
2212                Some(Payload::ReplacePaneWithExistingPanePayload(
2213                    replace_pane_with_other_pane_payload,
2214                )) => Ok(PluginCommand::ReplacePaneWithExistingPane(
2215                    replace_pane_with_other_pane_payload
2216                        .pane_id_to_replace
2217                        .and_then(|p_id| PaneId::try_from(p_id).ok())
2218                        .ok_or("Failed to parse ReplacePaneWithExistingPanePayload")?,
2219                    replace_pane_with_other_pane_payload
2220                        .existing_pane_id
2221                        .and_then(|p_id| PaneId::try_from(p_id).ok())
2222                        .ok_or("Failed to parse ReplacePaneWithExistingPanePayload")?,
2223                    replace_pane_with_other_pane_payload.suppress_replaced_pane,
2224                )),
2225                _ => Err("Mismatched payload for ReplacePaneWithExistingPane"),
2226            },
2227            Some(CommandName::RunAction) => match protobuf_plugin_command.payload {
2228                Some(Payload::RunActionPayload(protobuf_payload)) => {
2229                    let action = Action::try_from(
2230                        protobuf_payload
2231                            .action
2232                            .ok_or("Missing action in RunAction payload")?,
2233                    )
2234                    .map_err(|_| "Failed to convert protobuf action")?;
2235
2236                    let context: BTreeMap<String, String> = protobuf_payload
2237                        .context
2238                        .into_iter()
2239                        .map(|item| (item.name, item.value))
2240                        .collect();
2241
2242                    Ok(PluginCommand::RunAction(action, context))
2243                },
2244                _ => Err("Mismatched payload for RunAction"),
2245            },
2246            Some(CommandName::CopyToClipboard) => match protobuf_plugin_command.payload {
2247                Some(Payload::CopyToClipboardPayload(payload)) => {
2248                    Ok(PluginCommand::CopyToClipboard(payload.text))
2249                },
2250                _ => Err("Mismatched payload for CopyToClipboard"),
2251            },
2252            Some(CommandName::GenerateRandomName) => Ok(PluginCommand::GenerateRandomName),
2253            Some(CommandName::DumpLayout) => match protobuf_plugin_command.payload {
2254                Some(Payload::DumpLayoutPayload(payload)) => {
2255                    Ok(PluginCommand::DumpLayout(payload.layout_name))
2256                },
2257                _ => Err("Mismatched payload for DumpLayout"),
2258            },
2259            Some(CommandName::ParseLayout) => match protobuf_plugin_command.payload {
2260                Some(Payload::ParseLayoutPayload(payload)) => {
2261                    Ok(PluginCommand::ParseLayout(payload.layout_string))
2262                },
2263                _ => Err("Mismatched payload for ParseLayout"),
2264            },
2265            Some(CommandName::GetLayoutDir) => Ok(PluginCommand::GetLayoutDir),
2266            Some(CommandName::GetFocusedPaneInfo) => Ok(PluginCommand::GetFocusedPaneInfo),
2267            Some(CommandName::SaveSession) => Ok(PluginCommand::SaveSession),
2268            Some(CommandName::CurrentSessionLastSavedTime) => {
2269                Ok(PluginCommand::CurrentSessionLastSavedTime)
2270            },
2271            Some(CommandName::GetPaneInfo) => match protobuf_plugin_command.payload {
2272                Some(Payload::GetPaneInfoPayload(get_pane_info_payload)) => {
2273                    let pane_id = get_pane_info_payload
2274                        .pane_id
2275                        .ok_or("Malformed pane_id for GetPaneInfo")
2276                        .and_then(|p| p.try_into())?;
2277                    Ok(PluginCommand::GetPaneInfo(pane_id))
2278                },
2279                _ => Err("Malformed payload for GetPaneInfo"),
2280            },
2281            Some(CommandName::GetTabInfo) => match protobuf_plugin_command.payload {
2282                Some(Payload::GetTabInfoPayload(get_tab_info_payload)) => {
2283                    let tab_id = get_tab_info_payload.tab_id as usize;
2284                    Ok(PluginCommand::GetTabInfo(tab_id))
2285                },
2286                _ => Err("Malformed payload for GetTabInfo"),
2287            },
2288            Some(CommandName::GetSessionEnvironmentVariables) => {
2289                match protobuf_plugin_command.payload {
2290                    Some(Payload::GetSessionEnvironmentVariablesPayload(_)) => {
2291                        Ok(PluginCommand::GetSessionEnvironmentVariables)
2292                    },
2293                    _ => Err("Mismatched payload for GetSessionEnvironmentVariables"),
2294                }
2295            },
2296            Some(CommandName::OpenCommandPaneInNewTab) => match protobuf_plugin_command.payload {
2297                Some(Payload::OpenCommandPaneInNewTabPayload(payload)) => {
2298                    match payload.command_to_run {
2299                        Some(command_to_run) => {
2300                            let context: BTreeMap<String, String> = payload
2301                                .context
2302                                .into_iter()
2303                                .map(|e| (e.name, e.value))
2304                                .collect();
2305                            Ok(PluginCommand::OpenCommandPaneInNewTab(
2306                                command_to_run.try_into()?,
2307                                context,
2308                            ))
2309                        },
2310                        None => Err("Malformed open_command_pane_in_new_tab payload"),
2311                    }
2312                },
2313                _ => Err("Mismatched payload for OpenCommandPaneInNewTab"),
2314            },
2315            Some(CommandName::OpenPluginPaneInNewTab) => match protobuf_plugin_command.payload {
2316                Some(Payload::OpenPluginPaneInNewTabPayload(payload)) => {
2317                    let configuration: BTreeMap<String, String> =
2318                        payload.configuration.into_iter().collect();
2319                    let context: BTreeMap<String, String> = payload
2320                        .context
2321                        .into_iter()
2322                        .map(|e| (e.name, e.value))
2323                        .collect();
2324                    Ok(PluginCommand::OpenPluginPaneInNewTab {
2325                        plugin_url: payload.plugin_url,
2326                        configuration,
2327                        context,
2328                    })
2329                },
2330                _ => Err("Mismatched payload for OpenPluginPaneInNewTab"),
2331            },
2332            Some(CommandName::OpenEditorPaneInNewTab) => match protobuf_plugin_command.payload {
2333                Some(Payload::OpenEditorPaneInNewTabPayload(payload)) => {
2334                    match payload.file_to_open {
2335                        Some(file) => {
2336                            let context: BTreeMap<String, String> = payload
2337                                .context
2338                                .into_iter()
2339                                .map(|e| (e.name, e.value))
2340                                .collect();
2341                            Ok(PluginCommand::OpenEditorPaneInNewTab(
2342                                file.try_into()?,
2343                                context,
2344                            ))
2345                        },
2346                        None => Err("Malformed open_editor_pane_in_new_tab payload"),
2347                    }
2348                },
2349                _ => Err("Mismatched payload for OpenEditorPaneInNewTab"),
2350            },
2351            Some(CommandName::OpenCommandPaneInPlaceOfPaneId) => {
2352                match protobuf_plugin_command.payload {
2353                    Some(Payload::OpenCommandPaneInPlaceOfPaneIdPayload(payload)) => {
2354                        match (payload.pane_id_to_replace, payload.command_to_run) {
2355                            (Some(pane_id), Some(command_to_run)) => {
2356                                let context: BTreeMap<String, String> = payload
2357                                    .context
2358                                    .into_iter()
2359                                    .map(|e| (e.name, e.value))
2360                                    .collect();
2361                                Ok(PluginCommand::OpenCommandPaneInPlaceOfPaneId(
2362                                    pane_id.try_into()?,
2363                                    command_to_run.try_into()?,
2364                                    payload.close_replaced_pane,
2365                                    context,
2366                                ))
2367                            },
2368                            _ => Err("Malformed open_command_pane_in_place_of_pane_id payload"),
2369                        }
2370                    },
2371                    _ => Err("Mismatched payload for OpenCommandPaneInPlaceOfPaneId"),
2372                }
2373            },
2374            Some(CommandName::OpenTerminalPaneInPlaceOfPaneId) => {
2375                match protobuf_plugin_command.payload {
2376                    Some(Payload::OpenTerminalPaneInPlaceOfPaneIdPayload(payload)) => {
2377                        match payload.pane_id_to_replace {
2378                            Some(pane_id) => {
2379                                let cwd = payload.cwd.map(|f| f.try_into()).transpose()?;
2380                                let cwd = cwd.unwrap_or_else(|| crate::data::FileToOpen {
2381                                    path: std::path::PathBuf::from("."),
2382                                    ..Default::default()
2383                                });
2384                                Ok(PluginCommand::OpenTerminalPaneInPlaceOfPaneId(
2385                                    pane_id.try_into()?,
2386                                    cwd,
2387                                    payload.close_replaced_pane,
2388                                ))
2389                            },
2390                            None => Err("Malformed open_terminal_pane_in_place_of_pane_id payload"),
2391                        }
2392                    },
2393                    _ => Err("Mismatched payload for OpenTerminalPaneInPlaceOfPaneId"),
2394                }
2395            },
2396            Some(CommandName::OpenEditPaneInPlaceOfPaneId) => {
2397                match protobuf_plugin_command.payload {
2398                    Some(Payload::OpenEditPaneInPlaceOfPaneIdPayload(payload)) => {
2399                        match (payload.pane_id_to_replace, payload.file_to_open) {
2400                            (Some(pane_id), Some(file_to_open)) => {
2401                                let context: BTreeMap<String, String> = payload
2402                                    .context
2403                                    .into_iter()
2404                                    .map(|e| (e.name, e.value))
2405                                    .collect();
2406                                Ok(PluginCommand::OpenEditPaneInPlaceOfPaneId(
2407                                    pane_id.try_into()?,
2408                                    file_to_open.try_into()?,
2409                                    payload.close_replaced_pane,
2410                                    context,
2411                                ))
2412                            },
2413                            _ => Err("Malformed open_edit_pane_in_place_of_pane_id payload"),
2414                        }
2415                    },
2416                    _ => Err("Mismatched payload for OpenEditPaneInPlaceOfPaneId"),
2417                }
2418            },
2419            Some(CommandName::HideFloatingPanes) => match protobuf_plugin_command.payload {
2420                Some(Payload::HideFloatingPanesPayload(payload)) => {
2421                    Ok(PluginCommand::HideFloatingPanes {
2422                        tab_id: payload.tab_id.map(|id| id as usize),
2423                    })
2424                },
2425                None => Ok(PluginCommand::HideFloatingPanes { tab_id: None }),
2426                _ => Err("Mismatched payload for HideFloatingPanes"),
2427            },
2428            Some(CommandName::ShowFloatingPanes) => match protobuf_plugin_command.payload {
2429                Some(Payload::ShowFloatingPanesPayload(payload)) => {
2430                    Ok(PluginCommand::ShowFloatingPanes {
2431                        tab_id: payload.tab_id.map(|id| id as usize),
2432                    })
2433                },
2434                None => Ok(PluginCommand::ShowFloatingPanes { tab_id: None }),
2435                _ => Err("Mismatched payload for ShowFloatingPanes"),
2436            },
2437            Some(CommandName::SetPaneColor) => match protobuf_plugin_command.payload {
2438                Some(Payload::SetPaneColorPayload(payload)) => match payload.pane_id {
2439                    Some(pane_id) => Ok(PluginCommand::SetPaneColor(
2440                        pane_id.try_into()?,
2441                        payload.fg,
2442                        payload.bg,
2443                    )),
2444                    None => Err("Malformed SetPaneColor payload"),
2445                },
2446                _ => Err("Mismatched payload for SetPaneColor"),
2447            },
2448            Some(CommandName::SetPaneRegexHighlights) => {
2449                match protobuf_plugin_command.payload {
2450                    Some(Payload::SetPaneRegexHighlightsPayload(p)) => {
2451                        let pane_id: PaneId = p
2452                            .pane_id
2453                            .ok_or("Missing pane_id in SetPaneRegexHighlights")?
2454                            .try_into()?;
2455                        let highlights = p
2456                            .highlights
2457                            .into_iter()
2458                            .map(|h| {
2459                                let style = match h.style.and_then(|s| s.style) {
2460                                    Some(ProtobufHighlightStyleVariant::Emphasis0(_)) => {
2461                                        HighlightStyle::Emphasis0
2462                                    },
2463                                    Some(ProtobufHighlightStyleVariant::Emphasis1(_)) => {
2464                                        HighlightStyle::Emphasis1
2465                                    },
2466                                    Some(ProtobufHighlightStyleVariant::Emphasis2(_)) => {
2467                                        HighlightStyle::Emphasis2
2468                                    },
2469                                    Some(ProtobufHighlightStyleVariant::Emphasis3(_)) => {
2470                                        HighlightStyle::Emphasis3
2471                                    },
2472                                    Some(ProtobufHighlightStyleVariant::CustomRgb(c)) => {
2473                                        HighlightStyle::CustomRgb {
2474                                            fg: match (c.fg_r, c.fg_g, c.fg_b) {
2475                                                (Some(r), Some(g), Some(b)) => {
2476                                                    Some((r as u8, g as u8, b as u8))
2477                                                },
2478                                                _ => None,
2479                                            },
2480                                            bg: match (c.bg_r, c.bg_g, c.bg_b) {
2481                                                (Some(r), Some(g), Some(b)) => {
2482                                                    Some((r as u8, g as u8, b as u8))
2483                                                },
2484                                                _ => None,
2485                                            },
2486                                        }
2487                                    },
2488                                    Some(ProtobufHighlightStyleVariant::CustomIndex(c)) => {
2489                                        HighlightStyle::CustomIndex {
2490                                            fg: c.fg.map(|v| v as u8),
2491                                            bg: c.bg.map(|v| v as u8),
2492                                        }
2493                                    },
2494                                    Some(ProtobufHighlightStyleVariant::BackgroundEmphasis0(_)) => {
2495                                        HighlightStyle::BackgroundEmphasis0
2496                                    },
2497                                    Some(ProtobufHighlightStyleVariant::BackgroundEmphasis1(_)) => {
2498                                        HighlightStyle::BackgroundEmphasis1
2499                                    },
2500                                    Some(ProtobufHighlightStyleVariant::BackgroundEmphasis2(_)) => {
2501                                        HighlightStyle::BackgroundEmphasis2
2502                                    },
2503                                    Some(ProtobufHighlightStyleVariant::BackgroundEmphasis3(_)) => {
2504                                        HighlightStyle::BackgroundEmphasis3
2505                                    },
2506                                    Some(ProtobufHighlightStyleVariant::None(_)) => {
2507                                        HighlightStyle::None
2508                                    },
2509                                    None => HighlightStyle::Emphasis0, // fallback
2510                                };
2511                                let context = h
2512                                    .context
2513                                    .into_iter()
2514                                    .map(|item| (item.name, item.value))
2515                                    .collect();
2516                                RegexHighlight {
2517                                    pattern: h.pattern,
2518                                    style,
2519                                    layer: match h.layer {
2520                                        x if x == ProtobufHighlightLayer::Tool as i32 => {
2521                                            HighlightLayer::Tool
2522                                        },
2523                                        x if x == ProtobufHighlightLayer::ActionFeedback as i32 => {
2524                                            HighlightLayer::ActionFeedback
2525                                        },
2526                                        _ => HighlightLayer::Hint, // 0 or unknown => Hint
2527                                    },
2528                                    context,
2529                                    on_hover: h.on_hover,
2530                                    bold: h.bold,
2531                                    italic: h.italic,
2532                                    underline: h.underline,
2533                                    tooltip_text: h.tooltip_text,
2534                                }
2535                            })
2536                            .collect();
2537                        Ok(PluginCommand::SetPaneRegexHighlights(pane_id, highlights))
2538                    },
2539                    _ => Err("Mismatched payload for SetPaneRegexHighlights"),
2540                }
2541            },
2542            Some(CommandName::ClearPaneHighlights) => match protobuf_plugin_command.payload {
2543                Some(Payload::ClearPaneHighlightsPayload(p)) => {
2544                    let pane_id: PaneId = p
2545                        .pane_id
2546                        .ok_or("Missing pane_id in ClearPaneHighlights")?
2547                        .try_into()?;
2548                    Ok(PluginCommand::ClearPaneHighlights(pane_id))
2549                },
2550                _ => Err("Mismatched payload for ClearPaneHighlights"),
2551            },
2552            Some(CommandName::OpenPluginPaneFloating) => match protobuf_plugin_command.payload {
2553                Some(Payload::OpenPluginPaneFloatingPayload(payload)) => {
2554                    let configuration: BTreeMap<String, String> =
2555                        payload.configuration.into_iter().collect();
2556                    let floating_pane_coordinates =
2557                        payload.floating_pane_coordinates.map(|f| f.into());
2558                    let context: BTreeMap<String, String> = payload
2559                        .context
2560                        .into_iter()
2561                        .map(|e| (e.name, e.value))
2562                        .collect();
2563                    Ok(PluginCommand::OpenPluginPaneFloating {
2564                        plugin_url: payload.plugin_url,
2565                        configuration,
2566                        floating_pane_coordinates,
2567                        context,
2568                    })
2569                },
2570                _ => Err("Mismatched payload for OpenPluginPaneFloating"),
2571            },
2572            None => Err("Unrecognized plugin command"),
2573        }
2574    }
2575}
2576
2577impl TryFrom<PluginCommand> for ProtobufPluginCommand {
2578    type Error = &'static str;
2579    fn try_from(plugin_command: PluginCommand) -> Result<Self, &'static str> {
2580        match plugin_command {
2581            PluginCommand::Subscribe(subscriptions) => {
2582                let subscriptions: ProtobufEventNameList = subscriptions.try_into()?;
2583                Ok(ProtobufPluginCommand {
2584                    name: CommandName::Subscribe as i32,
2585                    payload: Some(Payload::SubscribePayload(SubscribePayload {
2586                        subscriptions: Some(subscriptions),
2587                    })),
2588                })
2589            },
2590            PluginCommand::Unsubscribe(subscriptions) => {
2591                let subscriptions: ProtobufEventNameList = subscriptions.try_into()?;
2592                Ok(ProtobufPluginCommand {
2593                    name: CommandName::Unsubscribe as i32,
2594                    payload: Some(Payload::UnsubscribePayload(UnsubscribePayload {
2595                        subscriptions: Some(subscriptions),
2596                    })),
2597                })
2598            },
2599            PluginCommand::SetSelectable(should_be_selectable) => Ok(ProtobufPluginCommand {
2600                name: CommandName::SetSelectable as i32,
2601                payload: Some(Payload::SetSelectablePayload(should_be_selectable)),
2602            }),
2603            PluginCommand::ShowCursor(cursor_position) => {
2604                let position = cursor_position.map(|(x, y)| CursorPosition {
2605                    x: x as u32,
2606                    y: y as u32,
2607                });
2608                Ok(ProtobufPluginCommand {
2609                    name: CommandName::ShowCursor as i32,
2610                    payload: Some(Payload::ShowCursorPayload(ShowCursorPayload { position })),
2611                })
2612            },
2613            PluginCommand::GetPluginIds => Ok(ProtobufPluginCommand {
2614                name: CommandName::GetPluginIds as i32,
2615                payload: None,
2616            }),
2617            PluginCommand::GetZellijVersion => Ok(ProtobufPluginCommand {
2618                name: CommandName::GetZellijVersion as i32,
2619                payload: None,
2620            }),
2621            PluginCommand::OpenFile(file_to_open, context) => Ok(ProtobufPluginCommand {
2622                name: CommandName::OpenFile as i32,
2623                payload: Some(Payload::OpenFilePayload(OpenFilePayload {
2624                    file_to_open: Some(file_to_open.try_into()?),
2625                    floating_pane_coordinates: None,
2626                    context: context
2627                        .into_iter()
2628                        .map(|(name, value)| ContextItem { name, value })
2629                        .collect(),
2630                })),
2631            }),
2632            PluginCommand::OpenFileFloating(file_to_open, floating_pane_coordinates, context) => {
2633                Ok(ProtobufPluginCommand {
2634                    name: CommandName::OpenFileFloating as i32,
2635                    payload: Some(Payload::OpenFileFloatingPayload(OpenFilePayload {
2636                        file_to_open: Some(file_to_open.try_into()?),
2637                        floating_pane_coordinates: floating_pane_coordinates.map(|f| f.into()),
2638                        context: context
2639                            .into_iter()
2640                            .map(|(name, value)| ContextItem { name, value })
2641                            .collect(),
2642                    })),
2643                })
2644            },
2645            PluginCommand::OpenTerminal(cwd) => Ok(ProtobufPluginCommand {
2646                name: CommandName::OpenTerminal as i32,
2647                payload: Some(Payload::OpenTerminalPayload(OpenFilePayload {
2648                    file_to_open: Some(cwd.try_into()?),
2649                    floating_pane_coordinates: None,
2650                    context: vec![], // will be added in the future
2651                })),
2652            }),
2653            PluginCommand::OpenTerminalFloating(cwd, floating_pane_coordinates) => {
2654                Ok(ProtobufPluginCommand {
2655                    name: CommandName::OpenTerminalFloating as i32,
2656                    payload: Some(Payload::OpenTerminalFloatingPayload(OpenFilePayload {
2657                        file_to_open: Some(cwd.try_into()?),
2658                        floating_pane_coordinates: floating_pane_coordinates.map(|f| f.into()),
2659                        context: vec![], // will be added in the future
2660                    })),
2661                })
2662            },
2663            PluginCommand::OpenCommandPane(command_to_run, context) => {
2664                let context: Vec<_> = context
2665                    .into_iter()
2666                    .map(|(name, value)| ContextItem { name, value })
2667                    .collect();
2668                Ok(ProtobufPluginCommand {
2669                    name: CommandName::OpenCommandPane as i32,
2670                    payload: Some(Payload::OpenCommandPanePayload(OpenCommandPanePayload {
2671                        command_to_run: Some(command_to_run.try_into()?),
2672                        floating_pane_coordinates: None,
2673                        context,
2674                    })),
2675                })
2676            },
2677            PluginCommand::OpenCommandPaneFloating(
2678                command_to_run,
2679                floating_pane_coordinates,
2680                context,
2681            ) => {
2682                let context: Vec<_> = context
2683                    .into_iter()
2684                    .map(|(name, value)| ContextItem { name, value })
2685                    .collect();
2686                Ok(ProtobufPluginCommand {
2687                    name: CommandName::OpenCommandPaneFloating as i32,
2688                    payload: Some(Payload::OpenCommandPaneFloatingPayload(
2689                        OpenCommandPanePayload {
2690                            command_to_run: Some(command_to_run.try_into()?),
2691                            floating_pane_coordinates: floating_pane_coordinates.map(|f| f.into()),
2692                            context,
2693                        },
2694                    )),
2695                })
2696            },
2697            PluginCommand::SwitchTabTo(tab_index) => Ok(ProtobufPluginCommand {
2698                name: CommandName::SwitchTabTo as i32,
2699                payload: Some(Payload::SwitchTabToPayload(SwitchTabToPayload {
2700                    tab_index: tab_index,
2701                })),
2702            }),
2703            PluginCommand::SetTimeout(seconds) => Ok(ProtobufPluginCommand {
2704                name: CommandName::SetTimeout as i32,
2705                payload: Some(Payload::SetTimeoutPayload(SetTimeoutPayload { seconds })),
2706            }),
2707            PluginCommand::ExecCmd(command_line) => Ok(ProtobufPluginCommand {
2708                name: CommandName::ExecCmd as i32,
2709                payload: Some(Payload::ExecCmdPayload(ExecCmdPayload { command_line })),
2710            }),
2711            PluginCommand::PostMessageTo(plugin_message) => Ok(ProtobufPluginCommand {
2712                name: CommandName::PostMessageTo as i32,
2713                payload: Some(Payload::PostMessageToPayload(PluginMessagePayload {
2714                    message: Some(plugin_message.try_into()?),
2715                })),
2716            }),
2717            PluginCommand::PostMessageToPlugin(plugin_message) => Ok(ProtobufPluginCommand {
2718                name: CommandName::PostMessageToPlugin as i32,
2719                payload: Some(Payload::PostMessageToPluginPayload(PluginMessagePayload {
2720                    message: Some(plugin_message.try_into()?),
2721                })),
2722            }),
2723            PluginCommand::HideSelf => Ok(ProtobufPluginCommand {
2724                name: CommandName::HideSelf as i32,
2725                payload: None,
2726            }),
2727            PluginCommand::ShowSelf(should_float_if_hidden) => Ok(ProtobufPluginCommand {
2728                name: CommandName::ShowSelf as i32,
2729                payload: Some(Payload::ShowSelfPayload(should_float_if_hidden)),
2730            }),
2731            PluginCommand::SwitchToMode(input_mode) => Ok(ProtobufPluginCommand {
2732                name: CommandName::SwitchToMode as i32,
2733                payload: Some(Payload::SwitchToModePayload(SwitchToModePayload {
2734                    input_mode: ProtobufInputMode::try_from(input_mode)? as i32,
2735                })),
2736            }),
2737            PluginCommand::NewTabsWithLayout(raw_layout) => Ok(ProtobufPluginCommand {
2738                name: CommandName::NewTabsWithLayout as i32,
2739                payload: Some(Payload::NewTabsWithLayoutPayload(raw_layout)),
2740            }),
2741            PluginCommand::NewTab { name, cwd } => Ok(ProtobufPluginCommand {
2742                name: CommandName::NewTab as i32,
2743                payload: Some(Payload::NewTabPayload(NewTabPayload { name, cwd })),
2744            }),
2745            PluginCommand::GoToNextTab => Ok(ProtobufPluginCommand {
2746                name: CommandName::GoToNextTab as i32,
2747                payload: None,
2748            }),
2749            PluginCommand::GoToPreviousTab => Ok(ProtobufPluginCommand {
2750                name: CommandName::GoToPreviousTab as i32,
2751                payload: None,
2752            }),
2753            PluginCommand::Resize(resize) => Ok(ProtobufPluginCommand {
2754                name: CommandName::Resize as i32,
2755                payload: Some(Payload::ResizePayload(ResizePayload {
2756                    resize: Some(resize.try_into()?),
2757                })),
2758            }),
2759            PluginCommand::ResizeWithDirection(resize) => Ok(ProtobufPluginCommand {
2760                name: CommandName::ResizeWithDirection as i32,
2761                payload: Some(Payload::ResizeWithDirectionPayload(ResizePayload {
2762                    resize: Some(resize.try_into()?),
2763                })),
2764            }),
2765            PluginCommand::FocusNextPane => Ok(ProtobufPluginCommand {
2766                name: CommandName::FocusNextPane as i32,
2767                payload: None,
2768            }),
2769            PluginCommand::FocusPreviousPane => Ok(ProtobufPluginCommand {
2770                name: CommandName::FocusPreviousPane as i32,
2771                payload: None,
2772            }),
2773            PluginCommand::MoveFocus(direction) => Ok(ProtobufPluginCommand {
2774                name: CommandName::MoveFocus as i32,
2775                payload: Some(Payload::MoveFocusPayload(MovePayload {
2776                    direction: Some(direction.try_into()?),
2777                })),
2778            }),
2779            PluginCommand::MoveFocusOrTab(direction) => Ok(ProtobufPluginCommand {
2780                name: CommandName::MoveFocusOrTab as i32,
2781                payload: Some(Payload::MoveFocusOrTabPayload(MovePayload {
2782                    direction: Some(direction.try_into()?),
2783                })),
2784            }),
2785            PluginCommand::Detach => Ok(ProtobufPluginCommand {
2786                name: CommandName::Detach as i32,
2787                payload: None,
2788            }),
2789            PluginCommand::EditScrollback => Ok(ProtobufPluginCommand {
2790                name: CommandName::EditScrollback as i32,
2791                payload: None,
2792            }),
2793            PluginCommand::Write(bytes) => Ok(ProtobufPluginCommand {
2794                name: CommandName::Write as i32,
2795                payload: Some(Payload::WritePayload(bytes)),
2796            }),
2797            PluginCommand::WriteChars(chars) => Ok(ProtobufPluginCommand {
2798                name: CommandName::WriteChars as i32,
2799                payload: Some(Payload::WriteCharsPayload(chars)),
2800            }),
2801            PluginCommand::ToggleTab => Ok(ProtobufPluginCommand {
2802                name: CommandName::ToggleTab as i32,
2803                payload: None,
2804            }),
2805            PluginCommand::MovePane => Ok(ProtobufPluginCommand {
2806                name: CommandName::MovePane as i32,
2807                payload: None,
2808            }),
2809            PluginCommand::MovePaneWithDirection(direction) => Ok(ProtobufPluginCommand {
2810                name: CommandName::MovePaneWithDirection as i32,
2811                payload: Some(Payload::MovePaneWithDirectionPayload(MovePayload {
2812                    direction: Some(direction.try_into()?),
2813                })),
2814            }),
2815            PluginCommand::ClearScreen => Ok(ProtobufPluginCommand {
2816                name: CommandName::ClearScreen as i32,
2817                payload: None,
2818            }),
2819            PluginCommand::ScrollUp => Ok(ProtobufPluginCommand {
2820                name: CommandName::ScrollUp as i32,
2821                payload: None,
2822            }),
2823            PluginCommand::ScrollDown => Ok(ProtobufPluginCommand {
2824                name: CommandName::ScrollDown as i32,
2825                payload: None,
2826            }),
2827            PluginCommand::ScrollToTop => Ok(ProtobufPluginCommand {
2828                name: CommandName::ScrollToTop as i32,
2829                payload: None,
2830            }),
2831            PluginCommand::ScrollToBottom => Ok(ProtobufPluginCommand {
2832                name: CommandName::ScrollToBottom as i32,
2833                payload: None,
2834            }),
2835            PluginCommand::PageScrollUp => Ok(ProtobufPluginCommand {
2836                name: CommandName::PageScrollUp as i32,
2837                payload: None,
2838            }),
2839            PluginCommand::PageScrollDown => Ok(ProtobufPluginCommand {
2840                name: CommandName::PageScrollDown as i32,
2841                payload: None,
2842            }),
2843            PluginCommand::ToggleFocusFullscreen => Ok(ProtobufPluginCommand {
2844                name: CommandName::ToggleFocusFullscreen as i32,
2845                payload: None,
2846            }),
2847            PluginCommand::TogglePaneFrames => Ok(ProtobufPluginCommand {
2848                name: CommandName::TogglePaneFrames as i32,
2849                payload: None,
2850            }),
2851            PluginCommand::TogglePaneEmbedOrEject => Ok(ProtobufPluginCommand {
2852                name: CommandName::TogglePaneEmbedOrEject as i32,
2853                payload: None,
2854            }),
2855            PluginCommand::UndoRenamePane => Ok(ProtobufPluginCommand {
2856                name: CommandName::UndoRenamePane as i32,
2857                payload: None,
2858            }),
2859            PluginCommand::CloseFocus => Ok(ProtobufPluginCommand {
2860                name: CommandName::CloseFocus as i32,
2861                payload: None,
2862            }),
2863            PluginCommand::ToggleActiveTabSync => Ok(ProtobufPluginCommand {
2864                name: CommandName::ToggleActiveTabSync as i32,
2865                payload: None,
2866            }),
2867            PluginCommand::CloseFocusedTab => Ok(ProtobufPluginCommand {
2868                name: CommandName::CloseFocusedTab as i32,
2869                payload: None,
2870            }),
2871            PluginCommand::UndoRenameTab => Ok(ProtobufPluginCommand {
2872                name: CommandName::UndoRenameTab as i32,
2873                payload: None,
2874            }),
2875            PluginCommand::QuitZellij => Ok(ProtobufPluginCommand {
2876                name: CommandName::QuitZellij as i32,
2877                payload: None,
2878            }),
2879            PluginCommand::PreviousSwapLayout => Ok(ProtobufPluginCommand {
2880                name: CommandName::PreviousSwapLayout as i32,
2881                payload: None,
2882            }),
2883            PluginCommand::NextSwapLayout => Ok(ProtobufPluginCommand {
2884                name: CommandName::NextSwapLayout as i32,
2885                payload: None,
2886            }),
2887            PluginCommand::GoToTabName(tab_name) => Ok(ProtobufPluginCommand {
2888                name: CommandName::GoToTabName as i32,
2889                payload: Some(Payload::GoToTabNamePayload(tab_name)),
2890            }),
2891            PluginCommand::FocusOrCreateTab(tab_name) => Ok(ProtobufPluginCommand {
2892                name: CommandName::FocusOrCreateTab as i32,
2893                payload: Some(Payload::FocusOrCreateTabPayload(tab_name)),
2894            }),
2895            PluginCommand::GoToTab(tab_index) => Ok(ProtobufPluginCommand {
2896                name: CommandName::GoToTab as i32,
2897                payload: Some(Payload::GoToTabPayload(tab_index)),
2898            }),
2899            PluginCommand::StartOrReloadPlugin(url) => Ok(ProtobufPluginCommand {
2900                name: CommandName::StartOrReloadPlugin as i32,
2901                payload: Some(Payload::StartOrReloadPluginPayload(url)),
2902            }),
2903            PluginCommand::CloseTerminalPane(pane_id) => Ok(ProtobufPluginCommand {
2904                name: CommandName::CloseTerminalPane as i32,
2905                payload: Some(Payload::CloseTerminalPanePayload(pane_id)),
2906            }),
2907            PluginCommand::ClosePluginPane(pane_id) => Ok(ProtobufPluginCommand {
2908                name: CommandName::ClosePluginPane as i32,
2909                payload: Some(Payload::ClosePluginPanePayload(pane_id)),
2910            }),
2911            PluginCommand::FocusTerminalPane(
2912                pane_id,
2913                should_float_if_hidden,
2914                should_be_in_place_if_hidden,
2915            ) => Ok(ProtobufPluginCommand {
2916                name: CommandName::FocusTerminalPane as i32,
2917                payload: Some(Payload::FocusTerminalPanePayload(PaneIdAndShouldFloat {
2918                    pane_id: pane_id,
2919                    should_float: should_float_if_hidden,
2920                    should_be_in_place: should_be_in_place_if_hidden,
2921                })),
2922            }),
2923            PluginCommand::FocusPluginPane(
2924                pane_id,
2925                should_float_if_hidden,
2926                should_be_in_place_if_hidden,
2927            ) => Ok(ProtobufPluginCommand {
2928                name: CommandName::FocusPluginPane as i32,
2929                payload: Some(Payload::FocusPluginPanePayload(PaneIdAndShouldFloat {
2930                    pane_id: pane_id,
2931                    should_float: should_float_if_hidden,
2932                    should_be_in_place: should_be_in_place_if_hidden,
2933                })),
2934            }),
2935            PluginCommand::RenameTerminalPane(pane_id, new_name) => Ok(ProtobufPluginCommand {
2936                name: CommandName::RenameTerminalPane as i32,
2937                payload: Some(Payload::RenameTerminalPanePayload(IdAndNewName {
2938                    id: pane_id,
2939                    new_name,
2940                })),
2941            }),
2942            PluginCommand::RenamePluginPane(pane_id, new_name) => Ok(ProtobufPluginCommand {
2943                name: CommandName::RenamePluginPane as i32,
2944                payload: Some(Payload::RenamePluginPanePayload(IdAndNewName {
2945                    id: pane_id,
2946                    new_name,
2947                })),
2948            }),
2949            PluginCommand::RenameTab(tab_index, new_name) => Ok(ProtobufPluginCommand {
2950                name: CommandName::RenameTab as i32,
2951                payload: Some(Payload::RenameTabPayload(IdAndNewName {
2952                    id: tab_index,
2953                    new_name,
2954                })),
2955            }),
2956            PluginCommand::ReportPanic(payload) => Ok(ProtobufPluginCommand {
2957                name: CommandName::ReportCrash as i32,
2958                payload: Some(Payload::ReportCrashPayload(payload)),
2959            }),
2960            PluginCommand::RequestPluginPermissions(permissions) => Ok(ProtobufPluginCommand {
2961                name: CommandName::RequestPluginPermissions as i32,
2962                payload: Some(Payload::RequestPluginPermissionPayload(
2963                    RequestPluginPermissionPayload {
2964                        permissions: permissions
2965                            .iter()
2966                            .filter_map(|p| ProtobufPermissionType::try_from(*p).ok())
2967                            .map(|p| p as i32)
2968                            .collect(),
2969                    },
2970                )),
2971            }),
2972            PluginCommand::SwitchSession(switch_to_session) => Ok(ProtobufPluginCommand {
2973                name: CommandName::SwitchSession as i32,
2974                payload: Some(Payload::SwitchSessionPayload(SwitchSessionPayload {
2975                    name: switch_to_session.name,
2976                    tab_position: switch_to_session.tab_position.map(|t| t as u32),
2977                    pane_id: switch_to_session.pane_id.map(|p| p.0),
2978                    pane_id_is_plugin: switch_to_session.pane_id.map(|p| p.1),
2979                    layout: switch_to_session.layout.and_then(|l| l.try_into().ok()),
2980                    cwd: switch_to_session.cwd.map(|c| c.display().to_string()),
2981                })),
2982            }),
2983            PluginCommand::OpenTerminalInPlace(cwd) => Ok(ProtobufPluginCommand {
2984                name: CommandName::OpenTerminalInPlace as i32,
2985                payload: Some(Payload::OpenTerminalInPlacePayload(OpenFilePayload {
2986                    file_to_open: Some(cwd.try_into()?),
2987                    floating_pane_coordinates: None,
2988                    context: vec![], // will be added in the future
2989                })),
2990            }),
2991            PluginCommand::OpenFileInPlace(file_to_open, context) => Ok(ProtobufPluginCommand {
2992                name: CommandName::OpenFileInPlace as i32,
2993                payload: Some(Payload::OpenFileInPlacePayload(OpenFilePayload {
2994                    file_to_open: Some(file_to_open.try_into()?),
2995                    floating_pane_coordinates: None,
2996                    context: context
2997                        .into_iter()
2998                        .map(|(name, value)| ContextItem { name, value })
2999                        .collect(),
3000                })),
3001            }),
3002            PluginCommand::OpenCommandPaneInPlace(command_to_run, context) => {
3003                let context: Vec<_> = context
3004                    .into_iter()
3005                    .map(|(name, value)| ContextItem { name, value })
3006                    .collect();
3007                Ok(ProtobufPluginCommand {
3008                    name: CommandName::OpenCommandInPlace as i32,
3009                    payload: Some(Payload::OpenCommandPaneInPlacePayload(
3010                        OpenCommandPanePayload {
3011                            command_to_run: Some(command_to_run.try_into()?),
3012                            floating_pane_coordinates: None,
3013                            context,
3014                        },
3015                    )),
3016                })
3017            },
3018            PluginCommand::RunCommand(command_line, env_variables, cwd, context) => {
3019                let env_variables: Vec<_> = env_variables
3020                    .into_iter()
3021                    .map(|(name, value)| EnvVariable { name, value })
3022                    .collect();
3023                let context: Vec<_> = context
3024                    .into_iter()
3025                    .map(|(name, value)| ContextItem { name, value })
3026                    .collect();
3027                let cwd = cwd.display().to_string();
3028                Ok(ProtobufPluginCommand {
3029                    name: CommandName::RunCommand as i32,
3030                    payload: Some(Payload::RunCommandPayload(RunCommandPayload {
3031                        command_line,
3032                        env_variables,
3033                        cwd,
3034                        context,
3035                    })),
3036                })
3037            },
3038            PluginCommand::WebRequest(url, verb, headers, body, context) => {
3039                let context: Vec<_> = context
3040                    .into_iter()
3041                    .map(|(name, value)| ContextItem { name, value })
3042                    .collect();
3043                let headers: Vec<_> = headers
3044                    .into_iter()
3045                    .map(|(name, value)| Header { name, value })
3046                    .collect();
3047                let verb: ProtobufHttpVerb = verb.into();
3048                Ok(ProtobufPluginCommand {
3049                    name: CommandName::WebRequest as i32,
3050                    payload: Some(Payload::WebRequestPayload(WebRequestPayload {
3051                        url,
3052                        verb: verb as i32,
3053                        body,
3054                        headers,
3055                        context,
3056                    })),
3057                })
3058            },
3059            PluginCommand::DeleteDeadSession(dead_session_name) => Ok(ProtobufPluginCommand {
3060                name: CommandName::DeleteDeadSession as i32,
3061                payload: Some(Payload::DeleteDeadSessionPayload(dead_session_name)),
3062            }),
3063            PluginCommand::DeleteAllDeadSessions => Ok(ProtobufPluginCommand {
3064                name: CommandName::DeleteAllDeadSessions as i32,
3065                payload: None,
3066            }),
3067            PluginCommand::RenameSession(new_session_name) => Ok(ProtobufPluginCommand {
3068                name: CommandName::RenameSession as i32,
3069                payload: Some(Payload::RenameSessionPayload(new_session_name)),
3070            }),
3071            PluginCommand::UnblockCliPipeInput(pipe_name) => Ok(ProtobufPluginCommand {
3072                name: CommandName::UnblockCliPipeInput as i32,
3073                payload: Some(Payload::UnblockCliPipeInputPayload(pipe_name)),
3074            }),
3075            PluginCommand::BlockCliPipeInput(pipe_name) => Ok(ProtobufPluginCommand {
3076                name: CommandName::BlockCliPipeInput as i32,
3077                payload: Some(Payload::BlockCliPipeInputPayload(pipe_name)),
3078            }),
3079            PluginCommand::CliPipeOutput(pipe_name, output) => Ok(ProtobufPluginCommand {
3080                name: CommandName::CliPipeOutput as i32,
3081                payload: Some(Payload::CliPipeOutputPayload(CliPipeOutputPayload {
3082                    pipe_name,
3083                    output,
3084                })),
3085            }),
3086            PluginCommand::MessageToPlugin(message_to_plugin) => {
3087                let plugin_config: Vec<_> = message_to_plugin
3088                    .plugin_config
3089                    .into_iter()
3090                    .map(|(name, value)| ContextItem { name, value })
3091                    .collect();
3092                let message_args: Vec<_> = message_to_plugin
3093                    .message_args
3094                    .into_iter()
3095                    .map(|(name, value)| ContextItem { name, value })
3096                    .collect();
3097                Ok(ProtobufPluginCommand {
3098                    name: CommandName::MessageToPlugin as i32,
3099                    payload: Some(Payload::MessageToPluginPayload(MessageToPluginPayload {
3100                        plugin_url: message_to_plugin.plugin_url,
3101                        plugin_config,
3102                        message_name: message_to_plugin.message_name,
3103                        message_payload: message_to_plugin.message_payload,
3104                        message_args,
3105                        new_plugin_args: message_to_plugin.new_plugin_args.map(|m_t_p| {
3106                            ProtobufNewPluginArgs {
3107                                should_float: m_t_p.should_float,
3108                                pane_id_to_replace: m_t_p
3109                                    .pane_id_to_replace
3110                                    .and_then(|p_id| ProtobufPaneId::try_from(p_id).ok()),
3111                                pane_title: m_t_p.pane_title,
3112                                cwd: m_t_p.cwd.map(|cwd| cwd.display().to_string()),
3113                                skip_cache: m_t_p.skip_cache,
3114                                should_focus: m_t_p.should_focus,
3115                            }
3116                        }),
3117                        destination_plugin_id: message_to_plugin.destination_plugin_id,
3118                        floating_pane_coordinates: message_to_plugin
3119                            .floating_pane_coordinates
3120                            .and_then(|f| f.try_into().ok()),
3121                    })),
3122                })
3123            },
3124            PluginCommand::DisconnectOtherClients => Ok(ProtobufPluginCommand {
3125                name: CommandName::DisconnectOtherClients as i32,
3126                payload: None,
3127            }),
3128            PluginCommand::KillSessions(session_names) => Ok(ProtobufPluginCommand {
3129                name: CommandName::KillSessions as i32,
3130                payload: Some(Payload::KillSessionsPayload(KillSessionsPayload {
3131                    session_names,
3132                })),
3133            }),
3134            PluginCommand::ScanHostFolder(folder_to_scan) => Ok(ProtobufPluginCommand {
3135                name: CommandName::ScanHostFolder as i32,
3136                payload: Some(Payload::ScanHostFolderPayload(
3137                    folder_to_scan.display().to_string(),
3138                )),
3139            }),
3140            PluginCommand::WatchFilesystem => Ok(ProtobufPluginCommand {
3141                name: CommandName::WatchFilesystem as i32,
3142                payload: None,
3143            }),
3144            PluginCommand::ListWindowsVolumes => Ok(ProtobufPluginCommand {
3145                name: CommandName::ListWindowsVolumes as i32,
3146                payload: None,
3147            }),
3148            PluginCommand::DumpSessionLayout { tab_index } => Ok(ProtobufPluginCommand {
3149                name: CommandName::DumpSessionLayout as i32,
3150                payload: tab_index.map(|idx| {
3151                    Payload::DumpSessionLayoutPayload(DumpSessionLayoutPayload {
3152                        tab_index: Some(idx as u32),
3153                    })
3154                }),
3155            }),
3156            PluginCommand::CloseSelf => Ok(ProtobufPluginCommand {
3157                name: CommandName::CloseSelf as i32,
3158                payload: None,
3159            }),
3160            PluginCommand::NewTabsWithLayoutInfo(new_tabs_with_layout_info_payload) => {
3161                Ok(ProtobufPluginCommand {
3162                    name: CommandName::NewTabsWithLayoutInfo as i32,
3163                    payload: Some(Payload::NewTabsWithLayoutInfoPayload(
3164                        NewTabsWithLayoutInfoPayload {
3165                            layout_info: new_tabs_with_layout_info_payload.try_into().ok(),
3166                        },
3167                    )),
3168                })
3169            },
3170            PluginCommand::Reconfigure(config, write_to_disk) => Ok(ProtobufPluginCommand {
3171                name: CommandName::Reconfigure as i32,
3172                payload: Some(Payload::ReconfigurePayload(ReconfigurePayload {
3173                    config,
3174                    write_to_disk,
3175                })),
3176            }),
3177            PluginCommand::HidePaneWithId(pane_id_to_hide) => Ok(ProtobufPluginCommand {
3178                name: CommandName::HidePaneWithId as i32,
3179                payload: Some(Payload::HidePaneWithIdPayload(HidePaneWithIdPayload {
3180                    pane_id: ProtobufPaneId::try_from(pane_id_to_hide).ok(),
3181                })),
3182            }),
3183            PluginCommand::ShowPaneWithId(
3184                pane_id_to_show,
3185                should_float_if_hidden,
3186                should_focus_pane,
3187            ) => Ok(ProtobufPluginCommand {
3188                name: CommandName::ShowPaneWithId as i32,
3189                payload: Some(Payload::ShowPaneWithIdPayload(ShowPaneWithIdPayload {
3190                    pane_id: ProtobufPaneId::try_from(pane_id_to_show).ok(),
3191                    should_float_if_hidden,
3192                    should_focus_pane,
3193                })),
3194            }),
3195            PluginCommand::OpenCommandPaneBackground(command_to_run, context) => {
3196                let context: Vec<_> = context
3197                    .into_iter()
3198                    .map(|(name, value)| ContextItem { name, value })
3199                    .collect();
3200                Ok(ProtobufPluginCommand {
3201                    name: CommandName::OpenCommandPaneBackground as i32,
3202                    payload: Some(Payload::OpenCommandPaneBackgroundPayload(
3203                        OpenCommandPanePayload {
3204                            command_to_run: Some(command_to_run.try_into()?),
3205                            floating_pane_coordinates: None,
3206                            context,
3207                        },
3208                    )),
3209                })
3210            },
3211            PluginCommand::RerunCommandPane(terminal_pane_id) => Ok(ProtobufPluginCommand {
3212                name: CommandName::RerunCommandPane as i32,
3213                payload: Some(Payload::RerunCommandPanePayload(RerunCommandPanePayload {
3214                    terminal_pane_id,
3215                })),
3216            }),
3217            PluginCommand::ResizePaneIdWithDirection(resize, pane_id) => {
3218                Ok(ProtobufPluginCommand {
3219                    name: CommandName::ResizePaneIdWithDirection as i32,
3220                    payload: Some(Payload::ResizePaneIdWithDirectionPayload(
3221                        ResizePaneIdWithDirectionPayload {
3222                            resize: Some(resize.try_into()?),
3223                            pane_id: Some(pane_id.try_into()?),
3224                        },
3225                    )),
3226                })
3227            },
3228            PluginCommand::EditScrollbackForPaneWithId(pane_id) => Ok(ProtobufPluginCommand {
3229                name: CommandName::EditScrollbackForPaneWithId as i32,
3230                payload: Some(Payload::EditScrollbackForPaneWithIdPayload(
3231                    EditScrollbackForPaneWithIdPayload {
3232                        pane_id: Some(pane_id.try_into()?),
3233                    },
3234                )),
3235            }),
3236            PluginCommand::GetPaneScrollback {
3237                pane_id,
3238                get_full_scrollback,
3239            } => Ok(ProtobufPluginCommand {
3240                name: CommandName::GetPaneScrollback as i32,
3241                payload: Some(Payload::GetPaneScrollbackPayload(
3242                    GetPaneScrollbackPayload {
3243                        pane_id: Some(pane_id.try_into()?),
3244                        get_full_scrollback,
3245                    },
3246                )),
3247            }),
3248            PluginCommand::WriteToPaneId(bytes_to_write, pane_id) => Ok(ProtobufPluginCommand {
3249                name: CommandName::WriteToPaneId as i32,
3250                payload: Some(Payload::WriteToPaneIdPayload(WriteToPaneIdPayload {
3251                    bytes_to_write,
3252                    pane_id: Some(pane_id.try_into()?),
3253                })),
3254            }),
3255            PluginCommand::WriteCharsToPaneId(chars_to_write, pane_id) => {
3256                Ok(ProtobufPluginCommand {
3257                    name: CommandName::WriteCharsToPaneId as i32,
3258                    payload: Some(Payload::WriteCharsToPaneIdPayload(
3259                        WriteCharsToPaneIdPayload {
3260                            chars_to_write,
3261                            pane_id: Some(pane_id.try_into()?),
3262                        },
3263                    )),
3264                })
3265            },
3266            PluginCommand::SendSigintToPaneId(pane_id) => Ok(ProtobufPluginCommand {
3267                name: CommandName::SendSigintToPaneId as i32,
3268                payload: Some(Payload::SendSigintToPaneIdPayload(pane_id.try_into()?)),
3269            }),
3270            PluginCommand::SendSigkillToPaneId(pane_id) => Ok(ProtobufPluginCommand {
3271                name: CommandName::SendSigkillToPaneId as i32,
3272                payload: Some(Payload::SendSigkillToPaneIdPayload(pane_id.try_into()?)),
3273            }),
3274            PluginCommand::GetPanePid { pane_id } => Ok(ProtobufPluginCommand {
3275                name: CommandName::GetPanePid as i32,
3276                payload: Some(Payload::GetPanePidPayload(GetPanePidPayload {
3277                    pane_id: Some(pane_id.try_into()?),
3278                })),
3279            }),
3280            PluginCommand::GetPaneRunningCommand { pane_id } => Ok(ProtobufPluginCommand {
3281                name: CommandName::GetPaneRunningCommand as i32,
3282                payload: Some(Payload::GetPaneRunningCommandPayload(
3283                    ProtobufGetPaneRunningCommandPayload {
3284                        pane_id: Some(pane_id.try_into()?),
3285                    },
3286                )),
3287            }),
3288            PluginCommand::GetPaneCwd { pane_id } => Ok(ProtobufPluginCommand {
3289                name: CommandName::GetPaneCwd as i32,
3290                payload: Some(Payload::GetPaneCwdPayload(ProtobufGetPaneCwdPayload {
3291                    pane_id: Some(pane_id.try_into()?),
3292                })),
3293            }),
3294            PluginCommand::OverrideLayout(
3295                layout_info,
3296                retain_existing_terminal_panes,
3297                retain_existing_plugin_panes,
3298                apply_only_to_active_tab,
3299                context,
3300            ) => Ok(ProtobufPluginCommand {
3301                name: CommandName::OverrideLayout as i32,
3302                payload: Some(Payload::OverrideLayoutPayload(OverrideLayoutPayload {
3303                    layout_info: layout_info.try_into().ok(),
3304                    context: context
3305                        .into_iter()
3306                        .map(|(name, value)| ContextItem { name, value })
3307                        .collect(),
3308                    retain_existing_terminal_panes,
3309                    retain_existing_plugin_panes,
3310                    apply_only_to_active_tab,
3311                })),
3312            }),
3313            PluginCommand::SaveLayout {
3314                layout_name,
3315                layout_kdl,
3316                overwrite,
3317            } => Ok(ProtobufPluginCommand {
3318                name: CommandName::SaveLayout as i32,
3319                payload: Some(Payload::SaveLayoutPayload(SaveLayoutPayload {
3320                    layout_name,
3321                    layout_kdl,
3322                    overwrite,
3323                })),
3324            }),
3325            PluginCommand::DeleteLayout { layout_name } => Ok(ProtobufPluginCommand {
3326                name: CommandName::DeleteLayout as i32,
3327                payload: Some(Payload::DeleteLayoutPayload(DeleteLayoutPayload {
3328                    layout_name,
3329                })),
3330            }),
3331            PluginCommand::RenameLayout {
3332                old_layout_name,
3333                new_layout_name,
3334            } => Ok(ProtobufPluginCommand {
3335                name: CommandName::RenameLayout as i32,
3336                payload: Some(Payload::RenameLayoutPayload(RenameLayoutPayload {
3337                    old_layout_name,
3338                    new_layout_name,
3339                })),
3340            }),
3341            PluginCommand::EditLayout {
3342                layout_name,
3343                context,
3344            } => {
3345                let context: Vec<_> = context
3346                    .into_iter()
3347                    .map(|(name, value)| ContextItem { name, value })
3348                    .collect();
3349                Ok(ProtobufPluginCommand {
3350                    name: CommandName::EditLayout as i32,
3351                    payload: Some(Payload::EditLayoutPayload(EditLayoutPayload {
3352                        layout_name,
3353                        context,
3354                    })),
3355                })
3356            },
3357            PluginCommand::MovePaneWithPaneId(pane_id) => Ok(ProtobufPluginCommand {
3358                name: CommandName::MovePaneWithPaneId as i32,
3359                payload: Some(Payload::MovePaneWithPaneIdPayload(
3360                    MovePaneWithPaneIdPayload {
3361                        pane_id: Some(pane_id.try_into()?),
3362                    },
3363                )),
3364            }),
3365            PluginCommand::MovePaneWithPaneIdInDirection(pane_id, direction) => {
3366                Ok(ProtobufPluginCommand {
3367                    name: CommandName::MovePaneWithPaneIdInDirection as i32,
3368                    payload: Some(Payload::MovePaneWithPaneIdInDirectionPayload(
3369                        MovePaneWithPaneIdInDirectionPayload {
3370                            pane_id: Some(pane_id.try_into()?),
3371                            direction: Some(direction.try_into()?),
3372                        },
3373                    )),
3374                })
3375            },
3376            PluginCommand::ClearScreenForPaneId(pane_id) => Ok(ProtobufPluginCommand {
3377                name: CommandName::ClearScreenForPaneId as i32,
3378                payload: Some(Payload::ClearScreenForPaneIdPayload(
3379                    ClearScreenForPaneIdPayload {
3380                        pane_id: Some(pane_id.try_into()?),
3381                    },
3382                )),
3383            }),
3384            PluginCommand::ScrollUpInPaneId(pane_id) => Ok(ProtobufPluginCommand {
3385                name: CommandName::ScrollUpInPaneId as i32,
3386                payload: Some(Payload::ScrollUpInPaneIdPayload(ScrollUpInPaneIdPayload {
3387                    pane_id: Some(pane_id.try_into()?),
3388                })),
3389            }),
3390            PluginCommand::ScrollDownInPaneId(pane_id) => Ok(ProtobufPluginCommand {
3391                name: CommandName::ScrollDownInPaneId as i32,
3392                payload: Some(Payload::ScrollDownInPaneIdPayload(
3393                    ScrollDownInPaneIdPayload {
3394                        pane_id: Some(pane_id.try_into()?),
3395                    },
3396                )),
3397            }),
3398            PluginCommand::ScrollToTopInPaneId(pane_id) => Ok(ProtobufPluginCommand {
3399                name: CommandName::ScrollToTopInPaneId as i32,
3400                payload: Some(Payload::ScrollToTopInPaneIdPayload(
3401                    ScrollToTopInPaneIdPayload {
3402                        pane_id: Some(pane_id.try_into()?),
3403                    },
3404                )),
3405            }),
3406            PluginCommand::ScrollToBottomInPaneId(pane_id) => Ok(ProtobufPluginCommand {
3407                name: CommandName::ScrollToBottomInPaneId as i32,
3408                payload: Some(Payload::ScrollToBottomInPaneIdPayload(
3409                    ScrollToBottomInPaneIdPayload {
3410                        pane_id: Some(pane_id.try_into()?),
3411                    },
3412                )),
3413            }),
3414            PluginCommand::PageScrollUpInPaneId(pane_id) => Ok(ProtobufPluginCommand {
3415                name: CommandName::PageScrollUpInPaneId as i32,
3416                payload: Some(Payload::PageScrollUpInPaneIdPayload(
3417                    PageScrollUpInPaneIdPayload {
3418                        pane_id: Some(pane_id.try_into()?),
3419                    },
3420                )),
3421            }),
3422            PluginCommand::PageScrollDownInPaneId(pane_id) => Ok(ProtobufPluginCommand {
3423                name: CommandName::PageScrollDownInPaneId as i32,
3424                payload: Some(Payload::PageScrollDownInPaneIdPayload(
3425                    PageScrollDownInPaneIdPayload {
3426                        pane_id: Some(pane_id.try_into()?),
3427                    },
3428                )),
3429            }),
3430            PluginCommand::TogglePaneIdFullscreen(pane_id) => Ok(ProtobufPluginCommand {
3431                name: CommandName::TogglePaneIdFullscreen as i32,
3432                payload: Some(Payload::TogglePaneIdFullscreenPayload(
3433                    TogglePaneIdFullscreenPayload {
3434                        pane_id: Some(pane_id.try_into()?),
3435                    },
3436                )),
3437            }),
3438            PluginCommand::TogglePaneEmbedOrEjectForPaneId(pane_id) => Ok(ProtobufPluginCommand {
3439                name: CommandName::TogglePaneEmbedOrEjectForPaneId as i32,
3440                payload: Some(Payload::TogglePaneEmbedOrEjectForPaneIdPayload(
3441                    TogglePaneEmbedOrEjectForPaneIdPayload {
3442                        pane_id: Some(pane_id.try_into()?),
3443                    },
3444                )),
3445            }),
3446            PluginCommand::CloseTabWithIndex(tab_index) => Ok(ProtobufPluginCommand {
3447                name: CommandName::CloseTabWithIndex as i32,
3448                payload: Some(Payload::CloseTabWithIndexPayload(
3449                    CloseTabWithIndexPayload {
3450                        tab_index: tab_index as u32,
3451                    },
3452                )),
3453            }),
3454            PluginCommand::BreakPanesToNewTab(
3455                pane_ids,
3456                new_tab_name,
3457                should_change_focus_to_new_tab,
3458            ) => Ok(ProtobufPluginCommand {
3459                name: CommandName::BreakPanesToNewTab as i32,
3460                payload: Some(Payload::BreakPanesToNewTabPayload(
3461                    BreakPanesToNewTabPayload {
3462                        pane_ids: pane_ids
3463                            .into_iter()
3464                            .filter_map(|p_id| p_id.try_into().ok())
3465                            .collect(),
3466                        should_change_focus_to_new_tab,
3467                        new_tab_name,
3468                    },
3469                )),
3470            }),
3471            PluginCommand::BreakPanesToTabWithIndex(
3472                pane_ids,
3473                tab_index,
3474                should_change_focus_to_target_tab,
3475            ) => Ok(ProtobufPluginCommand {
3476                name: CommandName::BreakPanesToTabWithIndex as i32,
3477                payload: Some(Payload::BreakPanesToTabWithIndexPayload(
3478                    BreakPanesToTabWithIndexPayload {
3479                        pane_ids: pane_ids
3480                            .into_iter()
3481                            .filter_map(|p_id| p_id.try_into().ok())
3482                            .collect(),
3483                        tab_index: tab_index as u32,
3484                        should_change_focus_to_target_tab,
3485                    },
3486                )),
3487            }),
3488            PluginCommand::SwitchTabToId(tab_id) => Ok(ProtobufPluginCommand {
3489                name: CommandName::SwitchTabToId as i32,
3490                payload: Some(Payload::SwitchTabToIdPayload(SwitchTabToIdPayload {
3491                    tab_id,
3492                })),
3493            }),
3494            PluginCommand::GoToTabWithId(tab_id) => Ok(ProtobufPluginCommand {
3495                name: CommandName::GoToTabWithId as i32,
3496                payload: Some(Payload::GoToTabWithIdPayload(GoToTabWithIdPayload {
3497                    tab_id,
3498                })),
3499            }),
3500            PluginCommand::CloseTabWithId(tab_id) => Ok(ProtobufPluginCommand {
3501                name: CommandName::CloseTabWithId as i32,
3502                payload: Some(Payload::CloseTabWithIdPayload(CloseTabWithIdPayload {
3503                    tab_id,
3504                })),
3505            }),
3506            PluginCommand::RenameTabWithId(tab_id, new_name) => Ok(ProtobufPluginCommand {
3507                name: CommandName::RenameTabWithId as i32,
3508                payload: Some(Payload::RenameTabWithIdPayload(RenameTabWithIdPayload {
3509                    tab_id,
3510                    new_name,
3511                })),
3512            }),
3513            PluginCommand::BreakPanesToTabWithId(
3514                pane_ids,
3515                tab_id,
3516                should_change_focus_to_target_tab,
3517            ) => Ok(ProtobufPluginCommand {
3518                name: CommandName::BreakPanesToTabWithId as i32,
3519                payload: Some(Payload::BreakPanesToTabWithIdPayload(
3520                    BreakPanesToTabWithIdPayload {
3521                        pane_ids: pane_ids
3522                            .into_iter()
3523                            .filter_map(|p_id| p_id.try_into().ok())
3524                            .collect(),
3525                        tab_id,
3526                        should_change_focus_to_target_tab,
3527                    },
3528                )),
3529            }),
3530            PluginCommand::ReloadPlugin(plugin_id) => Ok(ProtobufPluginCommand {
3531                name: CommandName::ReloadPlugin as i32,
3532                payload: Some(Payload::ReloadPluginPayload(ReloadPluginPayload {
3533                    plugin_id,
3534                })),
3535            }),
3536            PluginCommand::LoadNewPlugin {
3537                url,
3538                config,
3539                load_in_background,
3540                skip_plugin_cache,
3541            } => Ok(ProtobufPluginCommand {
3542                name: CommandName::LoadNewPlugin as i32,
3543                payload: Some(Payload::LoadNewPluginPayload(LoadNewPluginPayload {
3544                    plugin_url: url,
3545                    plugin_config: config
3546                        .into_iter()
3547                        .map(|(name, value)| ContextItem { name, value })
3548                        .collect(),
3549                    should_skip_plugin_cache: skip_plugin_cache,
3550                    should_load_plugin_in_background: load_in_background,
3551                })),
3552            }),
3553            PluginCommand::RebindKeys {
3554                keys_to_rebind,
3555                keys_to_unbind,
3556                write_config_to_disk,
3557            } => Ok(ProtobufPluginCommand {
3558                name: CommandName::RebindKeys as i32,
3559                payload: Some(Payload::RebindKeysPayload(RebindKeysPayload {
3560                    keys_to_rebind: keys_to_rebind
3561                        .into_iter()
3562                        .filter_map(|k| k.try_into().ok())
3563                        .collect(),
3564                    keys_to_unbind: keys_to_unbind
3565                        .into_iter()
3566                        .filter_map(|k| k.try_into().ok())
3567                        .collect(),
3568                    write_config_to_disk,
3569                })),
3570            }),
3571            PluginCommand::ListClients => Ok(ProtobufPluginCommand {
3572                name: CommandName::ListClients as i32,
3573                payload: None,
3574            }),
3575            PluginCommand::ChangeHostFolder(new_host_folder) => Ok(ProtobufPluginCommand {
3576                name: CommandName::ChangeHostFolder as i32,
3577                payload: Some(Payload::ChangeHostFolderPayload(ChangeHostFolderPayload {
3578                    new_host_folder: new_host_folder.display().to_string(), // TODO: not accurate?
3579                })),
3580            }),
3581            PluginCommand::SetFloatingPanePinned(pane_id, should_be_pinned) => {
3582                Ok(ProtobufPluginCommand {
3583                    name: CommandName::SetFloatingPanePinned as i32,
3584                    payload: Some(Payload::SetFloatingPanePinnedPayload(
3585                        SetFloatingPanePinnedPayload {
3586                            pane_id: pane_id.try_into().ok(),
3587                            should_be_pinned,
3588                        },
3589                    )),
3590                })
3591            },
3592            PluginCommand::StackPanes(pane_ids) => Ok(ProtobufPluginCommand {
3593                name: CommandName::StackPanes as i32,
3594                payload: Some(Payload::StackPanesPayload(StackPanesPayload {
3595                    pane_ids: pane_ids
3596                        .into_iter()
3597                        .filter_map(|p_id| p_id.try_into().ok())
3598                        .collect(),
3599                })),
3600            }),
3601            PluginCommand::ChangeFloatingPanesCoordinates(
3602                pane_ids_and_floating_panes_coordinates,
3603            ) => Ok(ProtobufPluginCommand {
3604                name: CommandName::ChangeFloatingPanesCoordinates as i32,
3605                payload: Some(Payload::ChangeFloatingPanesCoordinatesPayload(
3606                    ChangeFloatingPanesCoordinatesPayload {
3607                        pane_ids_and_floating_panes_coordinates:
3608                            pane_ids_and_floating_panes_coordinates
3609                                .into_iter()
3610                                .filter_map(|(p_id, floating_pane_coordinates)| {
3611                                    Some(PaneIdAndFloatingPaneCoordinates {
3612                                        pane_id: Some(p_id.try_into().ok()?),
3613                                        floating_pane_coordinates: Some(
3614                                            floating_pane_coordinates.try_into().ok()?,
3615                                        ),
3616                                    })
3617                                })
3618                                .collect(),
3619                    },
3620                )),
3621            }),
3622            PluginCommand::TogglePaneBorderless(pane_id) => Ok(ProtobufPluginCommand {
3623                name: CommandName::TogglePaneBorderless as i32,
3624                payload: Some(Payload::TogglePaneBorderlessPayload(
3625                    TogglePaneBorderlessPayload {
3626                        pane_id: Some(pane_id.try_into()?),
3627                    },
3628                )),
3629            }),
3630            PluginCommand::SetPaneBorderless(pane_id, borderless) => Ok(ProtobufPluginCommand {
3631                name: CommandName::SetPaneBorderless as i32,
3632                payload: Some(Payload::SetPaneBorderlessPayload(
3633                    SetPaneBorderlessPayload {
3634                        pane_id: Some(pane_id.try_into()?),
3635                        borderless,
3636                    },
3637                )),
3638            }),
3639            PluginCommand::OpenCommandPaneNearPlugin(command_to_run, context) => {
3640                let context: Vec<_> = context
3641                    .into_iter()
3642                    .map(|(name, value)| ContextItem { name, value })
3643                    .collect();
3644                Ok(ProtobufPluginCommand {
3645                    name: CommandName::OpenCommandPaneNearPlugin as i32,
3646                    payload: Some(Payload::OpenCommandPaneNearPluginPayload(
3647                        OpenCommandPaneNearPluginPayload {
3648                            command_to_run: Some(command_to_run.try_into()?),
3649                            floating_pane_coordinates: None,
3650                            context,
3651                        },
3652                    )),
3653                })
3654            },
3655            PluginCommand::OpenCommandPaneFloatingNearPlugin(
3656                command_to_run,
3657                floating_pane_coordinates,
3658                context,
3659            ) => {
3660                let context: Vec<_> = context
3661                    .into_iter()
3662                    .map(|(name, value)| ContextItem { name, value })
3663                    .collect();
3664                Ok(ProtobufPluginCommand {
3665                    name: CommandName::OpenCommandPaneFloatingNearPlugin as i32,
3666                    payload: Some(Payload::OpenCommandPaneFloatingNearPluginPayload(
3667                        OpenCommandPaneFloatingNearPluginPayload {
3668                            command_to_run: Some(command_to_run.try_into()?),
3669                            floating_pane_coordinates: floating_pane_coordinates.map(|f| f.into()),
3670                            context,
3671                        },
3672                    )),
3673                })
3674            },
3675            PluginCommand::OpenTerminalNearPlugin(cwd) => Ok(ProtobufPluginCommand {
3676                name: CommandName::OpenTerminalNearPlugin as i32,
3677                payload: Some(Payload::OpenTerminalNearPluginPayload(
3678                    OpenTerminalNearPluginPayload {
3679                        file_to_open: Some(cwd.try_into()?),
3680                        context: vec![], // will be added in the future
3681                    },
3682                )),
3683            }),
3684            PluginCommand::OpenTerminalFloatingNearPlugin(cwd, floating_pane_coordinates) => {
3685                Ok(ProtobufPluginCommand {
3686                    name: CommandName::OpenTerminalFloatingNearPlugin as i32,
3687                    payload: Some(Payload::OpenTerminalFloatingNearPluginPayload(
3688                        OpenTerminalFloatingNearPluginPayload {
3689                            file_to_open: Some(cwd.try_into()?),
3690                            floating_pane_coordinates: floating_pane_coordinates.map(|f| f.into()),
3691                            context: vec![], // will be added in the future
3692                        },
3693                    )),
3694                })
3695            },
3696            PluginCommand::OpenTerminalInPlaceOfPlugin(cwd, close_plugin_after_replace) => {
3697                Ok(ProtobufPluginCommand {
3698                    name: CommandName::OpenTerminalInPlaceOfPlugin as i32,
3699                    payload: Some(Payload::OpenTerminalInPlaceOfPluginPayload(
3700                        OpenTerminalInPlaceOfPluginPayload {
3701                            file_to_open: Some(cwd.try_into()?),
3702                            close_plugin_after_replace,
3703                            context: vec![], // will be added in the future
3704                        },
3705                    )),
3706                })
3707            },
3708            PluginCommand::OpenCommandPaneInPlaceOfPlugin(
3709                command_to_run,
3710                close_plugin_after_replace,
3711                context,
3712            ) => {
3713                let context: Vec<_> = context
3714                    .into_iter()
3715                    .map(|(name, value)| ContextItem { name, value })
3716                    .collect();
3717                Ok(ProtobufPluginCommand {
3718                    name: CommandName::OpenCommandPaneInPlaceOfPlugin as i32,
3719                    payload: Some(Payload::OpenCommandPaneInPlaceOfPluginPayload(
3720                        OpenCommandPaneInPlaceOfPluginPayload {
3721                            command_to_run: Some(command_to_run.try_into()?),
3722                            close_plugin_after_replace,
3723                            context,
3724                        },
3725                    )),
3726                })
3727            },
3728            PluginCommand::OpenFileNearPlugin(file_to_open, context) => Ok(ProtobufPluginCommand {
3729                name: CommandName::OpenFileNearPlugin as i32,
3730                payload: Some(Payload::OpenFileNearPluginPayload(
3731                    OpenFileNearPluginPayload {
3732                        file_to_open: Some(file_to_open.try_into()?),
3733                        floating_pane_coordinates: None,
3734                        context: context
3735                            .into_iter()
3736                            .map(|(name, value)| ContextItem { name, value })
3737                            .collect(),
3738                    },
3739                )),
3740            }),
3741            PluginCommand::OpenFileFloatingNearPlugin(
3742                file_to_open,
3743                floating_pane_coordinates,
3744                context,
3745            ) => Ok(ProtobufPluginCommand {
3746                name: CommandName::OpenFileFloatingNearPlugin as i32,
3747                payload: Some(Payload::OpenFileFloatingNearPluginPayload(
3748                    OpenFileFloatingNearPluginPayload {
3749                        file_to_open: Some(file_to_open.try_into()?),
3750                        floating_pane_coordinates: floating_pane_coordinates.map(|f| f.into()),
3751                        context: context
3752                            .into_iter()
3753                            .map(|(name, value)| ContextItem { name, value })
3754                            .collect(),
3755                    },
3756                )),
3757            }),
3758            PluginCommand::OpenFileInPlaceOfPlugin(
3759                file_to_open,
3760                close_plugin_after_replace,
3761                context,
3762            ) => Ok(ProtobufPluginCommand {
3763                name: CommandName::OpenFileInPlaceOfPlugin as i32,
3764                payload: Some(Payload::OpenFileInPlaceOfPluginPayload(
3765                    OpenFileInPlaceOfPluginPayload {
3766                        file_to_open: Some(file_to_open.try_into()?),
3767                        floating_pane_coordinates: None,
3768                        close_plugin_after_replace,
3769                        context: context
3770                            .into_iter()
3771                            .map(|(name, value)| ContextItem { name, value })
3772                            .collect(),
3773                    },
3774                )),
3775            }),
3776            PluginCommand::GroupAndUngroupPanes(
3777                panes_to_group,
3778                panes_to_ungroup,
3779                for_all_clients,
3780            ) => Ok(ProtobufPluginCommand {
3781                name: CommandName::GroupAndUngroupPanes as i32,
3782                payload: Some(Payload::GroupAndUngroupPanesPayload(
3783                    GroupAndUngroupPanesPayload {
3784                        pane_ids_to_group: panes_to_group
3785                            .iter()
3786                            .filter_map(|&p| p.try_into().ok())
3787                            .collect(),
3788                        pane_ids_to_ungroup: panes_to_ungroup
3789                            .iter()
3790                            .filter_map(|&p| p.try_into().ok())
3791                            .collect(),
3792                        for_all_clients,
3793                    },
3794                )),
3795            }),
3796            PluginCommand::StartWebServer => Ok(ProtobufPluginCommand {
3797                name: CommandName::StartWebServer as i32,
3798                payload: None,
3799            }),
3800            PluginCommand::StopWebServer => Ok(ProtobufPluginCommand {
3801                name: CommandName::StopWebServer as i32,
3802                payload: None,
3803            }),
3804            PluginCommand::QueryWebServerStatus => Ok(ProtobufPluginCommand {
3805                name: CommandName::QueryWebServerStatus as i32,
3806                payload: None,
3807            }),
3808            PluginCommand::HighlightAndUnhighlightPanes(
3809                panes_to_highlight,
3810                panes_to_unhighlight,
3811            ) => Ok(ProtobufPluginCommand {
3812                name: CommandName::HighlightAndUnhighlightPanes as i32,
3813                payload: Some(Payload::HighlightAndUnhighlightPanesPayload(
3814                    HighlightAndUnhighlightPanesPayload {
3815                        pane_ids_to_highlight: panes_to_highlight
3816                            .iter()
3817                            .filter_map(|&p| p.try_into().ok())
3818                            .collect(),
3819                        pane_ids_to_unhighlight: panes_to_unhighlight
3820                            .iter()
3821                            .filter_map(|&p| p.try_into().ok())
3822                            .collect(),
3823                    },
3824                )),
3825            }),
3826            PluginCommand::CloseMultiplePanes(pane_ids) => Ok(ProtobufPluginCommand {
3827                name: CommandName::CloseMultiplePanes as i32,
3828                payload: Some(Payload::CloseMultiplePanesPayload(
3829                    CloseMultiplePanesPayload {
3830                        pane_ids: pane_ids.iter().filter_map(|&p| p.try_into().ok()).collect(),
3831                    },
3832                )),
3833            }),
3834            PluginCommand::FloatMultiplePanes(pane_ids) => Ok(ProtobufPluginCommand {
3835                name: CommandName::FloatMultiplePanes as i32,
3836                payload: Some(Payload::FloatMultiplePanesPayload(
3837                    FloatMultiplePanesPayload {
3838                        pane_ids: pane_ids.iter().filter_map(|&p| p.try_into().ok()).collect(),
3839                    },
3840                )),
3841            }),
3842            PluginCommand::EmbedMultiplePanes(pane_ids) => Ok(ProtobufPluginCommand {
3843                name: CommandName::EmbedMultiplePanes as i32,
3844                payload: Some(Payload::EmbedMultiplePanesPayload(
3845                    EmbedMultiplePanesPayload {
3846                        pane_ids: pane_ids.iter().filter_map(|&p| p.try_into().ok()).collect(),
3847                    },
3848                )),
3849            }),
3850            PluginCommand::ShareCurrentSession => Ok(ProtobufPluginCommand {
3851                name: CommandName::ShareCurrentSession as i32,
3852                payload: None,
3853            }),
3854            PluginCommand::StopSharingCurrentSession => Ok(ProtobufPluginCommand {
3855                name: CommandName::StopSharingCurrentSession as i32,
3856                payload: None,
3857            }),
3858            PluginCommand::SetSelfMouseSelectionSupport(support_mouse_selection) => {
3859                Ok(ProtobufPluginCommand {
3860                    name: CommandName::SetSelfMouseSelectionSupport as i32,
3861                    payload: Some(Payload::SetSelfMouseSelectionSupportPayload(
3862                        SetSelfMouseSelectionSupportPayload {
3863                            support_mouse_selection,
3864                        },
3865                    )),
3866                })
3867            },
3868            PluginCommand::GenerateWebLoginToken(token_label, read_only) => {
3869                Ok(ProtobufPluginCommand {
3870                    name: CommandName::GenerateWebLoginToken as i32,
3871                    payload: Some(Payload::GenerateWebLoginTokenPayload(
3872                        GenerateWebLoginTokenPayload {
3873                            token_label,
3874                            read_only: Some(read_only),
3875                        },
3876                    )),
3877                })
3878            },
3879            PluginCommand::RevokeWebLoginToken(token_label) => Ok(ProtobufPluginCommand {
3880                name: CommandName::RevokeWebLoginToken as i32,
3881                payload: Some(Payload::RevokeWebLoginTokenPayload(
3882                    RevokeWebLoginTokenPayload { token_label },
3883                )),
3884            }),
3885            PluginCommand::ListWebLoginTokens => Ok(ProtobufPluginCommand {
3886                name: CommandName::ListWebLoginTokens as i32,
3887                payload: None,
3888            }),
3889            PluginCommand::RevokeAllWebLoginTokens => Ok(ProtobufPluginCommand {
3890                name: CommandName::RevokeAllWebLoginTokens as i32,
3891                payload: None,
3892            }),
3893            PluginCommand::RenameWebLoginToken(old_name, new_name) => Ok(ProtobufPluginCommand {
3894                name: CommandName::RenameWebLoginToken as i32,
3895                payload: Some(Payload::RenameWebLoginTokenPayload(
3896                    RenameWebLoginTokenPayload { old_name, new_name },
3897                )),
3898            }),
3899            PluginCommand::InterceptKeyPresses => Ok(ProtobufPluginCommand {
3900                name: CommandName::InterceptKeyPresses as i32,
3901                payload: None,
3902            }),
3903            PluginCommand::ClearKeyPressesIntercepts => Ok(ProtobufPluginCommand {
3904                name: CommandName::ClearKeyPressesIntercepts as i32,
3905                payload: None,
3906            }),
3907            PluginCommand::ReplacePaneWithExistingPane(
3908                pane_id_to_replace,
3909                existing_pane_id,
3910                suppress_replaced_pane,
3911            ) => Ok(ProtobufPluginCommand {
3912                name: CommandName::ReplacePaneWithExistingPane as i32,
3913                payload: Some(Payload::ReplacePaneWithExistingPanePayload(
3914                    ReplacePaneWithExistingPanePayload {
3915                        pane_id_to_replace: ProtobufPaneId::try_from(pane_id_to_replace).ok(),
3916                        existing_pane_id: ProtobufPaneId::try_from(existing_pane_id).ok(),
3917                        suppress_replaced_pane,
3918                    },
3919                )),
3920            }),
3921            PluginCommand::RunAction(action, context) => {
3922                let protobuf_action = ProtobufAction::try_from(action)
3923                    .map_err(|_| "Failed to convert action to protobuf")?;
3924
3925                let context_items: Vec<ContextItem> = context
3926                    .into_iter()
3927                    .map(|(name, value)| ContextItem { name, value })
3928                    .collect();
3929
3930                Ok(ProtobufPluginCommand {
3931                    name: CommandName::RunAction as i32,
3932                    payload: Some(Payload::RunActionPayload(RunActionPayload {
3933                        action: Some(protobuf_action),
3934                        context: context_items,
3935                    })),
3936                })
3937            },
3938            PluginCommand::CopyToClipboard(text) => Ok(ProtobufPluginCommand {
3939                name: CommandName::CopyToClipboard as i32,
3940                payload: Some(Payload::CopyToClipboardPayload(CopyToClipboardPayload {
3941                    text,
3942                })),
3943            }),
3944            PluginCommand::GenerateRandomName => Ok(ProtobufPluginCommand {
3945                name: CommandName::GenerateRandomName as i32,
3946                payload: Some(Payload::GenerateRandomNamePayload(
3947                    GenerateRandomNamePayload {},
3948                )),
3949            }),
3950            PluginCommand::DumpLayout(layout_name) => Ok(ProtobufPluginCommand {
3951                name: CommandName::DumpLayout as i32,
3952                payload: Some(Payload::DumpLayoutPayload(DumpLayoutPayload {
3953                    layout_name,
3954                })),
3955            }),
3956            PluginCommand::ParseLayout(layout_string) => Ok(ProtobufPluginCommand {
3957                name: CommandName::ParseLayout as i32,
3958                payload: Some(Payload::ParseLayoutPayload(ParseLayoutPayload {
3959                    layout_string,
3960                })),
3961            }),
3962            PluginCommand::GetLayoutDir => Ok(ProtobufPluginCommand {
3963                name: CommandName::GetLayoutDir as i32,
3964                payload: Some(Payload::GetLayoutDirPayload(GetLayoutDirPayload {})),
3965            }),
3966            PluginCommand::GetFocusedPaneInfo => Ok(ProtobufPluginCommand {
3967                name: CommandName::GetFocusedPaneInfo as i32,
3968                payload: Some(Payload::GetFocusedPaneInfoPayload(
3969                    GetFocusedPaneInfoPayload {},
3970                )),
3971            }),
3972            PluginCommand::SaveSession => Ok(ProtobufPluginCommand {
3973                name: CommandName::SaveSession as i32,
3974                payload: Some(Payload::SaveSessionPayload(SaveSessionPayload {})),
3975            }),
3976            PluginCommand::CurrentSessionLastSavedTime => Ok(ProtobufPluginCommand {
3977                name: CommandName::CurrentSessionLastSavedTime as i32,
3978                payload: Some(Payload::CurrentSessionLastSavedTimePayload(
3979                    CurrentSessionLastSavedTimePayload {},
3980                )),
3981            }),
3982            PluginCommand::GetPaneInfo(pane_id) => {
3983                let protobuf_pane_id: ProtobufPaneId = pane_id.try_into()?;
3984                Ok(ProtobufPluginCommand {
3985                    name: CommandName::GetPaneInfo as i32,
3986                    payload: Some(Payload::GetPaneInfoPayload(GetPaneInfoPayload {
3987                        pane_id: Some(protobuf_pane_id),
3988                    })),
3989                })
3990            },
3991            PluginCommand::GetTabInfo(tab_id) => Ok(ProtobufPluginCommand {
3992                name: CommandName::GetTabInfo as i32,
3993                payload: Some(Payload::GetTabInfoPayload(GetTabInfoPayload {
3994                    tab_id: tab_id as u64,
3995                })),
3996            }),
3997            PluginCommand::GetSessionEnvironmentVariables => {
3998                let payload = ProtobufGetSessionEnvironmentVariablesPayload {};
3999                Ok(ProtobufPluginCommand {
4000                    name: CommandName::GetSessionEnvironmentVariables as i32,
4001                    payload: Some(Payload::GetSessionEnvironmentVariablesPayload(payload)),
4002                })
4003            },
4004            PluginCommand::OpenCommandPaneInNewTab(command_to_run, context) => {
4005                let context: Vec<_> = context
4006                    .into_iter()
4007                    .map(|(name, value)| ContextItem { name, value })
4008                    .collect();
4009                Ok(ProtobufPluginCommand {
4010                    name: CommandName::OpenCommandPaneInNewTab as i32,
4011                    payload: Some(Payload::OpenCommandPaneInNewTabPayload(
4012                        OpenCommandPanePayload {
4013                            command_to_run: Some(command_to_run.try_into()?),
4014                            floating_pane_coordinates: None,
4015                            context,
4016                        },
4017                    )),
4018                })
4019            },
4020            PluginCommand::OpenPluginPaneInNewTab {
4021                plugin_url,
4022                configuration,
4023                context,
4024            } => {
4025                let context: Vec<_> = context
4026                    .into_iter()
4027                    .map(|(name, value)| ContextItem { name, value })
4028                    .collect();
4029                let configuration: std::collections::HashMap<String, String> =
4030                    configuration.into_iter().collect();
4031                Ok(ProtobufPluginCommand {
4032                    name: CommandName::OpenPluginPaneInNewTab as i32,
4033                    payload: Some(Payload::OpenPluginPaneInNewTabPayload(
4034                        ProtobufOpenPluginPaneInNewTabPayload {
4035                            plugin_url,
4036                            configuration,
4037                            context,
4038                        },
4039                    )),
4040                })
4041            },
4042            PluginCommand::OpenEditorPaneInNewTab(file_to_open, context) => {
4043                let context: Vec<_> = context
4044                    .into_iter()
4045                    .map(|(name, value)| ContextItem { name, value })
4046                    .collect();
4047                Ok(ProtobufPluginCommand {
4048                    name: CommandName::OpenEditorPaneInNewTab as i32,
4049                    payload: Some(Payload::OpenEditorPaneInNewTabPayload(OpenFilePayload {
4050                        file_to_open: Some(file_to_open.try_into()?),
4051                        floating_pane_coordinates: None,
4052                        context,
4053                    })),
4054                })
4055            },
4056            PluginCommand::OpenCommandPaneInPlaceOfPaneId(
4057                pane_id,
4058                command_to_run,
4059                close_replaced_pane,
4060                context,
4061            ) => {
4062                let context: Vec<_> = context
4063                    .into_iter()
4064                    .map(|(name, value)| ContextItem { name, value })
4065                    .collect();
4066                Ok(ProtobufPluginCommand {
4067                    name: CommandName::OpenCommandPaneInPlaceOfPaneId as i32,
4068                    payload: Some(Payload::OpenCommandPaneInPlaceOfPaneIdPayload(
4069                        OpenCommandPaneInPlaceOfPaneIdPayload {
4070                            pane_id_to_replace: Some(pane_id.try_into()?),
4071                            command_to_run: Some(command_to_run.try_into()?),
4072                            close_replaced_pane,
4073                            context,
4074                        },
4075                    )),
4076                })
4077            },
4078            PluginCommand::OpenTerminalPaneInPlaceOfPaneId(pane_id, cwd, close_replaced_pane) => {
4079                Ok(ProtobufPluginCommand {
4080                    name: CommandName::OpenTerminalPaneInPlaceOfPaneId as i32,
4081                    payload: Some(Payload::OpenTerminalPaneInPlaceOfPaneIdPayload(
4082                        OpenTerminalPaneInPlaceOfPaneIdPayload {
4083                            pane_id_to_replace: Some(pane_id.try_into()?),
4084                            cwd: Some(cwd.try_into()?),
4085                            close_replaced_pane,
4086                        },
4087                    )),
4088                })
4089            },
4090            PluginCommand::OpenEditPaneInPlaceOfPaneId(
4091                pane_id,
4092                file_to_open,
4093                close_replaced_pane,
4094                context,
4095            ) => {
4096                let context: Vec<_> = context
4097                    .into_iter()
4098                    .map(|(name, value)| ContextItem { name, value })
4099                    .collect();
4100                Ok(ProtobufPluginCommand {
4101                    name: CommandName::OpenEditPaneInPlaceOfPaneId as i32,
4102                    payload: Some(Payload::OpenEditPaneInPlaceOfPaneIdPayload(
4103                        OpenEditPaneInPlaceOfPaneIdPayload {
4104                            pane_id_to_replace: Some(pane_id.try_into()?),
4105                            file_to_open: Some(file_to_open.try_into()?),
4106                            close_replaced_pane,
4107                            context,
4108                        },
4109                    )),
4110                })
4111            },
4112            PluginCommand::HideFloatingPanes { tab_id } => Ok(ProtobufPluginCommand {
4113                name: CommandName::HideFloatingPanes as i32,
4114                payload: Some(Payload::HideFloatingPanesPayload(
4115                    ProtobufHideFloatingPanesPayload {
4116                        tab_id: tab_id.map(|id| id as u32),
4117                    },
4118                )),
4119            }),
4120            PluginCommand::ShowFloatingPanes { tab_id } => Ok(ProtobufPluginCommand {
4121                name: CommandName::ShowFloatingPanes as i32,
4122                payload: Some(Payload::ShowFloatingPanesPayload(
4123                    ProtobufShowFloatingPanesPayload {
4124                        tab_id: tab_id.map(|id| id as u32),
4125                    },
4126                )),
4127            }),
4128            PluginCommand::SetPaneColor(pane_id, fg, bg) => Ok(ProtobufPluginCommand {
4129                name: CommandName::SetPaneColor as i32,
4130                payload: Some(Payload::SetPaneColorPayload(SetPaneColorPayload {
4131                    pane_id: Some(pane_id.try_into()?),
4132                    fg,
4133                    bg,
4134                })),
4135            }),
4136            PluginCommand::SetPaneRegexHighlights(pane_id, highlights) => {
4137                Ok(ProtobufPluginCommand {
4138                    name: CommandName::SetPaneRegexHighlights as i32,
4139                    payload: Some(Payload::SetPaneRegexHighlightsPayload(
4140                        SetPaneRegexHighlightsPayload {
4141                            pane_id: pane_id.try_into().ok(),
4142                            highlights: highlights
4143                                .into_iter()
4144                                .map(|h| {
4145                                    let style_variant = match h.style {
4146                                        HighlightStyle::Emphasis0 => {
4147                                            ProtobufHighlightStyleVariant::Emphasis0(true)
4148                                        },
4149                                        HighlightStyle::Emphasis1 => {
4150                                            ProtobufHighlightStyleVariant::Emphasis1(true)
4151                                        },
4152                                        HighlightStyle::Emphasis2 => {
4153                                            ProtobufHighlightStyleVariant::Emphasis2(true)
4154                                        },
4155                                        HighlightStyle::Emphasis3 => {
4156                                            ProtobufHighlightStyleVariant::Emphasis3(true)
4157                                        },
4158                                        HighlightStyle::CustomRgb { fg, bg } => {
4159                                            ProtobufHighlightStyleVariant::CustomRgb(
4160                                                ProtobufCustomRgbHighlight {
4161                                                    fg_r: fg.map(|(r, _, _)| r as u32),
4162                                                    fg_g: fg.map(|(_, g, _)| g as u32),
4163                                                    fg_b: fg.map(|(_, _, b)| b as u32),
4164                                                    bg_r: bg.map(|(r, _, _)| r as u32),
4165                                                    bg_g: bg.map(|(_, g, _)| g as u32),
4166                                                    bg_b: bg.map(|(_, _, b)| b as u32),
4167                                                },
4168                                            )
4169                                        },
4170                                        HighlightStyle::CustomIndex { fg, bg } => {
4171                                            ProtobufHighlightStyleVariant::CustomIndex(
4172                                                ProtobufCustomIndexHighlight {
4173                                                    fg: fg.map(|v| v as u32),
4174                                                    bg: bg.map(|v| v as u32),
4175                                                },
4176                                            )
4177                                        },
4178                                        HighlightStyle::BackgroundEmphasis0 => {
4179                                            ProtobufHighlightStyleVariant::BackgroundEmphasis0(true)
4180                                        },
4181                                        HighlightStyle::BackgroundEmphasis1 => {
4182                                            ProtobufHighlightStyleVariant::BackgroundEmphasis1(true)
4183                                        },
4184                                        HighlightStyle::BackgroundEmphasis2 => {
4185                                            ProtobufHighlightStyleVariant::BackgroundEmphasis2(true)
4186                                        },
4187                                        HighlightStyle::BackgroundEmphasis3 => {
4188                                            ProtobufHighlightStyleVariant::BackgroundEmphasis3(true)
4189                                        },
4190                                        HighlightStyle::None => {
4191                                            ProtobufHighlightStyleVariant::None(true)
4192                                        },
4193                                    };
4194                                    ProtobufRegexHighlight {
4195                                        pattern: h.pattern,
4196                                        style: Some(ProtobufHighlightStyle {
4197                                            style: Some(style_variant),
4198                                        }),
4199                                        context: h
4200                                            .context
4201                                            .into_iter()
4202                                            .map(|(name, value)| ContextItem { name, value })
4203                                            .collect(),
4204                                        on_hover: h.on_hover,
4205                                        bold: h.bold,
4206                                        italic: h.italic,
4207                                        underline: h.underline,
4208                                        tooltip_text: h.tooltip_text,
4209                                        layer: match h.layer {
4210                                            HighlightLayer::Hint => {
4211                                                ProtobufHighlightLayer::Hint as i32
4212                                            },
4213                                            HighlightLayer::Tool => {
4214                                                ProtobufHighlightLayer::Tool as i32
4215                                            },
4216                                            HighlightLayer::ActionFeedback => {
4217                                                ProtobufHighlightLayer::ActionFeedback as i32
4218                                            },
4219                                        },
4220                                    }
4221                                })
4222                                .collect(),
4223                        },
4224                    )),
4225                })
4226            },
4227            PluginCommand::ClearPaneHighlights(pane_id) => Ok(ProtobufPluginCommand {
4228                name: CommandName::ClearPaneHighlights as i32,
4229                payload: Some(Payload::ClearPaneHighlightsPayload(
4230                    ClearPaneHighlightsPayload {
4231                        pane_id: pane_id.try_into().ok(),
4232                    },
4233                )),
4234            }),
4235            PluginCommand::OpenPluginPaneFloating {
4236                plugin_url,
4237                configuration,
4238                floating_pane_coordinates,
4239                context,
4240            } => {
4241                let context: Vec<_> = context
4242                    .into_iter()
4243                    .map(|(name, value)| ContextItem { name, value })
4244                    .collect();
4245                let configuration: std::collections::HashMap<String, String> =
4246                    configuration.into_iter().collect();
4247                Ok(ProtobufPluginCommand {
4248                    name: CommandName::OpenPluginPaneFloating as i32,
4249                    payload: Some(Payload::OpenPluginPaneFloatingPayload(
4250                        OpenPluginPaneFloatingPayload {
4251                            plugin_url,
4252                            configuration,
4253                            floating_pane_coordinates: floating_pane_coordinates.map(|f| f.into()),
4254                            context,
4255                        },
4256                    )),
4257                })
4258            },
4259        }
4260    }
4261}
4262
4263// Conversion implementations for tab creation response types
4264use crate::data::{
4265    BreakPanesToNewTabResponse, BreakPanesToTabWithIdResponse, BreakPanesToTabWithIndexResponse,
4266    FocusOrCreateTabResponse, NewTabResponse, NewTabsResponse, OpenCommandPaneBackgroundResponse,
4267    OpenCommandPaneFloatingNearPluginResponse, OpenCommandPaneFloatingResponse,
4268    OpenCommandPaneInPlaceOfPaneIdResponse, OpenCommandPaneInPlaceOfPluginResponse,
4269    OpenCommandPaneInPlaceResponse, OpenCommandPaneNearPluginResponse, OpenCommandPaneResponse,
4270    OpenEditPaneInPlaceOfPaneIdResponse, OpenFileFloatingNearPluginResponse,
4271    OpenFileFloatingResponse, OpenFileInPlaceOfPluginResponse, OpenFileInPlaceResponse,
4272    OpenFileNearPluginResponse, OpenFileResponse, OpenPaneInNewTabResponse,
4273    OpenPluginPaneFloatingResponse, OpenTerminalFloatingNearPluginResponse,
4274    OpenTerminalFloatingResponse, OpenTerminalInPlaceOfPluginResponse, OpenTerminalInPlaceResponse,
4275    OpenTerminalNearPluginResponse, OpenTerminalPaneInPlaceOfPaneIdResponse, OpenTerminalResponse,
4276};
4277
4278impl TryFrom<ProtobufNewTabResponse> for NewTabResponse {
4279    type Error = &'static str;
4280    fn try_from(protobuf: ProtobufNewTabResponse) -> Result<Self, Self::Error> {
4281        match protobuf.result {
4282            Some(new_tab_response::Result::TabId(id)) => Ok(Some(id as usize)),
4283            Some(new_tab_response::Result::None(_)) | None => Ok(None),
4284        }
4285    }
4286}
4287
4288impl From<NewTabResponse> for ProtobufNewTabResponse {
4289    fn from(response: NewTabResponse) -> Self {
4290        match response {
4291            Some(tab_id) => ProtobufNewTabResponse {
4292                result: Some(new_tab_response::Result::TabId(tab_id as u64)),
4293            },
4294            None => ProtobufNewTabResponse {
4295                result: Some(new_tab_response::Result::None(true)),
4296            },
4297        }
4298    }
4299}
4300
4301impl TryFrom<ProtobufNewTabsResponse> for NewTabsResponse {
4302    type Error = &'static str;
4303    fn try_from(protobuf: ProtobufNewTabsResponse) -> Result<Self, Self::Error> {
4304        Ok(protobuf.tab_ids.into_iter().map(|id| id as usize).collect())
4305    }
4306}
4307
4308impl From<NewTabsResponse> for ProtobufNewTabsResponse {
4309    fn from(response: NewTabsResponse) -> Self {
4310        ProtobufNewTabsResponse {
4311            tab_ids: response.into_iter().map(|id| id as u64).collect(),
4312        }
4313    }
4314}
4315
4316impl TryFrom<ProtobufFocusOrCreateTabResponse> for FocusOrCreateTabResponse {
4317    type Error = &'static str;
4318    fn try_from(protobuf: ProtobufFocusOrCreateTabResponse) -> Result<Self, Self::Error> {
4319        match protobuf.result {
4320            Some(focus_or_create_tab_response::Result::TabId(id)) => Ok(Some(id as usize)),
4321            Some(focus_or_create_tab_response::Result::None(_)) | None => Ok(None),
4322        }
4323    }
4324}
4325
4326impl From<FocusOrCreateTabResponse> for ProtobufFocusOrCreateTabResponse {
4327    fn from(response: FocusOrCreateTabResponse) -> Self {
4328        match response {
4329            Some(tab_id) => ProtobufFocusOrCreateTabResponse {
4330                result: Some(focus_or_create_tab_response::Result::TabId(tab_id as u64)),
4331            },
4332            None => ProtobufFocusOrCreateTabResponse {
4333                result: Some(focus_or_create_tab_response::Result::None(true)),
4334            },
4335        }
4336    }
4337}
4338
4339impl TryFrom<ProtobufBreakPanesToNewTabResponse> for BreakPanesToNewTabResponse {
4340    type Error = &'static str;
4341    fn try_from(protobuf: ProtobufBreakPanesToNewTabResponse) -> Result<Self, Self::Error> {
4342        match protobuf.result {
4343            Some(break_panes_to_new_tab_response::Result::TabId(id)) => Ok(Some(id as usize)),
4344            Some(break_panes_to_new_tab_response::Result::None(_)) | None => Ok(None),
4345        }
4346    }
4347}
4348
4349impl From<BreakPanesToNewTabResponse> for ProtobufBreakPanesToNewTabResponse {
4350    fn from(response: BreakPanesToNewTabResponse) -> Self {
4351        match response {
4352            Some(tab_id) => ProtobufBreakPanesToNewTabResponse {
4353                result: Some(break_panes_to_new_tab_response::Result::TabId(
4354                    tab_id as u64,
4355                )),
4356            },
4357            None => ProtobufBreakPanesToNewTabResponse {
4358                result: Some(break_panes_to_new_tab_response::Result::None(true)),
4359            },
4360        }
4361    }
4362}
4363
4364impl TryFrom<ProtobufBreakPanesToTabWithIndexResponse> for BreakPanesToTabWithIndexResponse {
4365    type Error = &'static str;
4366    fn try_from(protobuf: ProtobufBreakPanesToTabWithIndexResponse) -> Result<Self, Self::Error> {
4367        match protobuf.result {
4368            Some(break_panes_to_tab_with_index_response::Result::TabId(id)) => {
4369                Ok(Some(id as usize))
4370            },
4371            Some(break_panes_to_tab_with_index_response::Result::None(_)) | None => Ok(None),
4372        }
4373    }
4374}
4375
4376impl From<BreakPanesToTabWithIndexResponse> for ProtobufBreakPanesToTabWithIndexResponse {
4377    fn from(response: BreakPanesToTabWithIndexResponse) -> Self {
4378        match response {
4379            Some(tab_id) => ProtobufBreakPanesToTabWithIndexResponse {
4380                result: Some(break_panes_to_tab_with_index_response::Result::TabId(
4381                    tab_id as u64,
4382                )),
4383            },
4384            None => ProtobufBreakPanesToTabWithIndexResponse {
4385                result: Some(break_panes_to_tab_with_index_response::Result::None(true)),
4386            },
4387        }
4388    }
4389}
4390
4391impl TryFrom<ProtobufBreakPanesToTabWithIdResponse> for BreakPanesToTabWithIdResponse {
4392    type Error = &'static str;
4393    fn try_from(protobuf: ProtobufBreakPanesToTabWithIdResponse) -> Result<Self, Self::Error> {
4394        match protobuf.result {
4395            Some(break_panes_to_tab_with_id_response::Result::TabId(id)) => Ok(Some(id as usize)),
4396            Some(break_panes_to_tab_with_id_response::Result::None(_)) | None => Ok(None),
4397        }
4398    }
4399}
4400
4401impl From<BreakPanesToTabWithIdResponse> for ProtobufBreakPanesToTabWithIdResponse {
4402    fn from(response: BreakPanesToTabWithIdResponse) -> Self {
4403        match response {
4404            Some(tab_id) => ProtobufBreakPanesToTabWithIdResponse {
4405                result: Some(break_panes_to_tab_with_id_response::Result::TabId(
4406                    tab_id as u64,
4407                )),
4408            },
4409            None => ProtobufBreakPanesToTabWithIdResponse {
4410                result: Some(break_panes_to_tab_with_id_response::Result::None(true)),
4411            },
4412        }
4413    }
4414}
4415
4416// Pane-creating command response conversions
4417
4418impl TryFrom<ProtobufOpenFileResponse> for OpenFileResponse {
4419    type Error = &'static str;
4420    fn try_from(protobuf: ProtobufOpenFileResponse) -> Result<Self, Self::Error> {
4421        match protobuf.pane_id {
4422            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
4423            None => Ok(None),
4424        }
4425    }
4426}
4427
4428impl From<OpenFileResponse> for ProtobufOpenFileResponse {
4429    fn from(response: OpenFileResponse) -> Self {
4430        ProtobufOpenFileResponse {
4431            pane_id: response.map(|p| p.try_into().unwrap()),
4432        }
4433    }
4434}
4435
4436impl TryFrom<ProtobufOpenFileFloatingResponse> for OpenFileFloatingResponse {
4437    type Error = &'static str;
4438    fn try_from(protobuf: ProtobufOpenFileFloatingResponse) -> Result<Self, Self::Error> {
4439        match protobuf.pane_id {
4440            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
4441            None => Ok(None),
4442        }
4443    }
4444}
4445
4446impl From<OpenFileFloatingResponse> for ProtobufOpenFileFloatingResponse {
4447    fn from(response: OpenFileFloatingResponse) -> Self {
4448        ProtobufOpenFileFloatingResponse {
4449            pane_id: response.map(|p| p.try_into().unwrap()),
4450        }
4451    }
4452}
4453
4454impl TryFrom<ProtobufOpenFileInPlaceResponse> for OpenFileInPlaceResponse {
4455    type Error = &'static str;
4456    fn try_from(protobuf: ProtobufOpenFileInPlaceResponse) -> Result<Self, Self::Error> {
4457        match protobuf.pane_id {
4458            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
4459            None => Ok(None),
4460        }
4461    }
4462}
4463
4464impl From<OpenFileInPlaceResponse> for ProtobufOpenFileInPlaceResponse {
4465    fn from(response: OpenFileInPlaceResponse) -> Self {
4466        ProtobufOpenFileInPlaceResponse {
4467            pane_id: response.map(|p| p.try_into().unwrap()),
4468        }
4469    }
4470}
4471
4472impl TryFrom<ProtobufOpenFileNearPluginResponse> for OpenFileNearPluginResponse {
4473    type Error = &'static str;
4474    fn try_from(protobuf: ProtobufOpenFileNearPluginResponse) -> Result<Self, Self::Error> {
4475        match protobuf.pane_id {
4476            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
4477            None => Ok(None),
4478        }
4479    }
4480}
4481
4482impl From<OpenFileNearPluginResponse> for ProtobufOpenFileNearPluginResponse {
4483    fn from(response: OpenFileNearPluginResponse) -> Self {
4484        ProtobufOpenFileNearPluginResponse {
4485            pane_id: response.map(|p| p.try_into().unwrap()),
4486        }
4487    }
4488}
4489
4490impl TryFrom<ProtobufOpenFileFloatingNearPluginResponse> for OpenFileFloatingNearPluginResponse {
4491    type Error = &'static str;
4492    fn try_from(protobuf: ProtobufOpenFileFloatingNearPluginResponse) -> Result<Self, Self::Error> {
4493        match protobuf.pane_id {
4494            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
4495            None => Ok(None),
4496        }
4497    }
4498}
4499
4500impl From<OpenFileFloatingNearPluginResponse> for ProtobufOpenFileFloatingNearPluginResponse {
4501    fn from(response: OpenFileFloatingNearPluginResponse) -> Self {
4502        ProtobufOpenFileFloatingNearPluginResponse {
4503            pane_id: response.map(|p| p.try_into().unwrap()),
4504        }
4505    }
4506}
4507
4508impl TryFrom<ProtobufOpenFileInPlaceOfPluginResponse> for OpenFileInPlaceOfPluginResponse {
4509    type Error = &'static str;
4510    fn try_from(protobuf: ProtobufOpenFileInPlaceOfPluginResponse) -> Result<Self, Self::Error> {
4511        match protobuf.pane_id {
4512            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
4513            None => Ok(None),
4514        }
4515    }
4516}
4517
4518impl From<OpenFileInPlaceOfPluginResponse> for ProtobufOpenFileInPlaceOfPluginResponse {
4519    fn from(response: OpenFileInPlaceOfPluginResponse) -> Self {
4520        ProtobufOpenFileInPlaceOfPluginResponse {
4521            pane_id: response.map(|p| p.try_into().unwrap()),
4522        }
4523    }
4524}
4525
4526impl TryFrom<ProtobufOpenTerminalResponse> for OpenTerminalResponse {
4527    type Error = &'static str;
4528    fn try_from(protobuf: ProtobufOpenTerminalResponse) -> Result<Self, Self::Error> {
4529        match protobuf.pane_id {
4530            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
4531            None => Ok(None),
4532        }
4533    }
4534}
4535
4536impl From<OpenTerminalResponse> for ProtobufOpenTerminalResponse {
4537    fn from(response: OpenTerminalResponse) -> Self {
4538        ProtobufOpenTerminalResponse {
4539            pane_id: response.map(|p| p.try_into().unwrap()),
4540        }
4541    }
4542}
4543
4544impl TryFrom<ProtobufOpenTerminalFloatingResponse> for OpenTerminalFloatingResponse {
4545    type Error = &'static str;
4546    fn try_from(protobuf: ProtobufOpenTerminalFloatingResponse) -> Result<Self, Self::Error> {
4547        match protobuf.pane_id {
4548            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
4549            None => Ok(None),
4550        }
4551    }
4552}
4553
4554impl From<OpenTerminalFloatingResponse> for ProtobufOpenTerminalFloatingResponse {
4555    fn from(response: OpenTerminalFloatingResponse) -> Self {
4556        ProtobufOpenTerminalFloatingResponse {
4557            pane_id: response.map(|p| p.try_into().unwrap()),
4558        }
4559    }
4560}
4561
4562impl TryFrom<ProtobufOpenTerminalInPlaceResponse> for OpenTerminalInPlaceResponse {
4563    type Error = &'static str;
4564    fn try_from(protobuf: ProtobufOpenTerminalInPlaceResponse) -> Result<Self, Self::Error> {
4565        match protobuf.pane_id {
4566            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
4567            None => Ok(None),
4568        }
4569    }
4570}
4571
4572impl From<OpenTerminalInPlaceResponse> for ProtobufOpenTerminalInPlaceResponse {
4573    fn from(response: OpenTerminalInPlaceResponse) -> Self {
4574        ProtobufOpenTerminalInPlaceResponse {
4575            pane_id: response.map(|p| p.try_into().unwrap()),
4576        }
4577    }
4578}
4579
4580impl TryFrom<ProtobufOpenTerminalNearPluginResponse> for OpenTerminalNearPluginResponse {
4581    type Error = &'static str;
4582    fn try_from(protobuf: ProtobufOpenTerminalNearPluginResponse) -> Result<Self, Self::Error> {
4583        match protobuf.pane_id {
4584            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
4585            None => Ok(None),
4586        }
4587    }
4588}
4589
4590impl From<OpenTerminalNearPluginResponse> for ProtobufOpenTerminalNearPluginResponse {
4591    fn from(response: OpenTerminalNearPluginResponse) -> Self {
4592        ProtobufOpenTerminalNearPluginResponse {
4593            pane_id: response.map(|p| p.try_into().unwrap()),
4594        }
4595    }
4596}
4597
4598impl TryFrom<ProtobufOpenTerminalFloatingNearPluginResponse>
4599    for OpenTerminalFloatingNearPluginResponse
4600{
4601    type Error = &'static str;
4602    fn try_from(
4603        protobuf: ProtobufOpenTerminalFloatingNearPluginResponse,
4604    ) -> Result<Self, Self::Error> {
4605        match protobuf.pane_id {
4606            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
4607            None => Ok(None),
4608        }
4609    }
4610}
4611
4612impl From<OpenTerminalFloatingNearPluginResponse>
4613    for ProtobufOpenTerminalFloatingNearPluginResponse
4614{
4615    fn from(response: OpenTerminalFloatingNearPluginResponse) -> Self {
4616        ProtobufOpenTerminalFloatingNearPluginResponse {
4617            pane_id: response.map(|p| p.try_into().unwrap()),
4618        }
4619    }
4620}
4621
4622impl TryFrom<ProtobufOpenTerminalInPlaceOfPluginResponse> for OpenTerminalInPlaceOfPluginResponse {
4623    type Error = &'static str;
4624    fn try_from(
4625        protobuf: ProtobufOpenTerminalInPlaceOfPluginResponse,
4626    ) -> Result<Self, Self::Error> {
4627        match protobuf.pane_id {
4628            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
4629            None => Ok(None),
4630        }
4631    }
4632}
4633
4634impl From<OpenTerminalInPlaceOfPluginResponse> for ProtobufOpenTerminalInPlaceOfPluginResponse {
4635    fn from(response: OpenTerminalInPlaceOfPluginResponse) -> Self {
4636        ProtobufOpenTerminalInPlaceOfPluginResponse {
4637            pane_id: response.map(|p| p.try_into().unwrap()),
4638        }
4639    }
4640}
4641
4642impl TryFrom<ProtobufOpenCommandPaneResponse> for OpenCommandPaneResponse {
4643    type Error = &'static str;
4644    fn try_from(protobuf: ProtobufOpenCommandPaneResponse) -> Result<Self, Self::Error> {
4645        match protobuf.pane_id {
4646            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
4647            None => Ok(None),
4648        }
4649    }
4650}
4651
4652impl From<OpenCommandPaneResponse> for ProtobufOpenCommandPaneResponse {
4653    fn from(response: OpenCommandPaneResponse) -> Self {
4654        ProtobufOpenCommandPaneResponse {
4655            pane_id: response.map(|p| p.try_into().unwrap()),
4656        }
4657    }
4658}
4659
4660impl TryFrom<ProtobufOpenCommandPaneFloatingResponse> for OpenCommandPaneFloatingResponse {
4661    type Error = &'static str;
4662    fn try_from(protobuf: ProtobufOpenCommandPaneFloatingResponse) -> Result<Self, Self::Error> {
4663        match protobuf.pane_id {
4664            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
4665            None => Ok(None),
4666        }
4667    }
4668}
4669
4670impl From<OpenCommandPaneFloatingResponse> for ProtobufOpenCommandPaneFloatingResponse {
4671    fn from(response: OpenCommandPaneFloatingResponse) -> Self {
4672        ProtobufOpenCommandPaneFloatingResponse {
4673            pane_id: response.map(|p| p.try_into().unwrap()),
4674        }
4675    }
4676}
4677
4678impl TryFrom<ProtobufOpenCommandPaneInPlaceResponse> for OpenCommandPaneInPlaceResponse {
4679    type Error = &'static str;
4680    fn try_from(protobuf: ProtobufOpenCommandPaneInPlaceResponse) -> Result<Self, Self::Error> {
4681        match protobuf.pane_id {
4682            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
4683            None => Ok(None),
4684        }
4685    }
4686}
4687
4688impl From<OpenCommandPaneInPlaceResponse> for ProtobufOpenCommandPaneInPlaceResponse {
4689    fn from(response: OpenCommandPaneInPlaceResponse) -> Self {
4690        ProtobufOpenCommandPaneInPlaceResponse {
4691            pane_id: response.map(|p| p.try_into().unwrap()),
4692        }
4693    }
4694}
4695
4696impl TryFrom<ProtobufOpenCommandPaneNearPluginResponse> for OpenCommandPaneNearPluginResponse {
4697    type Error = &'static str;
4698    fn try_from(protobuf: ProtobufOpenCommandPaneNearPluginResponse) -> Result<Self, Self::Error> {
4699        match protobuf.pane_id {
4700            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
4701            None => Ok(None),
4702        }
4703    }
4704}
4705
4706impl From<OpenCommandPaneNearPluginResponse> for ProtobufOpenCommandPaneNearPluginResponse {
4707    fn from(response: OpenCommandPaneNearPluginResponse) -> Self {
4708        ProtobufOpenCommandPaneNearPluginResponse {
4709            pane_id: response.map(|p| p.try_into().unwrap()),
4710        }
4711    }
4712}
4713
4714impl TryFrom<ProtobufOpenCommandPaneFloatingNearPluginResponse>
4715    for OpenCommandPaneFloatingNearPluginResponse
4716{
4717    type Error = &'static str;
4718    fn try_from(
4719        protobuf: ProtobufOpenCommandPaneFloatingNearPluginResponse,
4720    ) -> Result<Self, Self::Error> {
4721        match protobuf.pane_id {
4722            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
4723            None => Ok(None),
4724        }
4725    }
4726}
4727
4728impl From<OpenCommandPaneFloatingNearPluginResponse>
4729    for ProtobufOpenCommandPaneFloatingNearPluginResponse
4730{
4731    fn from(response: OpenCommandPaneFloatingNearPluginResponse) -> Self {
4732        ProtobufOpenCommandPaneFloatingNearPluginResponse {
4733            pane_id: response.map(|p| p.try_into().unwrap()),
4734        }
4735    }
4736}
4737
4738impl TryFrom<ProtobufOpenCommandPaneInPlaceOfPluginResponse>
4739    for OpenCommandPaneInPlaceOfPluginResponse
4740{
4741    type Error = &'static str;
4742    fn try_from(
4743        protobuf: ProtobufOpenCommandPaneInPlaceOfPluginResponse,
4744    ) -> Result<Self, Self::Error> {
4745        match protobuf.pane_id {
4746            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
4747            None => Ok(None),
4748        }
4749    }
4750}
4751
4752impl From<OpenCommandPaneInPlaceOfPluginResponse>
4753    for ProtobufOpenCommandPaneInPlaceOfPluginResponse
4754{
4755    fn from(response: OpenCommandPaneInPlaceOfPluginResponse) -> Self {
4756        ProtobufOpenCommandPaneInPlaceOfPluginResponse {
4757            pane_id: response.map(|p| p.try_into().unwrap()),
4758        }
4759    }
4760}
4761
4762impl TryFrom<ProtobufOpenCommandPaneBackgroundResponse> for OpenCommandPaneBackgroundResponse {
4763    type Error = &'static str;
4764    fn try_from(protobuf: ProtobufOpenCommandPaneBackgroundResponse) -> Result<Self, Self::Error> {
4765        match protobuf.pane_id {
4766            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
4767            None => Ok(None),
4768        }
4769    }
4770}
4771
4772impl From<OpenCommandPaneBackgroundResponse> for ProtobufOpenCommandPaneBackgroundResponse {
4773    fn from(response: OpenCommandPaneBackgroundResponse) -> Self {
4774        ProtobufOpenCommandPaneBackgroundResponse {
4775            pane_id: response.map(|p| p.try_into().unwrap()),
4776        }
4777    }
4778}
4779
4780impl TryFrom<ProtobufOpenPaneInNewTabResponse> for OpenPaneInNewTabResponse {
4781    type Error = &'static str;
4782    fn try_from(protobuf: ProtobufOpenPaneInNewTabResponse) -> Result<Self, Self::Error> {
4783        let tab_id = protobuf.tab_id.map(|id| id as usize);
4784        let pane_id = match protobuf.pane_id {
4785            Some(pane_id) => Some(pane_id.try_into()?),
4786            None => None,
4787        };
4788        Ok(OpenPaneInNewTabResponse { tab_id, pane_id })
4789    }
4790}
4791
4792impl From<OpenPaneInNewTabResponse> for ProtobufOpenPaneInNewTabResponse {
4793    fn from(response: OpenPaneInNewTabResponse) -> Self {
4794        ProtobufOpenPaneInNewTabResponse {
4795            tab_id: response.tab_id.map(|id| id as u64),
4796            pane_id: response.pane_id.map(|p| p.try_into().unwrap()),
4797        }
4798    }
4799}
4800
4801impl TryFrom<ProtobufOpenCommandPaneInPlaceOfPaneIdResponse>
4802    for OpenCommandPaneInPlaceOfPaneIdResponse
4803{
4804    type Error = &'static str;
4805    fn try_from(
4806        protobuf: ProtobufOpenCommandPaneInPlaceOfPaneIdResponse,
4807    ) -> Result<Self, Self::Error> {
4808        match protobuf.pane_id {
4809            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
4810            None => Ok(None),
4811        }
4812    }
4813}
4814
4815impl From<OpenCommandPaneInPlaceOfPaneIdResponse>
4816    for ProtobufOpenCommandPaneInPlaceOfPaneIdResponse
4817{
4818    fn from(response: OpenCommandPaneInPlaceOfPaneIdResponse) -> Self {
4819        ProtobufOpenCommandPaneInPlaceOfPaneIdResponse {
4820            pane_id: response.map(|p| p.try_into().unwrap()),
4821        }
4822    }
4823}
4824
4825impl TryFrom<ProtobufOpenTerminalPaneInPlaceOfPaneIdResponse>
4826    for OpenTerminalPaneInPlaceOfPaneIdResponse
4827{
4828    type Error = &'static str;
4829    fn try_from(
4830        protobuf: ProtobufOpenTerminalPaneInPlaceOfPaneIdResponse,
4831    ) -> Result<Self, Self::Error> {
4832        match protobuf.pane_id {
4833            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
4834            None => Ok(None),
4835        }
4836    }
4837}
4838
4839impl From<OpenTerminalPaneInPlaceOfPaneIdResponse>
4840    for ProtobufOpenTerminalPaneInPlaceOfPaneIdResponse
4841{
4842    fn from(response: OpenTerminalPaneInPlaceOfPaneIdResponse) -> Self {
4843        ProtobufOpenTerminalPaneInPlaceOfPaneIdResponse {
4844            pane_id: response.map(|p| p.try_into().unwrap()),
4845        }
4846    }
4847}
4848
4849impl TryFrom<ProtobufOpenEditPaneInPlaceOfPaneIdResponse> for OpenEditPaneInPlaceOfPaneIdResponse {
4850    type Error = &'static str;
4851    fn try_from(
4852        protobuf: ProtobufOpenEditPaneInPlaceOfPaneIdResponse,
4853    ) -> Result<Self, Self::Error> {
4854        match protobuf.pane_id {
4855            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
4856            None => Ok(None),
4857        }
4858    }
4859}
4860
4861impl From<OpenEditPaneInPlaceOfPaneIdResponse> for ProtobufOpenEditPaneInPlaceOfPaneIdResponse {
4862    fn from(response: OpenEditPaneInPlaceOfPaneIdResponse) -> Self {
4863        ProtobufOpenEditPaneInPlaceOfPaneIdResponse {
4864            pane_id: response.map(|p| p.try_into().unwrap()),
4865        }
4866    }
4867}
4868
4869impl TryFrom<ProtobufOpenPluginPaneFloatingResponse> for OpenPluginPaneFloatingResponse {
4870    type Error = &'static str;
4871    fn try_from(protobuf: ProtobufOpenPluginPaneFloatingResponse) -> Result<Self, Self::Error> {
4872        match protobuf.pane_id {
4873            Some(pane_id) => Ok(Some(pane_id.try_into()?)),
4874            None => Ok(None),
4875        }
4876    }
4877}
4878
4879impl From<OpenPluginPaneFloatingResponse> for ProtobufOpenPluginPaneFloatingResponse {
4880    fn from(response: OpenPluginPaneFloatingResponse) -> Self {
4881        ProtobufOpenPluginPaneFloatingResponse {
4882            pane_id: response.map(|p| p.try_into().unwrap()),
4883        }
4884    }
4885}