zellij_utils/plugin_api/
action.rs

1pub use super::generated_api::api::{
2    action::{
3        action::OptionalPayload, Action as ProtobufAction, ActionName as ProtobufActionName,
4        DumpScreenPayload, EditFilePayload, GoToTabNamePayload, IdAndName,
5        LaunchOrFocusPluginPayload, MovePanePayload, MoveTabDirection as ProtobufMoveTabDirection,
6        NameAndValue as ProtobufNameAndValue, NewFloatingPanePayload, NewPanePayload,
7        NewPluginPanePayload, NewTiledPanePayload, PaneIdAndShouldFloat,
8        PluginConfiguration as ProtobufPluginConfiguration, Position as ProtobufPosition,
9        RunCommandAction as ProtobufRunCommandAction, ScrollAtPayload,
10        SearchDirection as ProtobufSearchDirection, SearchOption as ProtobufSearchOption,
11        SwitchToModePayload, WriteCharsPayload, WritePayload,
12    },
13    input_mode::InputMode as ProtobufInputMode,
14    resize::{Resize as ProtobufResize, ResizeDirection as ProtobufResizeDirection},
15};
16use crate::data::{Direction, InputMode, ResizeStrategy};
17use crate::errors::prelude::*;
18use crate::input::actions::Action;
19use crate::input::actions::{SearchDirection, SearchOption};
20use crate::input::command::{OpenFilePayload, RunCommandAction};
21use crate::input::layout::{
22    PluginUserConfiguration, RunPlugin, RunPluginLocation, RunPluginOrAlias,
23};
24use crate::position::Position;
25
26use std::collections::BTreeMap;
27use std::convert::TryFrom;
28use std::path::PathBuf;
29
30impl TryFrom<ProtobufAction> for Action {
31    type Error = &'static str;
32    fn try_from(protobuf_action: ProtobufAction) -> Result<Self, &'static str> {
33        match ProtobufActionName::from_i32(protobuf_action.name) {
34            Some(ProtobufActionName::Quit) => match protobuf_action.optional_payload {
35                Some(_) => Err("The Quit Action should not have a payload"),
36                None => Ok(Action::Quit),
37            },
38            Some(ProtobufActionName::Write) => match protobuf_action.optional_payload {
39                Some(OptionalPayload::WritePayload(write_payload)) => {
40                    Ok(Action::Write(None, write_payload.bytes_to_write, false))
41                },
42                _ => Err("Wrong payload for Action::Write"),
43            },
44            Some(ProtobufActionName::WriteChars) => match protobuf_action.optional_payload {
45                Some(OptionalPayload::WriteCharsPayload(write_chars_payload)) => {
46                    Ok(Action::WriteChars(write_chars_payload.chars))
47                },
48                _ => Err("Wrong payload for Action::WriteChars"),
49            },
50            Some(ProtobufActionName::SwitchToMode) => match protobuf_action.optional_payload {
51                Some(OptionalPayload::SwitchToModePayload(switch_to_mode_payload)) => {
52                    let input_mode: InputMode =
53                        ProtobufInputMode::from_i32(switch_to_mode_payload.input_mode)
54                            .ok_or("Malformed input mode for SwitchToMode Action")?
55                            .try_into()?;
56                    Ok(Action::SwitchToMode(input_mode))
57                },
58                _ => Err("Wrong payload for Action::SwitchToModePayload"),
59            },
60            Some(ProtobufActionName::SwitchModeForAllClients) => {
61                match protobuf_action.optional_payload {
62                    Some(OptionalPayload::SwitchModeForAllClientsPayload(
63                        switch_to_mode_payload,
64                    )) => {
65                        let input_mode: InputMode =
66                            ProtobufInputMode::from_i32(switch_to_mode_payload.input_mode)
67                                .ok_or("Malformed input mode for SwitchToMode Action")?
68                                .try_into()?;
69                        Ok(Action::SwitchModeForAllClients(input_mode))
70                    },
71                    _ => Err("Wrong payload for Action::SwitchModeForAllClients"),
72                }
73            },
74            Some(ProtobufActionName::Resize) => match protobuf_action.optional_payload {
75                Some(OptionalPayload::ResizePayload(resize_payload)) => {
76                    let resize_strategy: ResizeStrategy = resize_payload.try_into()?;
77                    Ok(Action::Resize(
78                        resize_strategy.resize,
79                        resize_strategy.direction,
80                    ))
81                },
82                _ => Err("Wrong payload for Action::Resize"),
83            },
84            Some(ProtobufActionName::FocusNextPane) => match protobuf_action.optional_payload {
85                Some(_) => Err("FocusNextPane should not have a payload"),
86                None => Ok(Action::FocusNextPane),
87            },
88            Some(ProtobufActionName::FocusPreviousPane) => match protobuf_action.optional_payload {
89                Some(_) => Err("FocusPreviousPane should not have a payload"),
90                None => Ok(Action::FocusPreviousPane),
91            },
92            Some(ProtobufActionName::SwitchFocus) => match protobuf_action.optional_payload {
93                Some(_) => Err("SwitchFocus should not have a payload"),
94                None => Ok(Action::SwitchFocus),
95            },
96            Some(ProtobufActionName::MoveFocus) => match protobuf_action.optional_payload {
97                Some(OptionalPayload::MoveFocusPayload(move_focus_payload)) => {
98                    let direction: Direction =
99                        ProtobufResizeDirection::from_i32(move_focus_payload)
100                            .ok_or("Malformed resize direction for Action::MoveFocus")?
101                            .try_into()?;
102                    Ok(Action::MoveFocus(direction))
103                },
104                _ => Err("Wrong payload for Action::MoveFocus"),
105            },
106            Some(ProtobufActionName::MoveFocusOrTab) => match protobuf_action.optional_payload {
107                Some(OptionalPayload::MoveFocusOrTabPayload(move_focus_or_tab_payload)) => {
108                    let direction: Direction =
109                        ProtobufResizeDirection::from_i32(move_focus_or_tab_payload)
110                            .ok_or("Malformed resize direction for Action::MoveFocusOrTab")?
111                            .try_into()?;
112                    Ok(Action::MoveFocusOrTab(direction))
113                },
114                _ => Err("Wrong payload for Action::MoveFocusOrTab"),
115            },
116            Some(ProtobufActionName::MovePane) => match protobuf_action.optional_payload {
117                Some(OptionalPayload::MovePanePayload(payload)) => {
118                    let direction: Option<Direction> = payload
119                        .direction
120                        .and_then(|d| ProtobufResizeDirection::from_i32(d))
121                        .and_then(|d| d.try_into().ok());
122                    Ok(Action::MovePane(direction))
123                },
124                _ => Err("Wrong payload for Action::MovePane"),
125            },
126            Some(ProtobufActionName::MovePaneBackwards) => match protobuf_action.optional_payload {
127                Some(_) => Err("MovePaneBackwards should not have a payload"),
128                None => Ok(Action::MovePaneBackwards),
129            },
130            Some(ProtobufActionName::ClearScreen) => match protobuf_action.optional_payload {
131                Some(_) => Err("ClearScreen should not have a payload"),
132                None => Ok(Action::ClearScreen),
133            },
134            Some(ProtobufActionName::DumpScreen) => match protobuf_action.optional_payload {
135                Some(OptionalPayload::DumpScreenPayload(payload)) => {
136                    let file_path = payload.file_path;
137                    let include_scrollback = payload.include_scrollback;
138                    Ok(Action::DumpScreen(file_path, include_scrollback))
139                },
140                _ => Err("Wrong payload for Action::DumpScreen"),
141            },
142            Some(ProtobufActionName::EditScrollback) => match protobuf_action.optional_payload {
143                Some(_) => Err("EditScrollback should not have a payload"),
144                None => Ok(Action::EditScrollback),
145            },
146            Some(ProtobufActionName::ScrollUp) => match protobuf_action.optional_payload {
147                Some(_) => Err("ScrollUp should not have a payload"),
148                None => Ok(Action::ScrollUp),
149            },
150            Some(ProtobufActionName::ScrollDown) => match protobuf_action.optional_payload {
151                Some(_) => Err("ScrollDown should not have a payload"),
152                None => Ok(Action::ScrollDown),
153            },
154            Some(ProtobufActionName::ScrollUpAt) => match protobuf_action.optional_payload {
155                Some(OptionalPayload::ScrollUpAtPayload(payload)) => {
156                    let position = payload
157                        .position
158                        .ok_or("ScrollUpAtPayload must have a position")?
159                        .try_into()?;
160                    Ok(Action::ScrollUpAt(position))
161                },
162                _ => Err("Wrong payload for Action::ScrollUpAt"),
163            },
164            Some(ProtobufActionName::ScrollDownAt) => match protobuf_action.optional_payload {
165                Some(OptionalPayload::ScrollDownAtPayload(payload)) => {
166                    let position = payload
167                        .position
168                        .ok_or("ScrollDownAtPayload must have a position")?
169                        .try_into()?;
170                    Ok(Action::ScrollDownAt(position))
171                },
172                _ => Err("Wrong payload for Action::ScrollDownAt"),
173            },
174            Some(ProtobufActionName::ScrollToBottom) => match protobuf_action.optional_payload {
175                Some(_) => Err("ScrollToBottom should not have a payload"),
176                None => Ok(Action::ScrollToBottom),
177            },
178            Some(ProtobufActionName::ScrollToTop) => match protobuf_action.optional_payload {
179                Some(_) => Err("ScrollToTop should not have a payload"),
180                None => Ok(Action::ScrollToTop),
181            },
182            Some(ProtobufActionName::PageScrollUp) => match protobuf_action.optional_payload {
183                Some(_) => Err("PageScrollUp should not have a payload"),
184                None => Ok(Action::PageScrollUp),
185            },
186            Some(ProtobufActionName::PageScrollDown) => match protobuf_action.optional_payload {
187                Some(_) => Err("PageScrollDown should not have a payload"),
188                None => Ok(Action::PageScrollDown),
189            },
190            Some(ProtobufActionName::HalfPageScrollUp) => match protobuf_action.optional_payload {
191                Some(_) => Err("HalfPageScrollUp should not have a payload"),
192                None => Ok(Action::HalfPageScrollUp),
193            },
194            Some(ProtobufActionName::HalfPageScrollDown) => {
195                match protobuf_action.optional_payload {
196                    Some(_) => Err("HalfPageScrollDown should not have a payload"),
197                    None => Ok(Action::HalfPageScrollDown),
198                }
199            },
200            Some(ProtobufActionName::ToggleFocusFullscreen) => {
201                match protobuf_action.optional_payload {
202                    Some(_) => Err("ToggleFocusFullscreen should not have a payload"),
203                    None => Ok(Action::ToggleFocusFullscreen),
204                }
205            },
206            Some(ProtobufActionName::TogglePaneFrames) => match protobuf_action.optional_payload {
207                Some(_) => Err("TogglePaneFrames should not have a payload"),
208                None => Ok(Action::TogglePaneFrames),
209            },
210            Some(ProtobufActionName::ToggleActiveSyncTab) => {
211                match protobuf_action.optional_payload {
212                    Some(_) => Err("ToggleActiveSyncTab should not have a payload"),
213                    None => Ok(Action::ToggleActiveSyncTab),
214                }
215            },
216            Some(ProtobufActionName::NewPane) => match protobuf_action.optional_payload {
217                Some(OptionalPayload::NewPanePayload(payload)) => {
218                    let direction: Option<Direction> = payload
219                        .direction
220                        .and_then(|d| ProtobufResizeDirection::from_i32(d))
221                        .and_then(|d| d.try_into().ok());
222                    let pane_name = payload.pane_name;
223                    Ok(Action::NewPane(direction, pane_name, false))
224                },
225                _ => Err("Wrong payload for Action::NewPane"),
226            },
227            Some(ProtobufActionName::EditFile) => match protobuf_action.optional_payload {
228                Some(OptionalPayload::EditFilePayload(payload)) => {
229                    let file_to_edit = PathBuf::from(payload.file_to_edit);
230                    let line_number: Option<usize> = payload.line_number.map(|l| l as usize);
231                    let cwd: Option<PathBuf> = payload.cwd.map(|p| PathBuf::from(p));
232                    let direction: Option<Direction> = payload
233                        .direction
234                        .and_then(|d| ProtobufResizeDirection::from_i32(d))
235                        .and_then(|d| d.try_into().ok());
236                    let should_float = payload.should_float;
237                    let should_be_in_place = false;
238                    Ok(Action::EditFile(
239                        OpenFilePayload::new(file_to_edit, line_number, cwd),
240                        direction,
241                        should_float,
242                        should_be_in_place,
243                        false,
244                        None,
245                    ))
246                },
247                _ => Err("Wrong payload for Action::NewPane"),
248            },
249            Some(ProtobufActionName::NewFloatingPane) => match protobuf_action.optional_payload {
250                Some(OptionalPayload::NewFloatingPanePayload(payload)) => {
251                    if let Some(payload) = payload.command {
252                        let pane_name = payload.pane_name.clone();
253                        let run_command_action: RunCommandAction = payload.try_into()?;
254                        Ok(Action::NewFloatingPane(
255                            Some(run_command_action),
256                            pane_name,
257                            None,
258                        ))
259                    } else {
260                        Ok(Action::NewFloatingPane(None, None, None))
261                    }
262                },
263                _ => Err("Wrong payload for Action::NewFloatingPane"),
264            },
265            Some(ProtobufActionName::NewTiledPane) => match protobuf_action.optional_payload {
266                Some(OptionalPayload::NewTiledPanePayload(payload)) => {
267                    let direction: Option<Direction> = payload
268                        .direction
269                        .and_then(|d| ProtobufResizeDirection::from_i32(d))
270                        .and_then(|d| d.try_into().ok());
271                    if let Some(payload) = payload.command {
272                        let pane_name = payload.pane_name.clone();
273                        let run_command_action: RunCommandAction = payload.try_into()?;
274                        Ok(Action::NewTiledPane(
275                            direction,
276                            Some(run_command_action),
277                            pane_name,
278                        ))
279                    } else {
280                        Ok(Action::NewTiledPane(direction, None, None))
281                    }
282                },
283                _ => Err("Wrong payload for Action::NewTiledPane"),
284            },
285            Some(ProtobufActionName::TogglePaneEmbedOrFloating) => {
286                match protobuf_action.optional_payload {
287                    Some(_) => Err("TogglePaneEmbedOrFloating should not have a payload"),
288                    None => Ok(Action::TogglePaneEmbedOrFloating),
289                }
290            },
291            Some(ProtobufActionName::ToggleFloatingPanes) => {
292                match protobuf_action.optional_payload {
293                    Some(_) => Err("ToggleFloatingPanes should not have a payload"),
294                    None => Ok(Action::ToggleFloatingPanes),
295                }
296            },
297            Some(ProtobufActionName::CloseFocus) => match protobuf_action.optional_payload {
298                Some(_) => Err("CloseFocus should not have a payload"),
299                None => Ok(Action::CloseFocus),
300            },
301            Some(ProtobufActionName::PaneNameInput) => match protobuf_action.optional_payload {
302                Some(OptionalPayload::PaneNameInputPayload(bytes)) => {
303                    Ok(Action::PaneNameInput(bytes))
304                },
305                _ => Err("Wrong payload for Action::PaneNameInput"),
306            },
307            Some(ProtobufActionName::UndoRenamePane) => match protobuf_action.optional_payload {
308                Some(_) => Err("UndoRenamePane should not have a payload"),
309                None => Ok(Action::UndoRenamePane),
310            },
311            Some(ProtobufActionName::NewTab) => {
312                match protobuf_action.optional_payload {
313                    Some(_) => Err("NewTab should not have a payload"),
314                    None => {
315                        // we do not serialize the layouts of this action
316                        Ok(Action::NewTab(None, vec![], None, None, None, true))
317                    },
318                }
319            },
320            Some(ProtobufActionName::NoOp) => match protobuf_action.optional_payload {
321                Some(_) => Err("NoOp should not have a payload"),
322                None => Ok(Action::NoOp),
323            },
324            Some(ProtobufActionName::GoToNextTab) => match protobuf_action.optional_payload {
325                Some(_) => Err("GoToNextTab should not have a payload"),
326                None => Ok(Action::GoToNextTab),
327            },
328            Some(ProtobufActionName::GoToPreviousTab) => match protobuf_action.optional_payload {
329                Some(_) => Err("GoToPreviousTab should not have a payload"),
330                None => Ok(Action::GoToPreviousTab),
331            },
332            Some(ProtobufActionName::CloseTab) => match protobuf_action.optional_payload {
333                Some(_) => Err("CloseTab should not have a payload"),
334                None => Ok(Action::CloseTab),
335            },
336            Some(ProtobufActionName::GoToTab) => match protobuf_action.optional_payload {
337                Some(OptionalPayload::GoToTabPayload(index)) => Ok(Action::GoToTab(index)),
338                _ => Err("Wrong payload for Action::GoToTab"),
339            },
340            Some(ProtobufActionName::GoToTabName) => match protobuf_action.optional_payload {
341                Some(OptionalPayload::GoToTabNamePayload(payload)) => {
342                    let tab_name = payload.tab_name;
343                    let create = payload.create;
344                    Ok(Action::GoToTabName(tab_name, create))
345                },
346                _ => Err("Wrong payload for Action::GoToTabName"),
347            },
348            Some(ProtobufActionName::ToggleTab) => match protobuf_action.optional_payload {
349                Some(_) => Err("ToggleTab should not have a payload"),
350                None => Ok(Action::ToggleTab),
351            },
352            Some(ProtobufActionName::TabNameInput) => match protobuf_action.optional_payload {
353                Some(OptionalPayload::TabNameInputPayload(bytes)) => {
354                    Ok(Action::TabNameInput(bytes))
355                },
356                _ => Err("Wrong payload for Action::TabNameInput"),
357            },
358            Some(ProtobufActionName::UndoRenameTab) => match protobuf_action.optional_payload {
359                Some(_) => Err("UndoRenameTab should not have a payload"),
360                None => Ok(Action::UndoRenameTab),
361            },
362            Some(ProtobufActionName::MoveTab) => match protobuf_action.optional_payload {
363                Some(OptionalPayload::MoveTabPayload(move_tab_payload)) => {
364                    let direction: Direction = ProtobufMoveTabDirection::from_i32(move_tab_payload)
365                        .ok_or("Malformed move tab direction for Action::MoveTab")?
366                        .try_into()?;
367                    Ok(Action::MoveTab(direction))
368                },
369                _ => Err("Wrong payload for Action::MoveTab"),
370            },
371            Some(ProtobufActionName::Run) => match protobuf_action.optional_payload {
372                Some(OptionalPayload::RunPayload(run_command_action)) => {
373                    let run_command_action = run_command_action.try_into()?;
374                    Ok(Action::Run(run_command_action))
375                },
376                _ => Err("Wrong payload for Action::Run"),
377            },
378            Some(ProtobufActionName::Detach) => match protobuf_action.optional_payload {
379                Some(_) => Err("Detach should not have a payload"),
380                None => Ok(Action::Detach),
381            },
382            Some(ProtobufActionName::LeftClick) => match protobuf_action.optional_payload {
383                Some(OptionalPayload::LeftClickPayload(payload)) => {
384                    let position = payload.try_into()?;
385                    Ok(Action::LeftClick(position))
386                },
387                _ => Err("Wrong payload for Action::LeftClick"),
388            },
389            Some(ProtobufActionName::RightClick) => match protobuf_action.optional_payload {
390                Some(OptionalPayload::RightClickPayload(payload)) => {
391                    let position = payload.try_into()?;
392                    Ok(Action::RightClick(position))
393                },
394                _ => Err("Wrong payload for Action::RightClick"),
395            },
396            Some(ProtobufActionName::MiddleClick) => match protobuf_action.optional_payload {
397                Some(OptionalPayload::MiddleClickPayload(payload)) => {
398                    let position = payload.try_into()?;
399                    Ok(Action::MiddleClick(position))
400                },
401                _ => Err("Wrong payload for Action::MiddleClick"),
402            },
403            Some(ProtobufActionName::LaunchOrFocusPlugin) => {
404                match protobuf_action.optional_payload {
405                    Some(OptionalPayload::LaunchOrFocusPluginPayload(payload)) => {
406                        let configuration: PluginUserConfiguration = payload
407                            .plugin_configuration
408                            .and_then(|p| PluginUserConfiguration::try_from(p).ok())
409                            .unwrap_or_default();
410                        let run_plugin_or_alias = RunPluginOrAlias::from_url(
411                            &payload.plugin_url.as_str(),
412                            &Some(configuration.inner().clone()),
413                            None,
414                            None,
415                        )
416                        .map_err(|_| "Malformed LaunchOrFocusPlugin payload")?;
417                        let should_float = payload.should_float;
418                        let move_to_focused_tab = payload.move_to_focused_tab;
419                        let should_open_in_place = payload.should_open_in_place;
420                        let skip_plugin_cache = payload.skip_plugin_cache;
421                        Ok(Action::LaunchOrFocusPlugin(
422                            run_plugin_or_alias,
423                            should_float,
424                            move_to_focused_tab,
425                            should_open_in_place,
426                            skip_plugin_cache,
427                        ))
428                    },
429                    _ => Err("Wrong payload for Action::LaunchOrFocusPlugin"),
430                }
431            },
432            Some(ProtobufActionName::LaunchPlugin) => match protobuf_action.optional_payload {
433                Some(OptionalPayload::LaunchOrFocusPluginPayload(payload)) => {
434                    let configuration: PluginUserConfiguration = payload
435                        .plugin_configuration
436                        .and_then(|p| PluginUserConfiguration::try_from(p).ok())
437                        .unwrap_or_default();
438                    let run_plugin_or_alias = RunPluginOrAlias::from_url(
439                        &payload.plugin_url.as_str(),
440                        &Some(configuration.inner().clone()),
441                        None,
442                        None,
443                    )
444                    .map_err(|_| "Malformed LaunchOrFocusPlugin payload")?;
445                    let should_float = payload.should_float;
446                    let _move_to_focused_tab = payload.move_to_focused_tab; // not actually used in
447                                                                            // this action
448                    let should_open_in_place = payload.should_open_in_place;
449                    let skip_plugin_cache = payload.skip_plugin_cache;
450                    Ok(Action::LaunchPlugin(
451                        run_plugin_or_alias,
452                        should_float,
453                        should_open_in_place,
454                        skip_plugin_cache,
455                        None,
456                    ))
457                },
458                _ => Err("Wrong payload for Action::LaunchOrFocusPlugin"),
459            },
460            Some(ProtobufActionName::LeftMouseRelease) => match protobuf_action.optional_payload {
461                Some(OptionalPayload::LeftMouseReleasePayload(payload)) => {
462                    let position = payload.try_into()?;
463                    Ok(Action::LeftMouseRelease(position))
464                },
465                _ => Err("Wrong payload for Action::LeftMouseRelease"),
466            },
467            Some(ProtobufActionName::RightMouseRelease) => match protobuf_action.optional_payload {
468                Some(OptionalPayload::RightMouseReleasePayload(payload)) => {
469                    let position = payload.try_into()?;
470                    Ok(Action::RightMouseRelease(position))
471                },
472                _ => Err("Wrong payload for Action::RightMouseRelease"),
473            },
474            Some(ProtobufActionName::MiddleMouseRelease) => {
475                match protobuf_action.optional_payload {
476                    Some(OptionalPayload::MiddleMouseReleasePayload(payload)) => {
477                        let position = payload.try_into()?;
478                        Ok(Action::MiddleMouseRelease(position))
479                    },
480                    _ => Err("Wrong payload for Action::MiddleMouseRelease"),
481                }
482            },
483            Some(ProtobufActionName::MouseHoldLeft) => match protobuf_action.optional_payload {
484                Some(OptionalPayload::MouseHoldLeftPayload(payload)) => {
485                    let position = payload.try_into()?;
486                    Ok(Action::MouseHoldLeft(position))
487                },
488                _ => Err("Wrong payload for Action::MouseHoldLeft"),
489            },
490            Some(ProtobufActionName::MouseHoldRight) => match protobuf_action.optional_payload {
491                Some(OptionalPayload::MouseHoldRightPayload(payload)) => {
492                    let position = payload.try_into()?;
493                    Ok(Action::MouseHoldRight(position))
494                },
495                _ => Err("Wrong payload for Action::MouseHoldRight"),
496            },
497            Some(ProtobufActionName::MouseHoldMiddle) => match protobuf_action.optional_payload {
498                Some(OptionalPayload::MouseHoldMiddlePayload(payload)) => {
499                    let position = payload.try_into()?;
500                    Ok(Action::MouseHoldMiddle(position))
501                },
502                _ => Err("Wrong payload for Action::MouseHoldMiddle"),
503            },
504            Some(ProtobufActionName::SearchInput) => match protobuf_action.optional_payload {
505                Some(OptionalPayload::SearchInputPayload(payload)) => {
506                    Ok(Action::SearchInput(payload))
507                },
508                _ => Err("Wrong payload for Action::SearchInput"),
509            },
510            Some(ProtobufActionName::Search) => match protobuf_action.optional_payload {
511                Some(OptionalPayload::SearchPayload(search_direction)) => Ok(Action::Search(
512                    ProtobufSearchDirection::from_i32(search_direction)
513                        .ok_or("Malformed payload for Action::Search")?
514                        .try_into()?,
515                )),
516                _ => Err("Wrong payload for Action::Search"),
517            },
518            Some(ProtobufActionName::SearchToggleOption) => {
519                match protobuf_action.optional_payload {
520                    Some(OptionalPayload::SearchToggleOptionPayload(search_option)) => {
521                        Ok(Action::SearchToggleOption(
522                            ProtobufSearchOption::from_i32(search_option)
523                                .ok_or("Malformed payload for Action::SearchToggleOption")?
524                                .try_into()?,
525                        ))
526                    },
527                    _ => Err("Wrong payload for Action::SearchToggleOption"),
528                }
529            },
530            Some(ProtobufActionName::ToggleMouseMode) => match protobuf_action.optional_payload {
531                Some(_) => Err("ToggleMouseMode should not have a payload"),
532                None => Ok(Action::ToggleMouseMode),
533            },
534            Some(ProtobufActionName::PreviousSwapLayout) => {
535                match protobuf_action.optional_payload {
536                    Some(_) => Err("PreviousSwapLayout should not have a payload"),
537                    None => Ok(Action::PreviousSwapLayout),
538                }
539            },
540            Some(ProtobufActionName::NextSwapLayout) => match protobuf_action.optional_payload {
541                Some(_) => Err("NextSwapLayout should not have a payload"),
542                None => Ok(Action::NextSwapLayout),
543            },
544            Some(ProtobufActionName::QueryTabNames) => match protobuf_action.optional_payload {
545                Some(_) => Err("QueryTabNames should not have a payload"),
546                None => Ok(Action::QueryTabNames),
547            },
548            Some(ProtobufActionName::NewTiledPluginPane) => {
549                match protobuf_action.optional_payload {
550                    Some(OptionalPayload::NewTiledPluginPanePayload(payload)) => {
551                        let run_plugin_location =
552                            RunPluginLocation::parse(&payload.plugin_url, None)
553                                .map_err(|_| "Malformed NewTiledPluginPane payload")?;
554                        let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin {
555                            location: run_plugin_location,
556                            _allow_exec_host_cmd: false,
557                            configuration: PluginUserConfiguration::default(),
558                            ..Default::default()
559                        });
560                        let pane_name = payload.pane_name;
561                        let skip_plugin_cache = payload.skip_plugin_cache;
562                        Ok(Action::NewTiledPluginPane(
563                            run_plugin,
564                            pane_name,
565                            skip_plugin_cache,
566                            None,
567                        ))
568                    },
569                    _ => Err("Wrong payload for Action::NewTiledPluginPane"),
570                }
571            },
572            Some(ProtobufActionName::NewFloatingPluginPane) => {
573                match protobuf_action.optional_payload {
574                    Some(OptionalPayload::NewFloatingPluginPanePayload(payload)) => {
575                        let run_plugin_location =
576                            RunPluginLocation::parse(&payload.plugin_url, None)
577                                .map_err(|_| "Malformed NewTiledPluginPane payload")?;
578                        let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin {
579                            location: run_plugin_location,
580                            _allow_exec_host_cmd: false,
581                            configuration: PluginUserConfiguration::default(),
582                            ..Default::default()
583                        });
584                        let pane_name = payload.pane_name;
585                        let skip_plugin_cache = payload.skip_plugin_cache;
586                        Ok(Action::NewFloatingPluginPane(
587                            run_plugin,
588                            pane_name,
589                            skip_plugin_cache,
590                            None,
591                            None,
592                        ))
593                    },
594                    _ => Err("Wrong payload for Action::MiddleClick"),
595                }
596            },
597            Some(ProtobufActionName::StartOrReloadPlugin) => {
598                match protobuf_action.optional_payload {
599                    Some(OptionalPayload::StartOrReloadPluginPayload(payload)) => {
600                        let run_plugin_or_alias =
601                            RunPluginOrAlias::from_url(&payload.as_str(), &None, None, None)
602                                .map_err(|_| "Malformed LaunchOrFocusPlugin payload")?;
603
604                        Ok(Action::StartOrReloadPlugin(run_plugin_or_alias))
605                    },
606                    _ => Err("Wrong payload for Action::StartOrReloadPlugin"),
607                }
608            },
609            Some(ProtobufActionName::CloseTerminalPane) => match protobuf_action.optional_payload {
610                Some(OptionalPayload::CloseTerminalPanePayload(payload)) => {
611                    Ok(Action::CloseTerminalPane(payload))
612                },
613                _ => Err("Wrong payload for Action::CloseTerminalPane"),
614            },
615            Some(ProtobufActionName::ClosePluginPane) => match protobuf_action.optional_payload {
616                Some(OptionalPayload::ClosePluginPanePayload(payload)) => {
617                    Ok(Action::ClosePluginPane(payload))
618                },
619                _ => Err("Wrong payload for Action::ClosePluginPane"),
620            },
621            Some(ProtobufActionName::FocusTerminalPaneWithId) => {
622                match protobuf_action.optional_payload {
623                    Some(OptionalPayload::FocusTerminalPaneWithIdPayload(payload)) => {
624                        let terminal_pane_id = payload.pane_id;
625                        let should_float_if_hidden = payload.should_float;
626                        Ok(Action::FocusTerminalPaneWithId(
627                            terminal_pane_id,
628                            should_float_if_hidden,
629                        ))
630                    },
631                    _ => Err("Wrong payload for Action::FocusTerminalPaneWithId"),
632                }
633            },
634            Some(ProtobufActionName::FocusPluginPaneWithId) => {
635                match protobuf_action.optional_payload {
636                    Some(OptionalPayload::FocusPluginPaneWithIdPayload(payload)) => {
637                        let plugin_pane_id = payload.pane_id;
638                        let should_float_if_hidden = payload.should_float;
639                        Ok(Action::FocusPluginPaneWithId(
640                            plugin_pane_id,
641                            should_float_if_hidden,
642                        ))
643                    },
644                    _ => Err("Wrong payload for Action::FocusPluginPaneWithId"),
645                }
646            },
647            Some(ProtobufActionName::RenameTerminalPane) => {
648                match protobuf_action.optional_payload {
649                    Some(OptionalPayload::RenameTerminalPanePayload(payload)) => {
650                        let terminal_pane_id = payload.id;
651                        let new_pane_name = payload.name;
652                        Ok(Action::RenameTerminalPane(terminal_pane_id, new_pane_name))
653                    },
654                    _ => Err("Wrong payload for Action::RenameTerminalPane"),
655                }
656            },
657            Some(ProtobufActionName::RenamePluginPane) => match protobuf_action.optional_payload {
658                Some(OptionalPayload::RenamePluginPanePayload(payload)) => {
659                    let plugin_pane_id = payload.id;
660                    let new_pane_name = payload.name;
661                    Ok(Action::RenamePluginPane(plugin_pane_id, new_pane_name))
662                },
663                _ => Err("Wrong payload for Action::RenamePluginPane"),
664            },
665            Some(ProtobufActionName::RenameTab) => match protobuf_action.optional_payload {
666                Some(OptionalPayload::RenameTabPayload(payload)) => {
667                    let tab_index = payload.id;
668                    let new_tab_name = payload.name;
669                    Ok(Action::RenameTab(tab_index, new_tab_name))
670                },
671                _ => Err("Wrong payload for Action::RenameTab"),
672            },
673            Some(ProtobufActionName::BreakPane) => match protobuf_action.optional_payload {
674                Some(_) => Err("BreakPane should not have a payload"),
675                None => Ok(Action::BreakPane),
676            },
677            Some(ProtobufActionName::BreakPaneRight) => match protobuf_action.optional_payload {
678                Some(_) => Err("BreakPaneRight should not have a payload"),
679                None => Ok(Action::BreakPaneRight),
680            },
681            Some(ProtobufActionName::BreakPaneLeft) => match protobuf_action.optional_payload {
682                Some(_) => Err("BreakPaneLeft should not have a payload"),
683                None => Ok(Action::BreakPaneLeft),
684            },
685            Some(ProtobufActionName::RenameSession) => match protobuf_action.optional_payload {
686                Some(OptionalPayload::RenameSessionPayload(name)) => {
687                    Ok(Action::RenameSession(name))
688                },
689                _ => Err("Wrong payload for Action::RenameSession"),
690            },
691            Some(ProtobufActionName::KeybindPipe) => match protobuf_action.optional_payload {
692                Some(_) => Err("KeybindPipe should not have a payload"),
693                // TODO: at some point we might want to support a payload here
694                None => Ok(Action::KeybindPipe {
695                    name: None,
696                    payload: None,
697                    args: None,
698                    plugin: None,
699                    configuration: None,
700                    launch_new: false,
701                    skip_cache: false,
702                    floating: None,
703                    in_place: None,
704                    cwd: None,
705                    pane_title: None,
706                    plugin_id: None,
707                }),
708            },
709            _ => Err("Unknown Action"),
710        }
711    }
712}
713
714impl TryFrom<Action> for ProtobufAction {
715    type Error = &'static str;
716    fn try_from(action: Action) -> Result<Self, &'static str> {
717        match action {
718            Action::Quit => Ok(ProtobufAction {
719                name: ProtobufActionName::Quit as i32,
720                optional_payload: None,
721            }),
722            Action::Write(_, bytes, _) => Ok(ProtobufAction {
723                name: ProtobufActionName::Write as i32,
724                optional_payload: Some(OptionalPayload::WritePayload(WritePayload {
725                    bytes_to_write: bytes,
726                })),
727            }),
728            Action::WriteChars(chars_to_write) => Ok(ProtobufAction {
729                name: ProtobufActionName::WriteChars as i32,
730                optional_payload: Some(OptionalPayload::WriteCharsPayload(WriteCharsPayload {
731                    chars: chars_to_write,
732                })),
733            }),
734            Action::SwitchToMode(input_mode) => {
735                let input_mode: ProtobufInputMode = input_mode.try_into()?;
736                Ok(ProtobufAction {
737                    name: ProtobufActionName::SwitchToMode as i32,
738                    optional_payload: Some(OptionalPayload::SwitchToModePayload(
739                        SwitchToModePayload {
740                            input_mode: input_mode as i32,
741                        },
742                    )),
743                })
744            },
745            Action::SwitchModeForAllClients(input_mode) => {
746                let input_mode: ProtobufInputMode = input_mode.try_into()?;
747                Ok(ProtobufAction {
748                    name: ProtobufActionName::SwitchModeForAllClients as i32,
749                    optional_payload: Some(OptionalPayload::SwitchModeForAllClientsPayload(
750                        SwitchToModePayload {
751                            input_mode: input_mode as i32,
752                        },
753                    )),
754                })
755            },
756            Action::Resize(resize, direction) => {
757                let mut resize: ProtobufResize = resize.try_into()?;
758                resize.direction = direction.and_then(|d| {
759                    let resize_direction: ProtobufResizeDirection = d.try_into().ok()?;
760                    Some(resize_direction as i32)
761                });
762                Ok(ProtobufAction {
763                    name: ProtobufActionName::Resize as i32,
764                    optional_payload: Some(OptionalPayload::ResizePayload(resize)),
765                })
766            },
767            Action::FocusNextPane => Ok(ProtobufAction {
768                name: ProtobufActionName::FocusNextPane as i32,
769                optional_payload: None,
770            }),
771            Action::FocusPreviousPane => Ok(ProtobufAction {
772                name: ProtobufActionName::FocusPreviousPane as i32,
773                optional_payload: None,
774            }),
775            Action::SwitchFocus => Ok(ProtobufAction {
776                name: ProtobufActionName::SwitchFocus as i32,
777                optional_payload: None,
778            }),
779            Action::MoveFocus(direction) => {
780                let direction: ProtobufResizeDirection = direction.try_into()?;
781                Ok(ProtobufAction {
782                    name: ProtobufActionName::MoveFocus as i32,
783                    optional_payload: Some(OptionalPayload::MoveFocusPayload(direction as i32)),
784                })
785            },
786            Action::MoveFocusOrTab(direction) => {
787                let direction: ProtobufResizeDirection = direction.try_into()?;
788                Ok(ProtobufAction {
789                    name: ProtobufActionName::MoveFocusOrTab as i32,
790                    optional_payload: Some(OptionalPayload::MoveFocusOrTabPayload(
791                        direction as i32,
792                    )),
793                })
794            },
795            Action::MovePane(direction) => {
796                let direction = direction.and_then(|direction| {
797                    let protobuf_direction: ProtobufResizeDirection = direction.try_into().ok()?;
798                    Some(protobuf_direction as i32)
799                });
800                Ok(ProtobufAction {
801                    name: ProtobufActionName::MovePane as i32,
802                    optional_payload: Some(OptionalPayload::MovePanePayload(MovePanePayload {
803                        direction,
804                    })),
805                })
806            },
807            Action::MovePaneBackwards => Ok(ProtobufAction {
808                name: ProtobufActionName::MovePaneBackwards as i32,
809                optional_payload: None,
810            }),
811            Action::ClearScreen => Ok(ProtobufAction {
812                name: ProtobufActionName::ClearScreen as i32,
813                optional_payload: None,
814            }),
815            Action::DumpScreen(file_path, include_scrollback) => Ok(ProtobufAction {
816                name: ProtobufActionName::DumpScreen as i32,
817                optional_payload: Some(OptionalPayload::DumpScreenPayload(DumpScreenPayload {
818                    file_path,
819                    include_scrollback,
820                })),
821            }),
822            Action::EditScrollback => Ok(ProtobufAction {
823                name: ProtobufActionName::EditScrollback as i32,
824                optional_payload: None,
825            }),
826            Action::ScrollUp => Ok(ProtobufAction {
827                name: ProtobufActionName::ScrollUp as i32,
828                optional_payload: None,
829            }),
830            Action::ScrollUpAt(position) => {
831                let position: ProtobufPosition = position.try_into()?;
832                Ok(ProtobufAction {
833                    name: ProtobufActionName::ScrollUpAt as i32,
834                    optional_payload: Some(OptionalPayload::ScrollUpAtPayload(ScrollAtPayload {
835                        position: Some(position),
836                    })),
837                })
838            },
839            Action::ScrollDown => Ok(ProtobufAction {
840                name: ProtobufActionName::ScrollDown as i32,
841                optional_payload: None,
842            }),
843            Action::ScrollDownAt(position) => {
844                let position: ProtobufPosition = position.try_into()?;
845                Ok(ProtobufAction {
846                    name: ProtobufActionName::ScrollDownAt as i32,
847                    optional_payload: Some(OptionalPayload::ScrollDownAtPayload(ScrollAtPayload {
848                        position: Some(position),
849                    })),
850                })
851            },
852            Action::ScrollToBottom => Ok(ProtobufAction {
853                name: ProtobufActionName::ScrollToBottom as i32,
854                optional_payload: None,
855            }),
856            Action::ScrollToTop => Ok(ProtobufAction {
857                name: ProtobufActionName::ScrollToTop as i32,
858                optional_payload: None,
859            }),
860            Action::PageScrollUp => Ok(ProtobufAction {
861                name: ProtobufActionName::PageScrollUp as i32,
862                optional_payload: None,
863            }),
864            Action::PageScrollDown => Ok(ProtobufAction {
865                name: ProtobufActionName::PageScrollDown as i32,
866                optional_payload: None,
867            }),
868            Action::HalfPageScrollUp => Ok(ProtobufAction {
869                name: ProtobufActionName::HalfPageScrollUp as i32,
870                optional_payload: None,
871            }),
872            Action::HalfPageScrollDown => Ok(ProtobufAction {
873                name: ProtobufActionName::HalfPageScrollDown as i32,
874                optional_payload: None,
875            }),
876            Action::ToggleFocusFullscreen => Ok(ProtobufAction {
877                name: ProtobufActionName::ToggleFocusFullscreen as i32,
878                optional_payload: None,
879            }),
880            Action::TogglePaneFrames => Ok(ProtobufAction {
881                name: ProtobufActionName::TogglePaneFrames as i32,
882                optional_payload: None,
883            }),
884            Action::ToggleActiveSyncTab => Ok(ProtobufAction {
885                name: ProtobufActionName::ToggleActiveSyncTab as i32,
886                optional_payload: None,
887            }),
888            Action::NewPane(direction, new_pane_name, _start_suppressed) => {
889                let direction = direction.and_then(|direction| {
890                    let protobuf_direction: ProtobufResizeDirection = direction.try_into().ok()?;
891                    Some(protobuf_direction as i32)
892                });
893                Ok(ProtobufAction {
894                    name: ProtobufActionName::NewPane as i32,
895                    optional_payload: Some(OptionalPayload::NewPanePayload(NewPanePayload {
896                        direction,
897                        pane_name: new_pane_name,
898                    })),
899                })
900            },
901            Action::EditFile(
902                open_file_payload,
903                direction,
904                should_float,
905                _should_be_in_place,
906                _floating_pane_coordinates,
907                _start_suppressed,
908            ) => {
909                let file_to_edit = open_file_payload.path.display().to_string();
910                let cwd = open_file_payload.cwd.map(|cwd| cwd.display().to_string());
911                let direction: Option<i32> = direction
912                    .and_then(|d| ProtobufResizeDirection::try_from(d).ok())
913                    .map(|d| d as i32);
914                let line_number = open_file_payload.line_number.map(|l| l as u32);
915                Ok(ProtobufAction {
916                    name: ProtobufActionName::EditFile as i32,
917                    optional_payload: Some(OptionalPayload::EditFilePayload(EditFilePayload {
918                        file_to_edit,
919                        line_number,
920                        should_float,
921                        direction,
922                        cwd,
923                    })),
924                })
925            },
926            Action::NewFloatingPane(run_command_action, pane_name, _coordinates) => {
927                let command = run_command_action.and_then(|r| {
928                    let mut protobuf_run_command_action: ProtobufRunCommandAction =
929                        r.try_into().ok()?;
930                    protobuf_run_command_action.pane_name = pane_name;
931                    Some(protobuf_run_command_action)
932                });
933                Ok(ProtobufAction {
934                    name: ProtobufActionName::NewFloatingPane as i32,
935                    optional_payload: Some(OptionalPayload::NewFloatingPanePayload(
936                        NewFloatingPanePayload { command },
937                    )),
938                })
939            },
940            Action::NewTiledPane(direction, run_command_action, pane_name) => {
941                let direction = direction.and_then(|direction| {
942                    let protobuf_direction: ProtobufResizeDirection = direction.try_into().ok()?;
943                    Some(protobuf_direction as i32)
944                });
945                let command = run_command_action.and_then(|r| {
946                    let mut protobuf_run_command_action: ProtobufRunCommandAction =
947                        r.try_into().ok()?;
948                    let pane_name = pane_name.and_then(|n| n.try_into().ok());
949                    protobuf_run_command_action.pane_name = pane_name;
950                    Some(protobuf_run_command_action)
951                });
952                Ok(ProtobufAction {
953                    name: ProtobufActionName::NewTiledPane as i32,
954                    optional_payload: Some(OptionalPayload::NewTiledPanePayload(
955                        NewTiledPanePayload { direction, command },
956                    )),
957                })
958            },
959            Action::TogglePaneEmbedOrFloating => Ok(ProtobufAction {
960                name: ProtobufActionName::TogglePaneEmbedOrFloating as i32,
961                optional_payload: None,
962            }),
963            Action::ToggleFloatingPanes => Ok(ProtobufAction {
964                name: ProtobufActionName::ToggleFloatingPanes as i32,
965                optional_payload: None,
966            }),
967            Action::CloseFocus => Ok(ProtobufAction {
968                name: ProtobufActionName::CloseFocus as i32,
969                optional_payload: None,
970            }),
971            Action::PaneNameInput(bytes) => Ok(ProtobufAction {
972                name: ProtobufActionName::PaneNameInput as i32,
973                optional_payload: Some(OptionalPayload::PaneNameInputPayload(bytes)),
974            }),
975            Action::UndoRenamePane => Ok(ProtobufAction {
976                name: ProtobufActionName::UndoRenamePane as i32,
977                optional_payload: None,
978            }),
979            Action::NewTab(..) => {
980                // we do not serialize the various newtab payloads
981                Ok(ProtobufAction {
982                    name: ProtobufActionName::NewTab as i32,
983                    optional_payload: None,
984                })
985            },
986            Action::GoToNextTab => Ok(ProtobufAction {
987                name: ProtobufActionName::GoToNextTab as i32,
988                optional_payload: None,
989            }),
990            Action::GoToPreviousTab => Ok(ProtobufAction {
991                name: ProtobufActionName::GoToPreviousTab as i32,
992                optional_payload: None,
993            }),
994            Action::CloseTab => Ok(ProtobufAction {
995                name: ProtobufActionName::CloseTab as i32,
996                optional_payload: None,
997            }),
998            Action::GoToTab(tab_index) => Ok(ProtobufAction {
999                name: ProtobufActionName::GoToTab as i32,
1000                optional_payload: Some(OptionalPayload::GoToTabPayload(tab_index)),
1001            }),
1002            Action::GoToTabName(tab_name, create) => Ok(ProtobufAction {
1003                name: ProtobufActionName::GoToTabName as i32,
1004                optional_payload: Some(OptionalPayload::GoToTabNamePayload(GoToTabNamePayload {
1005                    tab_name,
1006                    create,
1007                })),
1008            }),
1009            Action::ToggleTab => Ok(ProtobufAction {
1010                name: ProtobufActionName::ToggleTab as i32,
1011                optional_payload: None,
1012            }),
1013            Action::TabNameInput(bytes) => Ok(ProtobufAction {
1014                name: ProtobufActionName::TabNameInput as i32,
1015                optional_payload: Some(OptionalPayload::TabNameInputPayload(bytes)),
1016            }),
1017            Action::UndoRenameTab => Ok(ProtobufAction {
1018                name: ProtobufActionName::UndoRenameTab as i32,
1019                optional_payload: None,
1020            }),
1021            Action::MoveTab(direction) => {
1022                let direction: ProtobufMoveTabDirection = direction.try_into()?;
1023                Ok(ProtobufAction {
1024                    name: ProtobufActionName::MoveTab as i32,
1025                    optional_payload: Some(OptionalPayload::MoveTabPayload(direction as i32)),
1026                })
1027            },
1028            Action::Run(run_command_action) => {
1029                let run_command_action: ProtobufRunCommandAction = run_command_action.try_into()?;
1030                Ok(ProtobufAction {
1031                    name: ProtobufActionName::Run as i32,
1032                    optional_payload: Some(OptionalPayload::RunPayload(run_command_action)),
1033                })
1034            },
1035            Action::Detach => Ok(ProtobufAction {
1036                name: ProtobufActionName::Detach as i32,
1037                optional_payload: None,
1038            }),
1039            Action::LeftClick(position) => {
1040                let position: ProtobufPosition = position.try_into()?;
1041                Ok(ProtobufAction {
1042                    name: ProtobufActionName::LeftClick as i32,
1043                    optional_payload: Some(OptionalPayload::LeftClickPayload(position)),
1044                })
1045            },
1046            Action::RightClick(position) => {
1047                let position: ProtobufPosition = position.try_into()?;
1048                Ok(ProtobufAction {
1049                    name: ProtobufActionName::RightClick as i32,
1050                    optional_payload: Some(OptionalPayload::RightClickPayload(position)),
1051                })
1052            },
1053            Action::MiddleClick(position) => {
1054                let position: ProtobufPosition = position.try_into()?;
1055                Ok(ProtobufAction {
1056                    name: ProtobufActionName::MiddleClick as i32,
1057                    optional_payload: Some(OptionalPayload::MiddleClickPayload(position)),
1058                })
1059            },
1060            Action::LaunchOrFocusPlugin(
1061                run_plugin_or_alias,
1062                should_float,
1063                move_to_focused_tab,
1064                should_open_in_place,
1065                skip_plugin_cache,
1066            ) => {
1067                let configuration = run_plugin_or_alias.get_configuration().unwrap_or_default();
1068                Ok(ProtobufAction {
1069                    name: ProtobufActionName::LaunchOrFocusPlugin as i32,
1070                    optional_payload: Some(OptionalPayload::LaunchOrFocusPluginPayload(
1071                        LaunchOrFocusPluginPayload {
1072                            plugin_url: run_plugin_or_alias.location_string(),
1073                            should_float,
1074                            move_to_focused_tab,
1075                            should_open_in_place,
1076                            plugin_configuration: Some(configuration.try_into()?),
1077                            skip_plugin_cache,
1078                        },
1079                    )),
1080                })
1081            },
1082            Action::LaunchPlugin(
1083                run_plugin_or_alias,
1084                should_float,
1085                should_open_in_place,
1086                skip_plugin_cache,
1087                _cwd,
1088            ) => {
1089                let configuration = run_plugin_or_alias.get_configuration().unwrap_or_default();
1090                Ok(ProtobufAction {
1091                    name: ProtobufActionName::LaunchPlugin as i32,
1092                    optional_payload: Some(OptionalPayload::LaunchOrFocusPluginPayload(
1093                        LaunchOrFocusPluginPayload {
1094                            plugin_url: run_plugin_or_alias.location_string(),
1095                            should_float,
1096                            move_to_focused_tab: false,
1097                            should_open_in_place,
1098                            plugin_configuration: Some(configuration.try_into()?),
1099                            skip_plugin_cache,
1100                        },
1101                    )),
1102                })
1103            },
1104            Action::LeftMouseRelease(position) => {
1105                let position: ProtobufPosition = position.try_into()?;
1106                Ok(ProtobufAction {
1107                    name: ProtobufActionName::LeftMouseRelease as i32,
1108                    optional_payload: Some(OptionalPayload::LeftMouseReleasePayload(position)),
1109                })
1110            },
1111            Action::RightMouseRelease(position) => {
1112                let position: ProtobufPosition = position.try_into()?;
1113                Ok(ProtobufAction {
1114                    name: ProtobufActionName::RightMouseRelease as i32,
1115                    optional_payload: Some(OptionalPayload::RightMouseReleasePayload(position)),
1116                })
1117            },
1118            Action::MiddleMouseRelease(position) => {
1119                let position: ProtobufPosition = position.try_into()?;
1120                Ok(ProtobufAction {
1121                    name: ProtobufActionName::MiddleMouseRelease as i32,
1122                    optional_payload: Some(OptionalPayload::MiddleMouseReleasePayload(position)),
1123                })
1124            },
1125            Action::MouseHoldLeft(position) => {
1126                let position: ProtobufPosition = position.try_into()?;
1127                Ok(ProtobufAction {
1128                    name: ProtobufActionName::MouseHoldLeft as i32,
1129                    optional_payload: Some(OptionalPayload::MouseHoldLeftPayload(position)),
1130                })
1131            },
1132            Action::MouseHoldRight(position) => {
1133                let position: ProtobufPosition = position.try_into()?;
1134                Ok(ProtobufAction {
1135                    name: ProtobufActionName::MouseHoldRight as i32,
1136                    optional_payload: Some(OptionalPayload::MouseHoldRightPayload(position)),
1137                })
1138            },
1139            Action::MouseHoldMiddle(position) => {
1140                let position: ProtobufPosition = position.try_into()?;
1141                Ok(ProtobufAction {
1142                    name: ProtobufActionName::MouseHoldMiddle as i32,
1143                    optional_payload: Some(OptionalPayload::MouseHoldMiddlePayload(position)),
1144                })
1145            },
1146            Action::SearchInput(bytes) => Ok(ProtobufAction {
1147                name: ProtobufActionName::SearchInput as i32,
1148                optional_payload: Some(OptionalPayload::SearchInputPayload(bytes)),
1149            }),
1150            Action::Search(search_direction) => {
1151                let search_direction: ProtobufSearchDirection = search_direction.try_into()?;
1152                Ok(ProtobufAction {
1153                    name: ProtobufActionName::Search as i32,
1154                    optional_payload: Some(OptionalPayload::SearchPayload(search_direction as i32)),
1155                })
1156            },
1157            Action::SearchToggleOption(search_option) => {
1158                let search_option: ProtobufSearchOption = search_option.try_into()?;
1159                Ok(ProtobufAction {
1160                    name: ProtobufActionName::SearchToggleOption as i32,
1161                    optional_payload: Some(OptionalPayload::SearchToggleOptionPayload(
1162                        search_option as i32,
1163                    )),
1164                })
1165            },
1166            Action::ToggleMouseMode => Ok(ProtobufAction {
1167                name: ProtobufActionName::ToggleMouseMode as i32,
1168                optional_payload: None,
1169            }),
1170            Action::PreviousSwapLayout => Ok(ProtobufAction {
1171                name: ProtobufActionName::PreviousSwapLayout as i32,
1172                optional_payload: None,
1173            }),
1174            Action::NextSwapLayout => Ok(ProtobufAction {
1175                name: ProtobufActionName::NextSwapLayout as i32,
1176                optional_payload: None,
1177            }),
1178            Action::QueryTabNames => Ok(ProtobufAction {
1179                name: ProtobufActionName::QueryTabNames as i32,
1180                optional_payload: None,
1181            }),
1182            Action::NewTiledPluginPane(run_plugin, pane_name, skip_plugin_cache, _cwd) => {
1183                Ok(ProtobufAction {
1184                    name: ProtobufActionName::NewTiledPluginPane as i32,
1185                    optional_payload: Some(OptionalPayload::NewTiledPluginPanePayload(
1186                        NewPluginPanePayload {
1187                            plugin_url: run_plugin.location_string(),
1188                            pane_name,
1189                            skip_plugin_cache,
1190                        },
1191                    )),
1192                })
1193            },
1194            Action::NewFloatingPluginPane(
1195                run_plugin,
1196                pane_name,
1197                skip_plugin_cache,
1198                _cwd,
1199                _coordinates,
1200            ) => Ok(ProtobufAction {
1201                name: ProtobufActionName::NewFloatingPluginPane as i32,
1202                optional_payload: Some(OptionalPayload::NewFloatingPluginPanePayload(
1203                    NewPluginPanePayload {
1204                        plugin_url: run_plugin.location_string(),
1205                        pane_name,
1206                        skip_plugin_cache,
1207                    },
1208                )),
1209            }),
1210            Action::StartOrReloadPlugin(run_plugin) => Ok(ProtobufAction {
1211                name: ProtobufActionName::StartOrReloadPlugin as i32,
1212                optional_payload: Some(OptionalPayload::StartOrReloadPluginPayload(
1213                    run_plugin.location_string(),
1214                )),
1215            }),
1216            Action::CloseTerminalPane(terminal_pane_id) => Ok(ProtobufAction {
1217                name: ProtobufActionName::CloseTerminalPane as i32,
1218                optional_payload: Some(OptionalPayload::CloseTerminalPanePayload(terminal_pane_id)),
1219            }),
1220            Action::ClosePluginPane(plugin_pane_id) => Ok(ProtobufAction {
1221                name: ProtobufActionName::ClosePluginPane as i32,
1222                optional_payload: Some(OptionalPayload::ClosePluginPanePayload(plugin_pane_id)),
1223            }),
1224            Action::FocusTerminalPaneWithId(terminal_pane_id, should_float_if_hidden) => {
1225                Ok(ProtobufAction {
1226                    name: ProtobufActionName::FocusTerminalPaneWithId as i32,
1227                    optional_payload: Some(OptionalPayload::FocusTerminalPaneWithIdPayload(
1228                        PaneIdAndShouldFloat {
1229                            pane_id: terminal_pane_id,
1230                            should_float: should_float_if_hidden,
1231                        },
1232                    )),
1233                })
1234            },
1235            Action::FocusPluginPaneWithId(plugin_pane_id, should_float_if_hidden) => {
1236                Ok(ProtobufAction {
1237                    name: ProtobufActionName::FocusPluginPaneWithId as i32,
1238                    optional_payload: Some(OptionalPayload::FocusPluginPaneWithIdPayload(
1239                        PaneIdAndShouldFloat {
1240                            pane_id: plugin_pane_id,
1241                            should_float: should_float_if_hidden,
1242                        },
1243                    )),
1244                })
1245            },
1246            Action::RenameTerminalPane(terminal_pane_id, new_name) => Ok(ProtobufAction {
1247                name: ProtobufActionName::RenameTerminalPane as i32,
1248                optional_payload: Some(OptionalPayload::RenameTerminalPanePayload(IdAndName {
1249                    name: new_name,
1250                    id: terminal_pane_id,
1251                })),
1252            }),
1253            Action::RenamePluginPane(plugin_pane_id, new_name) => Ok(ProtobufAction {
1254                name: ProtobufActionName::RenamePluginPane as i32,
1255                optional_payload: Some(OptionalPayload::RenamePluginPanePayload(IdAndName {
1256                    name: new_name,
1257                    id: plugin_pane_id,
1258                })),
1259            }),
1260            Action::RenameTab(tab_index, new_name) => Ok(ProtobufAction {
1261                name: ProtobufActionName::RenameTab as i32,
1262                optional_payload: Some(OptionalPayload::RenameTabPayload(IdAndName {
1263                    name: new_name,
1264                    id: tab_index,
1265                })),
1266            }),
1267            Action::BreakPane => Ok(ProtobufAction {
1268                name: ProtobufActionName::BreakPane as i32,
1269                optional_payload: None,
1270            }),
1271            Action::BreakPaneRight => Ok(ProtobufAction {
1272                name: ProtobufActionName::BreakPaneRight as i32,
1273                optional_payload: None,
1274            }),
1275            Action::BreakPaneLeft => Ok(ProtobufAction {
1276                name: ProtobufActionName::BreakPaneLeft as i32,
1277                optional_payload: None,
1278            }),
1279            Action::RenameSession(session_name) => Ok(ProtobufAction {
1280                name: ProtobufActionName::RenameSession as i32,
1281                optional_payload: Some(OptionalPayload::RenameSessionPayload(session_name)),
1282            }),
1283            Action::KeybindPipe { .. } => Ok(ProtobufAction {
1284                name: ProtobufActionName::KeybindPipe as i32,
1285                optional_payload: None,
1286            }),
1287            Action::NoOp
1288            | Action::Confirm
1289            | Action::NewInPlacePane(..)
1290            | Action::NewInPlacePluginPane(..)
1291            | Action::Deny
1292            | Action::Copy
1293            | Action::DumpLayout
1294            | Action::CliPipe { .. }
1295            | Action::ListClients
1296            | Action::SkipConfirm(..) => Err("Unsupported action"),
1297        }
1298    }
1299}
1300
1301impl TryFrom<ProtobufSearchOption> for SearchOption {
1302    type Error = &'static str;
1303    fn try_from(protobuf_search_option: ProtobufSearchOption) -> Result<Self, &'static str> {
1304        match protobuf_search_option {
1305            ProtobufSearchOption::CaseSensitivity => Ok(SearchOption::CaseSensitivity),
1306            ProtobufSearchOption::WholeWord => Ok(SearchOption::WholeWord),
1307            ProtobufSearchOption::Wrap => Ok(SearchOption::Wrap),
1308        }
1309    }
1310}
1311
1312impl TryFrom<SearchOption> for ProtobufSearchOption {
1313    type Error = &'static str;
1314    fn try_from(search_option: SearchOption) -> Result<Self, &'static str> {
1315        match search_option {
1316            SearchOption::CaseSensitivity => Ok(ProtobufSearchOption::CaseSensitivity),
1317            SearchOption::WholeWord => Ok(ProtobufSearchOption::WholeWord),
1318            SearchOption::Wrap => Ok(ProtobufSearchOption::Wrap),
1319        }
1320    }
1321}
1322
1323impl TryFrom<ProtobufSearchDirection> for SearchDirection {
1324    type Error = &'static str;
1325    fn try_from(protobuf_search_direction: ProtobufSearchDirection) -> Result<Self, &'static str> {
1326        match protobuf_search_direction {
1327            ProtobufSearchDirection::Up => Ok(SearchDirection::Up),
1328            ProtobufSearchDirection::Down => Ok(SearchDirection::Down),
1329        }
1330    }
1331}
1332
1333impl TryFrom<SearchDirection> for ProtobufSearchDirection {
1334    type Error = &'static str;
1335    fn try_from(search_direction: SearchDirection) -> Result<Self, &'static str> {
1336        match search_direction {
1337            SearchDirection::Up => Ok(ProtobufSearchDirection::Up),
1338            SearchDirection::Down => Ok(ProtobufSearchDirection::Down),
1339        }
1340    }
1341}
1342
1343impl TryFrom<ProtobufMoveTabDirection> for Direction {
1344    type Error = &'static str;
1345    fn try_from(
1346        protobuf_move_tab_direction: ProtobufMoveTabDirection,
1347    ) -> Result<Self, &'static str> {
1348        match protobuf_move_tab_direction {
1349            ProtobufMoveTabDirection::Left => Ok(Direction::Left),
1350            ProtobufMoveTabDirection::Right => Ok(Direction::Right),
1351        }
1352    }
1353}
1354
1355impl TryFrom<Direction> for ProtobufMoveTabDirection {
1356    type Error = &'static str;
1357    fn try_from(direction: Direction) -> Result<Self, &'static str> {
1358        match direction {
1359            Direction::Left => Ok(ProtobufMoveTabDirection::Left),
1360            Direction::Right => Ok(ProtobufMoveTabDirection::Right),
1361            _ => Err("Wrong direction for ProtobufMoveTabDirection"),
1362        }
1363    }
1364}
1365
1366impl TryFrom<ProtobufRunCommandAction> for RunCommandAction {
1367    type Error = &'static str;
1368    fn try_from(
1369        protobuf_run_command_action: ProtobufRunCommandAction,
1370    ) -> Result<Self, &'static str> {
1371        let command = PathBuf::from(protobuf_run_command_action.command);
1372        let args: Vec<String> = protobuf_run_command_action.args;
1373        let cwd: Option<PathBuf> = protobuf_run_command_action.cwd.map(|c| PathBuf::from(c));
1374        let direction: Option<Direction> = protobuf_run_command_action
1375            .direction
1376            .and_then(|d| ProtobufResizeDirection::from_i32(d))
1377            .and_then(|d| d.try_into().ok());
1378        let hold_on_close = protobuf_run_command_action.hold_on_close;
1379        let hold_on_start = protobuf_run_command_action.hold_on_start;
1380        Ok(RunCommandAction {
1381            command,
1382            args,
1383            cwd,
1384            direction,
1385            hold_on_close,
1386            hold_on_start,
1387            ..Default::default()
1388        })
1389    }
1390}
1391
1392impl TryFrom<RunCommandAction> for ProtobufRunCommandAction {
1393    type Error = &'static str;
1394    fn try_from(run_command_action: RunCommandAction) -> Result<Self, &'static str> {
1395        let command = run_command_action.command.display().to_string();
1396        let args: Vec<String> = run_command_action.args;
1397        let cwd = run_command_action.cwd.map(|c| c.display().to_string());
1398        let direction = run_command_action.direction.and_then(|p| {
1399            let direction: ProtobufResizeDirection = p.try_into().ok()?;
1400            Some(direction as i32)
1401        });
1402        let hold_on_close = run_command_action.hold_on_close;
1403        let hold_on_start = run_command_action.hold_on_start;
1404        Ok(ProtobufRunCommandAction {
1405            command,
1406            args,
1407            cwd,
1408            direction,
1409            hold_on_close,
1410            hold_on_start,
1411            pane_name: None,
1412        })
1413    }
1414}
1415
1416impl TryFrom<ProtobufPosition> for Position {
1417    type Error = &'static str;
1418    fn try_from(protobuf_position: ProtobufPosition) -> Result<Self, &'static str> {
1419        Ok(Position::new(
1420            protobuf_position.line as i32,
1421            protobuf_position.column as u16,
1422        ))
1423    }
1424}
1425
1426impl TryFrom<Position> for ProtobufPosition {
1427    type Error = &'static str;
1428    fn try_from(position: Position) -> Result<Self, &'static str> {
1429        Ok(ProtobufPosition {
1430            line: position.line.0 as i64,
1431            column: position.column.0 as i64,
1432        })
1433    }
1434}
1435
1436impl TryFrom<ProtobufPluginConfiguration> for PluginUserConfiguration {
1437    type Error = &'static str;
1438    fn try_from(plugin_configuration: ProtobufPluginConfiguration) -> Result<Self, &'static str> {
1439        let mut converted = BTreeMap::new();
1440        for name_and_value in plugin_configuration.name_and_value {
1441            converted.insert(name_and_value.name, name_and_value.value);
1442        }
1443        Ok(PluginUserConfiguration::new(converted))
1444    }
1445}
1446
1447impl TryFrom<PluginUserConfiguration> for ProtobufPluginConfiguration {
1448    type Error = &'static str;
1449    fn try_from(plugin_configuration: PluginUserConfiguration) -> Result<Self, &'static str> {
1450        let mut converted = vec![];
1451        for (name, value) in plugin_configuration.inner() {
1452            let name_and_value = ProtobufNameAndValue {
1453                name: name.to_owned(),
1454                value: value.to_owned(),
1455            };
1456            converted.push(name_and_value);
1457        }
1458        Ok(ProtobufPluginConfiguration {
1459            name_and_value: converted,
1460        })
1461    }
1462}
1463
1464impl TryFrom<&ProtobufPluginConfiguration> for BTreeMap<String, String> {
1465    type Error = &'static str;
1466    fn try_from(plugin_configuration: &ProtobufPluginConfiguration) -> Result<Self, &'static str> {
1467        let mut converted = BTreeMap::new();
1468        for name_and_value in &plugin_configuration.name_and_value {
1469            converted.insert(
1470                name_and_value.name.to_owned(),
1471                name_and_value.value.to_owned(),
1472            );
1473        }
1474        Ok(converted)
1475    }
1476}