diff options
| author | Aram Drevekenin <aram@poor.dev> | 2025-12-11 14:25:42 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-12-11 14:25:42 +0100 |
| commit | 693fa082def5ef0fe030cd7fdc3b1df051b35dc6 (patch) | |
| tree | b3f651462bbc1cd2fa5b2d79207343413ef6b83c /zellij-utils/src/plugin_api/action.rs | |
| parent | fix(zellij-server): unblock building on 32bit targets (#4484) (diff) | |
| download | sl0ppij-693fa082def5ef0fe030cd7fdc3b1df051b35dc6.tar.gz sl0ppij-693fa082def5ef0fe030cd7fdc3b1df051b35dc6.tar.xz sl0ppij-693fa082def5ef0fe030cd7fdc3b1df051b35dc6.zip | |
Command Sequences, Blocking CLI Commands, New Plugin APIs (#4546)
* fix deadlocks
* initial logging to plugin implementation
* only log actions, include keys as Action::Write
* send actions to plugins as a new Event::UserAction
* add Action::Write metadata to plugin API
* run_action plugin command and permission
* block-until-exit/success/failure in the cli
* fix tests
* add NewBlockingPane to the plugin API
* run actions in a different thread so as not to block plugin execution
* add custom context to run_action flow
* fixes for floating pane z_index sorting and unknown protobuf events not crashing plugins
* always render the frame of pinned panes
* macros module
* add macros to config
* KDL macro parsing
* thread macros to the plugin thread
* get/update/delete macros from plugins
* add macros updated event
* add ability to rename macros
* fix deadlock
* fix z_indices with pinned unselectable panes bug
* allow plugins to opt-in to show_cursor
* change show_cursor(bool) to show_cursor(Option<coordinates>)
* test
* upgrade strum to allow proper enum iteration and case insensitive fromstr implementation for Actions
* better cwd tracking for exited command panes
* add initial panes to NewTab Action (no-op for now)
* add initial panes to cli newtab action
* make initial panes work
* add close_on_exit and start_suspended to the cli
* properly convert newtab action (potentially breaking for plugins)
* allow first pane in tab to block
* make distinction between opening panes with the last active client and the current pane in the cli
* improve some apis
* fix fixed size closing pane bug
* fix plugin cursor coordinates
* fix issues with launch-or-focus-plugin having to do with in-place and suppressed panes
* fix: set affected pane in replace pane
* allow setting a pane in place when it is suppressed and being focused (tests failing)
* fix tests
* some fixes for unsuppression of panes
* some fixes for completion_tx in new tab action
* allow replace/suppressed rather than replace/close only
* fixes for inserting suppressed panes without conflicts
* pane focus history and explicit unsuppress plugin api
* fix: prefer explicitly provided pane id in placement in actions that provide it
* fix floating pane coordinate change and z_index
* fix plugin cache dir to persist between sessions
* fix some stack focusing issues when adding panes without a client id
* make sure pinned panes are always above non-pinned floating panes
* allow plugins to copy to clipboard
* initial text styling additions implementations - buggy
* fix dimming issues
* remove extras, stay with just dim and unbold
* unbold and dim as indices
* allow ui components to render success/error colors
* allow plugins to send sigint/sigkill to processes, as well as receive their pid
* ui component fixes and send originating plugin context with run_action
* originating plugin for new tab too
* make plugn show pane command also expand stacked panes
* add sequence plugin
* remove macros
* remove snapshots
* add shortcut for sequence plugin
* fix plugin tests
* plugin tests for new apis
* update snapshots for sequence shortcut
* cleanups and formatting
* Event::CwdChanged
* moar cleanups
* remove some warnings
* remove mroe warnings
* remove dead code
* remove more dead code
* fix minor bugs in sequence plugin
* update sequence help and format codebase
* fix conditional blocking cli commands
* fix initial-command new-tab cli flag
* add client_ids to CwdChanged Event
* last fixes
* add pr
Diffstat (limited to 'zellij-utils/src/plugin_api/action.rs')
| -rw-r--r-- | zellij-utils/src/plugin_api/action.rs | 1340 |
1 files changed, 1297 insertions, 43 deletions
diff --git a/zellij-utils/src/plugin_api/action.rs b/zellij-utils/src/plugin_api/action.rs index c442144a..f8de7b76 100644 --- a/zellij-utils/src/plugin_api/action.rs +++ b/zellij-utils/src/plugin_api/action.rs @@ -1,26 +1,88 @@ pub use super::generated_api::api::{ action::{ - action::OptionalPayload, Action as ProtobufAction, ActionName as ProtobufActionName, - DumpScreenPayload, EditFilePayload, GoToTabNamePayload, IdAndName, - LaunchOrFocusPluginPayload, MouseEventPayload as ProtobufMouseEventPayload, - MovePanePayload, MoveTabDirection as ProtobufMoveTabDirection, - NameAndValue as ProtobufNameAndValue, NewFloatingPanePayload, NewPanePayload, - NewPluginPanePayload, NewTiledPanePayload, PaneIdAndShouldFloat, - PluginConfiguration as ProtobufPluginConfiguration, Position as ProtobufPosition, - RunCommandAction as ProtobufRunCommandAction, ScrollAtPayload, - SearchDirection as ProtobufSearchDirection, SearchOption as ProtobufSearchOption, - SwitchToModePayload, WriteCharsPayload, WritePayload, + action::OptionalPayload, + command_or_plugin::CommandOrPluginType, + pane_run::RunType, + run_plugin_location_data::LocationData, + run_plugin_or_alias::PluginType, + Action as ProtobufAction, + ActionName as ProtobufActionName, + BareKey as ProtobufBareKey, + // New layout-related types + CommandOrPlugin as ProtobufCommandOrPlugin, + DumpScreenPayload, + EditFilePayload, + FloatingPaneCoordinates as ProtobufFloatingPaneCoordinates, + FloatingPaneLayout as ProtobufFloatingPaneLayout, + FloatingPlacement as ProtobufFloatingPlacement, + GoToTabNamePayload, + IdAndName, + InPlaceConfig as ProtobufInPlaceConfig, + KeyModifier as ProtobufKeyModifier, + KeyWithModifier as ProtobufKeyWithModifier, + LaunchOrFocusPluginPayload, + LayoutConstraint as ProtobufLayoutConstraint, + LayoutConstraintFloatingPair as ProtobufLayoutConstraintFloatingPair, + LayoutConstraintTiledPair as ProtobufLayoutConstraintTiledPair, + LayoutConstraintWithValue as ProtobufLayoutConstraintWithValue, + MouseEventPayload as ProtobufMouseEventPayload, + MovePanePayload, + MoveTabDirection as ProtobufMoveTabDirection, + NameAndValue as ProtobufNameAndValue, + NewBlockingPanePayload, + NewFloatingPanePayload, + NewInPlacePanePayload, + NewPanePayload, + NewPanePlacement as ProtobufNewPanePlacement, + NewPluginPanePayload, + NewTabPayload, + NewTiledPanePayload, + PaneId as ProtobufPaneId, + PaneIdAndShouldFloat, + PaneRun as ProtobufPaneRun, + PercentOrFixed as ProtobufPercentOrFixed, + PluginAlias as ProtobufPluginAlias, + PluginConfiguration as ProtobufPluginConfiguration, + PluginTag as ProtobufPluginTag, + PluginUserConfiguration as ProtobufPluginUserConfiguration, + Position as ProtobufPosition, + RunCommandAction as ProtobufRunCommandAction, + RunEditFileAction as ProtobufRunEditFileAction, + RunPlugin as ProtobufRunPlugin, + RunPluginLocation as ProtobufRunPluginLocation, + RunPluginLocationData as ProtobufRunPluginLocationData, + RunPluginOrAlias as ProtobufRunPluginOrAlias, + ScrollAtPayload, + SearchDirection as ProtobufSearchDirection, + SearchOption as ProtobufSearchOption, + SplitDirection as ProtobufSplitDirection, + SplitSize as ProtobufSplitSize, + StackedPlacement as ProtobufStackedPlacement, + SwapFloatingLayout as ProtobufSwapFloatingLayout, + SwapTiledLayout as ProtobufSwapTiledLayout, + SwitchToModePayload, + TiledPaneLayout as ProtobufTiledPaneLayout, + TiledPlacement as ProtobufTiledPlacement, + UnblockCondition as ProtobufUnblockCondition, + WriteCharsPayload, + WritePayload, }, input_mode::InputMode as ProtobufInputMode, resize::{Resize as ProtobufResize, ResizeDirection as ProtobufResizeDirection}, }; -use crate::data::{Direction, InputMode, ResizeStrategy}; +use crate::data::{ + CommandOrPlugin, Direction, FloatingPaneCoordinates, InputMode, KeyWithModifier, + NewPanePlacement, PaneId, PluginTag, ResizeStrategy, UnblockCondition, +}; use crate::errors::prelude::*; use crate::input::actions::Action; use crate::input::actions::{SearchDirection, SearchOption}; use crate::input::command::{OpenFilePayload, RunCommandAction}; +use crate::input::layout::SplitSize; use crate::input::layout::{ - PluginUserConfiguration, RunPlugin, RunPluginLocation, RunPluginOrAlias, + FloatingPaneLayout, LayoutConstraint, PercentOrFixed, PluginAlias, PluginUserConfiguration, + Run, RunPlugin, RunPluginLocation, RunPluginOrAlias, SplitDirection, SwapFloatingLayout, + SwapTiledLayout, TiledPaneLayout, }; use crate::input::mouse::{MouseEvent, MouseEventType}; use crate::position::Position; @@ -38,11 +100,16 @@ impl TryFrom<ProtobufAction> for Action { None => Ok(Action::Quit), }, Some(ProtobufActionName::Write) => match protobuf_action.optional_payload { - Some(OptionalPayload::WritePayload(write_payload)) => Ok(Action::Write { - key_with_modifier: None, - bytes: write_payload.bytes_to_write, - is_kitty_keyboard_protocol: false, - }), + Some(OptionalPayload::WritePayload(write_payload)) => { + let key_with_modifier = write_payload + .key_with_modifier + .and_then(|k| k.try_into().ok()); + Ok(Action::Write { + key_with_modifier, + bytes: write_payload.bytes_to_write, + is_kitty_keyboard_protocol: write_payload.is_kitty_keyboard_protocol, + }) + }, _ => Err("Wrong payload for Action::Write"), }, Some(ProtobufActionName::WriteChars) => match protobuf_action.optional_payload { @@ -246,6 +313,7 @@ impl TryFrom<ProtobufAction> for Action { .direction .and_then(|d| ProtobufResizeDirection::from_i32(d)) .and_then(|d| d.try_into().ok()); + let near_current_pane = payload.near_current_pane; let should_float = payload.should_float; let should_be_in_place = false; Ok(Action::EditFile { @@ -255,12 +323,14 @@ impl TryFrom<ProtobufAction> for Action { in_place: should_be_in_place, start_suppressed: false, coordinates: None, + near_current_pane, }) }, _ => Err("Wrong payload for Action::NewPane"), }, Some(ProtobufActionName::NewFloatingPane) => match protobuf_action.optional_payload { Some(OptionalPayload::NewFloatingPanePayload(payload)) => { + let near_current_pane = payload.near_current_pane; if let Some(payload) = payload.command { let pane_name = payload.pane_name.clone(); let run_command_action: RunCommandAction = payload.try_into()?; @@ -268,12 +338,14 @@ impl TryFrom<ProtobufAction> for Action { command: Some(run_command_action), pane_name, coordinates: None, + near_current_pane, }) } else { Ok(Action::NewFloatingPane { command: None, pane_name: None, coordinates: None, + near_current_pane, }) } }, @@ -285,6 +357,7 @@ impl TryFrom<ProtobufAction> for Action { .direction .and_then(|d| ProtobufResizeDirection::from_i32(d)) .and_then(|d| d.try_into().ok()); + let near_current_pane = payload.near_current_pane; if let Some(payload) = payload.command { let pane_name = payload.pane_name.clone(); let run_command_action: RunCommandAction = payload.try_into()?; @@ -292,12 +365,14 @@ impl TryFrom<ProtobufAction> for Action { direction, command: Some(run_command_action), pane_name, + near_current_pane, }) } else { Ok(Action::NewTiledPane { direction, command: None, pane_name: None, + near_current_pane, }) } }, @@ -331,9 +406,77 @@ impl TryFrom<ProtobufAction> for Action { }, Some(ProtobufActionName::NewTab) => { match protobuf_action.optional_payload { - Some(_) => Err("NewTab should not have a payload"), + Some(OptionalPayload::NewTabPayload(payload)) => { + // New behavior: extract all fields from payload + let tiled_layout = + payload.tiled_layout.map(|l| l.try_into()).transpose()?; + + let floating_layouts = payload + .floating_layouts + .into_iter() + .map(|l| l.try_into()) + .collect::<Result<Vec<_>, _>>()?; + + let swap_tiled_layouts = if payload.swap_tiled_layouts.is_empty() { + None + } else { + Some( + payload + .swap_tiled_layouts + .into_iter() + .map(|l| l.try_into()) + .collect::<Result<Vec<_>, _>>()?, + ) + }; + + let swap_floating_layouts = if payload.swap_floating_layouts.is_empty() { + None + } else { + Some( + payload + .swap_floating_layouts + .into_iter() + .map(|l| l.try_into()) + .collect::<Result<Vec<_>, _>>()?, + ) + }; + + let tab_name = payload.tab_name; + let should_change_focus_to_new_tab = payload.should_change_focus_to_new_tab; + let cwd = payload.cwd.map(PathBuf::from); + + let initial_panes = if payload.initial_panes.is_empty() { + None + } else { + Some( + payload + .initial_panes + .into_iter() + .map(|p| p.try_into()) + .collect::<Result<Vec<_>, _>>()?, + ) + }; + + let first_pane_unblock_condition = payload + .first_pane_unblock_condition + .and_then(|uc| ProtobufUnblockCondition::from_i32(uc)) + .and_then(|uc| uc.try_into().ok()); + + Ok(Action::NewTab { + tiled_layout, + floating_layouts, + swap_tiled_layouts, + swap_floating_layouts, + tab_name, + should_change_focus_to_new_tab, + cwd, + initial_panes, + first_pane_unblock_condition, + }) + }, None => { - // we do not serialize the layouts of this action + // Backwards compatibility: accept None payload for existing plugins + // Return the same defaults as before Ok(Action::NewTab { tiled_layout: None, floating_layouts: vec![], @@ -342,8 +485,11 @@ impl TryFrom<ProtobufAction> for Action { tab_name: None, should_change_focus_to_new_tab: true, cwd: None, + initial_panes: None, + first_pane_unblock_condition: None, }) }, + _ => Err("Wrong payload for Action::NewTab"), } }, Some(ProtobufActionName::NoOp) => match protobuf_action.optional_payload { @@ -405,6 +551,7 @@ impl TryFrom<ProtobufAction> for Action { let run_command_action = run_command_action.try_into()?; Ok(Action::Run { command: run_command_action, + near_current_pane: false, }) }, _ => Err("Wrong payload for Action::Run"), @@ -657,9 +804,11 @@ impl TryFrom<ProtobufAction> for Action { Some(OptionalPayload::FocusTerminalPaneWithIdPayload(payload)) => { let terminal_pane_id = payload.pane_id; let should_float_if_hidden = payload.should_float; + let should_be_in_place_if_hidden = payload.should_be_in_place; Ok(Action::FocusTerminalPaneWithId { pane_id: terminal_pane_id, should_float_if_hidden, + should_be_in_place_if_hidden, }) }, _ => Err("Wrong payload for Action::FocusTerminalPaneWithId"), @@ -670,9 +819,11 @@ impl TryFrom<ProtobufAction> for Action { Some(OptionalPayload::FocusPluginPaneWithIdPayload(payload)) => { let plugin_pane_id = payload.pane_id; let should_float_if_hidden = payload.should_float; + let should_be_in_place_if_hidden = payload.should_be_in_place; Ok(Action::FocusPluginPaneWithId { pane_id: plugin_pane_id, should_float_if_hidden, + should_be_in_place_if_hidden, }) }, _ => Err("Wrong payload for Action::FocusPluginPaneWithId"), @@ -768,8 +919,60 @@ impl TryFrom<ProtobufAction> for Action { None => Ok(Action::NewStackedPane { command: None, pane_name: None, + near_current_pane: false, }), }, + Some(ProtobufActionName::NewBlockingPane) => match protobuf_action.optional_payload { + Some(OptionalPayload::NewBlockingPanePayload(payload)) => { + let placement: NewPanePlacement = payload + .placement + .ok_or("NewBlockingPanePayload must have a placement")? + .try_into()?; + let pane_name = payload.pane_name; + let command = payload.command.and_then(|c| c.try_into().ok()); + let unblock_condition = payload + .unblock_condition + .and_then(|uc| ProtobufUnblockCondition::from_i32(uc)) + .and_then(|uc| uc.try_into().ok()); + let near_current_pane = payload.near_current_pane; + Ok(Action::NewBlockingPane { + placement, + pane_name, + command, + unblock_condition, + near_current_pane, + }) + }, + _ => Err("Wrong payload for Action::NewBlockingPane"), + }, + Some(ProtobufActionName::NewInPlacePane) => match protobuf_action.optional_payload { + Some(OptionalPayload::NewInPlacePanePayload(payload)) => { + let near_current_pane = payload.near_current_pane; + let pane_id_to_replace = + payload.pane_id_to_replace.and_then(|p| p.try_into().ok()); + let close_replace_pane = payload.close_replace_pane; + if let Some(command) = payload.command { + let pane_name = command.pane_name.clone(); + let run_command_action: RunCommandAction = command.try_into()?; + Ok(Action::NewInPlacePane { + command: Some(run_command_action), + pane_name, + near_current_pane, + pane_id_to_replace, + close_replace_pane, + }) + } else { + Ok(Action::NewInPlacePane { + command: None, + pane_name: payload.pane_name, + near_current_pane, + pane_id_to_replace, + close_replace_pane, + }) + } + }, + _ => Err("Wrong payload for Action::NewInPlacePane"), + }, _ => Err("Unknown Action"), } } @@ -784,15 +987,20 @@ impl TryFrom<Action> for ProtobufAction { optional_payload: None, }), Action::Write { - key_with_modifier: _, + key_with_modifier, bytes, - is_kitty_keyboard_protocol: _, - } => Ok(ProtobufAction { - name: ProtobufActionName::Write as i32, - optional_payload: Some(OptionalPayload::WritePayload(WritePayload { - bytes_to_write: bytes, - })), - }), + is_kitty_keyboard_protocol, + } => { + let protobuf_key_with_modifier = key_with_modifier.and_then(|k| k.try_into().ok()); + Ok(ProtobufAction { + name: ProtobufActionName::Write as i32, + optional_payload: Some(OptionalPayload::WritePayload(WritePayload { + key_with_modifier: protobuf_key_with_modifier, + bytes_to_write: bytes, + is_kitty_keyboard_protocol, + })), + }) + }, Action::WriteChars { chars: chars_to_write, } => Ok(ProtobufAction { @@ -982,6 +1190,7 @@ impl TryFrom<Action> for ProtobufAction { in_place: _should_be_in_place, start_suppressed: _start_suppressed, coordinates: _floating_pane_coordinates, + near_current_pane, } => { let file_to_edit = open_file_payload.path.display().to_string(); let cwd = open_file_payload.cwd.map(|cwd| cwd.display().to_string()); @@ -997,6 +1206,7 @@ impl TryFrom<Action> for ProtobufAction { should_float, direction, cwd, + near_current_pane, })), }) }, @@ -1004,6 +1214,7 @@ impl TryFrom<Action> for ProtobufAction { command: run_command_action, pane_name, coordinates: _coordinates, + near_current_pane, } => { let command = run_command_action.and_then(|r| { let mut protobuf_run_command_action: ProtobufRunCommandAction = @@ -1014,7 +1225,10 @@ impl TryFrom<Action> for ProtobufAction { Ok(ProtobufAction { name: ProtobufActionName::NewFloatingPane as i32, optional_payload: Some(OptionalPayload::NewFloatingPanePayload( - NewFloatingPanePayload { command }, + NewFloatingPanePayload { + command, + near_current_pane, + }, )), }) }, @@ -1022,6 +1236,7 @@ impl TryFrom<Action> for ProtobufAction { direction, command: run_command_action, pane_name, + near_current_pane, } => { let direction = direction.and_then(|direction| { let protobuf_direction: ProtobufResizeDirection = direction.try_into().ok()?; @@ -1037,7 +1252,11 @@ impl TryFrom<Action> for ProtobufAction { Ok(ProtobufAction { name: ProtobufActionName::NewTiledPane as i32, optional_payload: Some(OptionalPayload::NewTiledPanePayload( - NewTiledPanePayload { direction, command }, + NewTiledPanePayload { + direction, + command, + near_current_pane, + }, )), }) }, @@ -1062,18 +1281,82 @@ impl TryFrom<Action> for ProtobufAction { optional_payload: None, }), Action::NewTab { - tiled_layout: _, - floating_layouts: _, - swap_tiled_layouts: _, - swap_floating_layouts: _, - tab_name: _, - should_change_focus_to_new_tab: _, - cwd: _, + tiled_layout, + floating_layouts, + swap_tiled_layouts, + swap_floating_layouts, + tab_name, + should_change_focus_to_new_tab, + cwd, + initial_panes, + first_pane_unblock_condition, } => { - // we do not serialize the various newtab payloads + // Always send payload (even if all fields are default) + let protobuf_tiled_layout = tiled_layout + .as_ref() + .map(|l| l.clone().try_into()) + .transpose()?; + + let protobuf_floating_layouts = floating_layouts + .iter() + .map(|l| l.clone().try_into()) + .collect::<Result<Vec<_>, _>>()?; + + let protobuf_swap_tiled_layouts = swap_tiled_layouts + .as_ref() + .map(|layouts| { + layouts + .iter() + .map(|l| l.clone().try_into()) + .collect::<Result<Vec<_>, _>>() + }) + .transpose()? + .unwrap_or_default(); + + let protobuf_swap_floating_layouts = swap_floating_layouts + .as_ref() + .map(|layouts| { + layouts + .iter() + .map(|l| l.clone().try_into()) + .collect::<Result<Vec<_>, _>>() + }) + .transpose()? + .unwrap_or_default(); + + let cwd_string = cwd.as_ref().map(|p| p.display().to_string()); + + let protobuf_initial_panes = initial_panes + .as_ref() + .map(|panes| { + panes + .iter() + .map(|p| p.clone().try_into()) + .collect::<Result<Vec<_>, _>>() + }) + .transpose()? + .unwrap_or_default(); + + let protobuf_first_pane_unblock_condition = first_pane_unblock_condition + .map(|uc| { + let protobuf_uc: ProtobufUnblockCondition = uc.try_into().ok()?; + Some(protobuf_uc as i32) + }) + .flatten(); + Ok(ProtobufAction { name: ProtobufActionName::NewTab as i32, - optional_payload: None, + optional_payload: Some(OptionalPayload::NewTabPayload(NewTabPayload { + tiled_layout: protobuf_tiled_layout, + floating_layouts: protobuf_floating_layouts, + swap_tiled_layouts: protobuf_swap_tiled_layouts, + swap_floating_layouts: protobuf_swap_floating_layouts, + tab_name: tab_name.clone(), + should_change_focus_to_new_tab, + cwd: cwd_string, + initial_panes: protobuf_initial_panes, + first_pane_unblock_condition: protobuf_first_pane_unblock_condition, + })), }) }, Action::GoToNextTab => Ok(ProtobufAction { @@ -1123,6 +1406,7 @@ impl TryFrom<Action> for ProtobufAction { }, Action::Run { command: run_command_action, + near_current_pane: _, } => { let run_command_action: ProtobufRunCommandAction = run_command_action.try_into()?; Ok(ProtobufAction { @@ -1277,24 +1561,28 @@ impl TryFrom<Action> for ProtobufAction { Action::FocusTerminalPaneWithId { pane_id: terminal_pane_id, should_float_if_hidden, + should_be_in_place_if_hidden, } => Ok(ProtobufAction { name: ProtobufActionName::FocusTerminalPaneWithId as i32, optional_payload: Some(OptionalPayload::FocusTerminalPaneWithIdPayload( PaneIdAndShouldFloat { pane_id: terminal_pane_id, should_float: should_float_if_hidden, + should_be_in_place: should_be_in_place_if_hidden, }, )), }), Action::FocusPluginPaneWithId { pane_id: plugin_pane_id, should_float_if_hidden, + should_be_in_place_if_hidden, } => Ok(ProtobufAction { name: ProtobufActionName::FocusPluginPaneWithId as i32, optional_payload: Some(OptionalPayload::FocusPluginPaneWithIdPayload( PaneIdAndShouldFloat { pane_id: plugin_pane_id, should_float: should_float_if_hidden, + should_be_in_place: should_be_in_place_if_hidden, }, )), }), @@ -1363,16 +1651,72 @@ impl TryFrom<Action> for ProtobufAction { Action::NewStackedPane { command: _, pane_name: _, + near_current_pane: _, } => Ok(ProtobufAction { name: ProtobufActionName::NewStackedPane as i32, optional_payload: None, }), + Action::NewBlockingPane { + placement, + pane_name, + command, + unblock_condition, + near_current_pane, + } => { + let placement: ProtobufNewPanePlacement = placement.try_into()?; + let command = command.and_then(|c| { + let protobuf_command: ProtobufRunCommandAction = c.try_into().ok()?; + Some(protobuf_command) + }); + let unblock_condition = unblock_condition + .map(|uc| { + let protobuf_uc: ProtobufUnblockCondition = uc.try_into().ok()?; + Some(protobuf_uc as i32) + }) + .flatten(); + Ok(ProtobufAction { + name: ProtobufActionName::NewBlockingPane as i32, + optional_payload: Some(OptionalPayload::NewBlockingPanePayload( + NewBlockingPanePayload { + placement: Some(placement), + pane_name, + command, + unblock_condition, + near_current_pane, + }, + )), + }) + }, + Action::NewInPlacePane { + command: run_command_action, + pane_name, + near_current_pane, + pane_id_to_replace, + close_replace_pane, + } => { + let command = run_command_action.and_then(|r| { + let mut protobuf_run_command_action: ProtobufRunCommandAction = + r.try_into().ok()?; + let pane_name = pane_name.and_then(|n| n.try_into().ok()); + protobuf_run_command_action.pane_name = pane_name; + Some(protobuf_run_command_action) + }); + let pane_id_to_replace = pane_id_to_replace.and_then(|p| p.try_into().ok()); + Ok(ProtobufAction { + name: ProtobufActionName::NewInPlacePane as i32, + optional_payload: Some(OptionalPayload::NewInPlacePanePayload( + NewInPlacePanePayload { + command, + pane_name: None, // pane_name is already embedded in command + near_current_pane, + pane_id_to_replace, + close_replace_pane, + }, + )), + }) + }, Action::NoOp | Action::Confirm - | Action::NewInPlacePane { - command: _, - pane_name: _, - } | Action::NewInPlacePluginPane { plugin: _, pane_name: _, @@ -1389,7 +1733,6 @@ impl TryFrom<Action> for ProtobufAction { coordinates: _, } | Action::SkipConfirm { action: _ } - | Action::NewBlockingPane { .. } | Action::SwitchSession { .. } => Err("Unsupported action"), } } @@ -1616,3 +1959,914 @@ impl TryFrom<&ProtobufPluginConfiguration> for BTreeMap<String, String> { Ok(converted) } } + +impl TryFrom<ProtobufKeyWithModifier> for KeyWithModifier { + type Error = &'static str; + fn try_from(protobuf_key: ProtobufKeyWithModifier) -> Result<Self, &'static str> { + let bare_key = match ProtobufBareKey::from_i32(protobuf_key.bare_key) { + Some(ProtobufBareKey::PageDown) => crate::data::BareKey::PageDown, + Some(ProtobufBareKey::PageUp) => crate::data::BareKey::PageUp, + Some(ProtobufBareKey::Left) => crate::data::BareKey::Left, + Some(ProtobufBareKey::Down) => crate::data::BareKey::Down, + Some(ProtobufBareKey::Up) => crate::data::BareKey::Up, + Some(ProtobufBareKey::Right) => crate::data::BareKey::Right, + Some(ProtobufBareKey::Home) => crate::data::BareKey::Home, + Some(ProtobufBareKey::End) => crate::data::BareKey::End, + Some(ProtobufBareKey::Backspace) => crate::data::BareKey::Backspace, + Some(ProtobufBareKey::Delete) => crate::data::BareKey::Delete, + Some(ProtobufBareKey::Insert) => crate::data::BareKey::Insert, + Some(ProtobufBareKey::F1) => crate::data::BareKey::F(1), + Some(ProtobufBareKey::F2) => crate::data::BareKey::F(2), + Some(ProtobufBareKey::F3) => crate::data::BareKey::F(3), + Some(ProtobufBareKey::F4) => crate::data::BareKey::F(4), + Some(ProtobufBareKey::F5) => crate::data::BareKey::F(5), + Some(ProtobufBareKey::F6) => crate::data::BareKey::F(6), + Some(ProtobufBareKey::F7) => crate::data::BareKey::F(7), + Some(ProtobufBareKey::F8) => crate::data::BareKey::F(8), + Some(ProtobufBareKey::F9) => crate::data::BareKey::F(9), + Some(ProtobufBareKey::F10) => crate::data::BareKey::F(10), + Some(ProtobufBareKey::F11) => crate::data::BareKey::F(11), + Some(ProtobufBareKey::F12) => crate::data::BareKey::F(12), + Some(ProtobufBareKey::Char) => { + if let Some(character) = protobuf_key.character { + let ch = character + .chars() + .next() + .ok_or("BareKey::Char requires a character")?; + crate::data::BareKey::Char(ch) + } else { + return Err("BareKey::Char requires a character"); + } + }, + Some(ProtobufBareKey::Tab) => crate::data::BareKey::Tab, + Some(ProtobufBareKey::Esc) => crate::data::BareKey::Esc, + Some(ProtobufBareKey::Enter) => crate::data::BareKey::Enter, + Some(ProtobufBareKey::CapsLock) => crate::data::BareKey::CapsLock, + Some(ProtobufBareKey::ScrollLock) => crate::data::BareKey::ScrollLock, + Some(ProtobufBareKey::NumLock) => crate::data::BareKey::NumLock, + Some(ProtobufBareKey::PrintScreen) => crate::data::BareKey::PrintScreen, + Some(ProtobufBareKey::Pause) => crate::data::BareKey::Pause, + Some(ProtobufBareKey::Menu) => crate::data::BareKey::Menu, + _ => return Err("Unknown BareKey"), + }; + + let mut key_modifiers = std::collections::BTreeSet::new(); + for modifier in protobuf_key.key_modifiers { + let key_modifier = match ProtobufKeyModifier::from_i32(modifier) { + Some(ProtobufKeyModifier::Ctrl) => crate::data::KeyModifier::Ctrl, + Some(ProtobufKeyModifier::Alt) => crate::data::KeyModifier::Alt, + Some(ProtobufKeyModifier::Shift) => crate::data::KeyModifier::Shift, + Some(ProtobufKeyModifier::Super) => crate::data::KeyModifier::Super, + _ => continue, + }; + key_modifiers.insert(key_modifier); + } + + Ok(KeyWithModifier { + bare_key, + key_modifiers, + }) + } +} + +impl TryFrom<KeyWithModifier> for ProtobufKeyWithModifier { + type Error = &'static str; + fn try_from(key: KeyWithModifier) -> Result<Self, &'static str> { + let (bare_key, character) = match key.bare_key { + crate::data::BareKey::PageDown => (ProtobufBareKey::PageDown as i32, None), + crate::data::BareKey::PageUp => (ProtobufBareKey::PageUp as i32, None), + crate::data::BareKey::Left => (ProtobufBareKey::Left as i32, None), + crate::data::BareKey::Down => (ProtobufBareKey::Down as i32, None), + crate::data::BareKey::Up => (ProtobufBareKey::Up as i32, None), + crate::data::BareKey::Right => (ProtobufBareKey::Right as i32, None), + crate::data::BareKey::Home => (ProtobufBareKey::Home as i32, None), + crate::data::BareKey::End => (ProtobufBareKey::End as i32, None), + crate::data::BareKey::Backspace => (ProtobufBareKey::Backspace as i32, None), + crate::data::BareKey::Delete => (ProtobufBareKey::Delete as i32, None), + crate::data::BareKey::Insert => (ProtobufBareKey::Insert as i32, None), + crate::data::BareKey::F(1) => (ProtobufBareKey::F1 as i32, None), + crate::data::BareKey::F(2) => (ProtobufBareKey::F2 as i32, None), + crate::data::BareKey::F(3) => (ProtobufBareKey::F3 as i32, None), + crate::data::BareKey::F(4) => (ProtobufBareKey::F4 as i32, None), + crate::data::BareKey::F(5) => (ProtobufBareKey::F5 as i32, None), + crate::data::BareKey::F(6) => (ProtobufBareKey::F6 as i32, None), + crate::data::BareKey::F(7) => (ProtobufBareKey::F7 as i32, None), + crate::data::BareKey::F(8) => (ProtobufBareKey::F8 as i32, None), + crate::data::BareKey::F(9) => (ProtobufBareKey::F9 as i32, None), + crate::data::BareKey::F(10) => (ProtobufBareKey::F10 as i32, None), + crate::data::BareKey::F(11) => (ProtobufBareKey::F11 as i32, None), + crate::data::BareKey::F(12) => (ProtobufBareKey::F12 as i32, None), + crate::data::BareKey::Char(c) => (ProtobufBareKey::Char as i32, Some(c.to_string())), + crate::data::BareKey::Tab => (ProtobufBareKey::Tab as i32, None), + crate::data::BareKey::Esc => (ProtobufBareKey::Esc as i32, None), + crate::data::BareKey::Enter => (ProtobufBareKey::Enter as i32, None), + crate::data::BareKey::CapsLock => (ProtobufBareKey::CapsLock as i32, None), + crate::data::BareKey::ScrollLock => (ProtobufBareKey::ScrollLock as i32, None), + crate::data::BareKey::NumLock => (ProtobufBareKey::NumLock as i32, None), + crate::data::BareKey::PrintScreen => (ProtobufBareKey::PrintScreen as i32, None), + crate::data::BareKey::Pause => (ProtobufBareKey::Pause as i32, None), + crate::data::BareKey::Menu => (ProtobufBareKey::Menu as i32, None), + _ => return Err("Unsupported BareKey"), + }; + + let key_modifiers: Vec<i32> = key + .key_modifiers + .iter() + .map(|m| match m { + crate::data::KeyModifier::Ctrl => ProtobufKeyModifier::Ctrl as i32, + crate::data::KeyModifier::Alt => ProtobufKeyModifier::Alt as i32, + crate::data::KeyModifier::Shift => ProtobufKeyModifier::Shift as i32, + crate::data::KeyModifier::Super => ProtobufKeyModifier::Super as i32, + }) + .collect(); + + Ok(ProtobufKeyWithModifier { + bare_key, + key_modifiers, + character, + }) + } +} + +// UnblockCondition conversions +impl TryFrom<ProtobufUnblockCondition> for UnblockCondition { + type Error = &'static str; + fn try_from(protobuf_uc: ProtobufUnblockCondition) -> Result<Self, &'static str> { + match protobuf_uc { + ProtobufUnblockCondition::UnblockOnExitSuccess => Ok(UnblockCondition::OnExitSuccess), + ProtobufUnblockCondition::UnblockOnExitFailure => Ok(UnblockCondition::OnExitFailure), + ProtobufUnblockCondition::UnblockOnAnyExit => Ok(UnblockCondition::OnAnyExit), + } + } +} + +impl TryFrom<UnblockCondition> for ProtobufUnblockCondition { + type Error = &'static str; + fn try_from(uc: UnblockCondition) -> Result<Self, &'static str> { + match uc { + UnblockCondition::OnExitSuccess => Ok(ProtobufUnblockCondition::UnblockOnExitSuccess), + UnblockCondition::OnExitFailure => Ok(ProtobufUnblockCondition::UnblockOnExitFailure), + UnblockCondition::OnAnyExit => Ok(ProtobufUnblockCondition::UnblockOnAnyExit), + } + } +} + +impl TryFrom<i32> for UnblockCondition { + type Error = &'static str; + fn try_from(value: i32) -> Result<Self, &'static str> { + match ProtobufUnblockCondition::from_i32(value) { + Some(uc) => uc.try_into(), + None => Err("Invalid UnblockCondition value"), + } + } +} + +// PaneId conversions +impl TryFrom<ProtobufPaneId> for PaneId { + type Error = &'static str; + fn try_from(protobuf_pane_id: ProtobufPaneId) -> Result<Self, &'static str> { + use super::generated_api::api::action::pane_id::PaneIdVariant; + + match protobuf_pane_id.pane_id_variant { + Some(PaneIdVariant::Terminal(id)) => Ok(PaneId::Terminal(id)), + Some(PaneIdVariant::Plugin(id)) => Ok(PaneId::Plugin(id)), + None => Err("PaneId must have either terminal or plugin id"), + } + } +} + +impl TryFrom<PaneId> for ProtobufPaneId { + type Error = &'static str; + fn try_from(pane_id: PaneId) -> Result<Self, &'static str> { + use super::generated_api::api::action::pane_id::PaneIdVariant; + + let pane_id_variant = match pane_id { + PaneId::Terminal(id) => Some(PaneIdVariant::Terminal(id)), + PaneId::Plugin(id) => Some(PaneIdVariant::Plugin(id)), + }; + Ok(ProtobufPaneId { pane_id_variant }) + } +} + +// SplitSize conversions +impl TryFrom<ProtobufSplitSize> for SplitSize { + type Error = &'static str; + fn try_from(protobuf_split_size: ProtobufSplitSize) -> Result<Self, &'static str> { + use super::generated_api::api::action::split_size::SplitSizeVariant; + + match protobuf_split_size.split_size_variant { + Some(SplitSizeVariant::Percent(p)) => Ok(SplitSize::Percent(p as usize)), + Some(SplitSizeVariant::Fixed(f)) => Ok(SplitSize::Fixed(f as usize)), + None => Err("SplitSize must have either percent or fixed value"), + } + } +} + +impl TryFrom<SplitSize> for ProtobufSplitSize { + type Error = &'static str; + fn try_from(split_size: SplitSize) -> Result<Self, &'static str> { + use super::generated_api::api::action::split_size::SplitSizeVariant; + + let split_size_variant = match split_size { + SplitSize::Percent(p) => Some(SplitSizeVariant::Percent(p as u32)), + SplitSize::Fixed(f) => Some(SplitSizeVariant::Fixed(f as u32)), + }; + Ok(ProtobufSplitSize { split_size_variant }) + } +} + +// FloatingPaneCoordinates conversions +impl TryFrom<ProtobufFloatingPaneCoordinates> for FloatingPaneCoordinates { + type Error = &'static str; + fn try_from(protobuf_coords: ProtobufFloatingPaneCoordinates) -> Result<Self, &'static str> { + Ok(FloatingPaneCoordinates { + x: protobuf_coords.x.and_then(|x| x.try_into().ok()), + y: protobuf_coords.y.and_then(|y| y.try_into().ok()), + width: protobuf_coords.width.and_then(|w| w.try_into().ok()), + height: protobuf_coords.height.and_then(|h| h.try_into().ok()), + pinned: protobuf_coords.pinned, + }) + } +} + +impl TryFrom<FloatingPaneCoordinates> for ProtobufFloatingPaneCoordinates { + type Error = &'static str; + fn try_from(coords: FloatingPaneCoordinates) -> Result<Self, &'static str> { + Ok(ProtobufFloatingPaneCoordinates { + x: coords.x.and_then(|x| x.try_into().ok()), + y: coords.y.and_then(|y| y.try_into().ok()), + width: coords.width.and_then(|w| w.try_into().ok()), + height: coords.height.and_then(|h| h.try_into().ok()), + pinned: coords.pinned, + }) + } +} + +// NewPanePlacement conversions +impl TryFrom<ProtobufNewPanePlacement> for NewPanePlacement { + type Error = &'static str; + fn try_from(protobuf_placement: ProtobufNewPanePlacement) -> Result<Self, &'static str> { + use super::generated_api::api::action::new_pane_placement::PlacementVariant; + + match protobuf_placement.placement_variant { + Some(PlacementVariant::NoPreference(_)) => Ok(NewPanePlacement::NoPreference), + Some(PlacementVariant::Tiled(tiled)) => { + let direction = tiled + .direction + .and_then(|d| ProtobufResizeDirection::from_i32(d)) + .and_then(|d| d.try_into().ok()); + Ok(NewPanePlacement::Tiled(direction)) + }, + Some(PlacementVariant::Floating(floating)) => { + let coords = floating.coordinates.and_then(|c| c.try_into().ok()); + Ok(NewPanePlacement::Floating(coords)) + }, + Some(PlacementVariant::InPlace(config)) => { + let pane_id_to_replace = + config.pane_id_to_replace.and_then(|id| id.try_into().ok()); + Ok(NewPanePlacement::InPlace { + pane_id_to_replace, + close_replaced_pane: config.close_replaced_pane, + }) + }, + Some(PlacementVariant::Stacked(stacked)) => { + let pane_id = stacked.pane_id.and_then(|id| id.try_into().ok()); + Ok(NewPanePlacement::Stacked(pane_id)) + }, + None => Err("NewPanePlacement must have a placement variant"), + } + } +} + +impl TryFrom<NewPanePlacement> for ProtobufNewPanePlacement { + type Error = &'static str; + fn try_from(placement: NewPanePlacement) -> Result<Self, &'static str> { + use super::generated_api::api::action::new_pane_placement::PlacementVariant; + + let placement_variant = match placement { + NewPanePlacement::NoPreference => Some(PlacementVariant::NoPreference(true)), + NewPanePlacement::Tiled(direction) => { + let direction = direction.and_then(|d| { + let protobuf_direction: ProtobufResizeDirection = d.try_into().ok()?; + Some(protobuf_direction as i32) + }); + Some(PlacementVariant::Tiled(ProtobufTiledPlacement { + direction, + })) + }, + NewPanePlacement::Floating(coords) => { + let coordinates = coords.and_then(|c| c.try_into().ok()); + Some(PlacementVariant::Floating(ProtobufFloatingPlacement { + coordinates, + })) + }, + NewPanePlacement::InPlace { + pane_id_to_replace, + close_replaced_pane, + } => { + let pane_id_to_replace = pane_id_to_replace.and_then(|id| id.try_into().ok()); + Some(PlacementVariant::InPlace(ProtobufInPlaceConfig { + pane_id_to_replace, + close_replaced_pane, + })) + }, + NewPanePlacement::Stacked(pane_id) => { + let pane_id = pane_id.and_then(|id| id.try_into().ok()); + Some(PlacementVariant::Stacked(ProtobufStackedPlacement { + pane_id, + })) + }, + }; + + Ok(ProtobufNewPanePlacement { placement_variant }) + } +} + +// Layout type conversions + +impl TryFrom<ProtobufPercentOrFixed> for PercentOrFixed { + type Error = &'static str; + fn try_from(protobuf: ProtobufPercentOrFixed) -> Result<Self, Self::Error> { + use super::generated_api::api::action::percent_or_fixed::SizeType; + match protobuf.size_type { + Some(SizeType::Percent(p)) => Ok(PercentOrFixed::Percent(p as usize)), + Some(SizeType::Fixed(f)) => Ok(PercentOrFixed::Fixed(f as usize)), + None => Err("PercentOrFixed must have a size_type"), + } + } +} + +impl TryFrom<PercentOrFixed> for ProtobufPercentOrFixed { + type Error = &'static str; + fn try_from(internal: PercentOrFixed) -> Result<Self, Self::Error> { + use super::generated_api::api::action::percent_or_fixed::SizeType; + let size_type = match internal { + PercentOrFixed::Percent(p) => Some(SizeType::Percent(p as u32)), + PercentOrFixed::Fixed(f) => Some(SizeType::Fixed(f as u32)), + }; + Ok(ProtobufPercentOrFixed { size_type }) + } +} + +impl TryFrom<ProtobufSplitDirection> for SplitDirection { + type Error = &'static str; + fn try_from(protobuf: ProtobufSplitDirection) -> Result<Self, Self::Error> { + match protobuf { + ProtobufSplitDirection::Horizontal => Ok(SplitDirection::Horizontal), + ProtobufSplitDirection::Vertical => Ok(SplitDirection::Vertical), + ProtobufSplitDirection::Unspecified => Err("SplitDirection cannot be unspecified"), + } + } +} + +impl TryFrom<SplitDirection> for ProtobufSplitDirection { + type Error = &'static str; + fn try_from(internal: SplitDirection) -> Result<Self, Self::Error> { + Ok(match internal { + SplitDirection::Horizontal => ProtobufSplitDirection::Horizontal, + SplitDirection::Vertical => ProtobufSplitDirection::Vertical, + }) + } +} + +impl TryFrom<ProtobufLayoutConstraint> for LayoutConstraint { + type Error = &'static str; + fn try_from(protobuf: ProtobufLayoutConstraint) -> Result<Self, Self::Error> { + match protobuf { + ProtobufLayoutConstraint::MaxPanes => Ok(LayoutConstraint::MaxPanes(0)), + ProtobufLayoutConstraint::MinPanes => Ok(LayoutConstraint::MinPanes(0)), + ProtobufLayoutConstraint::ExactPanes => Ok(LayoutConstraint::ExactPanes(0)), + ProtobufLayoutConstraint::NoConstraint => Ok(LayoutConstraint::NoConstraint), + ProtobufLayoutConstraint::Unspecified => Err("LayoutConstraint cannot be unspecified"), + } + } +} + +impl TryFrom<LayoutConstraint> for ProtobufLayoutConstraint { + type Error = &'static str; + fn try_from(internal: LayoutConstraint) -> Result<Self, Self::Error> { + Ok(match internal { + LayoutConstraint::MaxPanes(_) => ProtobufLayoutConstraint::MaxPanes, + LayoutConstraint::MinPanes(_) => ProtobufLayoutConstraint::MinPanes, + LayoutConstraint::ExactPanes(_) => ProtobufLayoutConstraint::ExactPanes, + LayoutConstraint::NoConstraint => ProtobufLayoutConstraint::NoConstraint, + }) + } +} + +impl TryFrom<ProtobufLayoutConstraintWithValue> for LayoutConstraint { + type Error = &'static str; + fn try_from(protobuf: ProtobufLayoutConstraintWithValue) -> Result<Self, Self::Error> { + let constraint_type = ProtobufLayoutConstraint::from_i32(protobuf.constraint_type) + .ok_or("Invalid constraint type")?; + match constraint_type { + ProtobufLayoutConstraint::MaxPanes => Ok(LayoutConstraint::MaxPanes( + protobuf.value.unwrap_or(0) as usize, + )), + ProtobufLayoutConstraint::MinPanes => Ok(LayoutConstraint::MinPanes( + protobuf.value.unwrap_or(0) as usize, + )), + ProtobufLayoutConstraint::ExactPanes => Ok(LayoutConstraint::ExactPanes( + protobuf.value.unwrap_or(0) as usize, + )), + ProtobufLayoutConstraint::NoConstraint => Ok(LayoutConstraint::NoConstraint), + ProtobufLayoutConstraint::Unspecified => Err("LayoutConstraint cannot be unspecified"), + } + } +} + +impl TryFrom<LayoutConstraint> for ProtobufLayoutConstraintWithValue { + type Error = &'static str; + fn try_from(internal: LayoutConstraint) -> Result<Self, Self::Error> { + let (constraint_type, value) = match internal { + LayoutConstraint::MaxPanes(v) => { + (ProtobufLayoutConstraint::MaxPanes as i32, Some(v as u32)) + }, + LayoutConstraint::MinPanes(v) => { + (ProtobufLayoutConstraint::MinPanes as i32, Some(v as u32)) + }, + LayoutConstraint::ExactPanes(v) => { + (ProtobufLayoutConstraint::ExactPanes as i32, Some(v as u32)) + }, + LayoutConstraint::NoConstraint => (ProtobufLayoutConstraint::NoConstraint as i32, None), + }; + Ok(ProtobufLayoutConstraintWithValue { + constraint_type, + value, + }) + } +} + +impl TryFrom<ProtobufPluginTag> for PluginTag { + type Error = &'static str; + fn try_from(protobuf: ProtobufPluginTag) -> Result<Self, Self::Error> { + Ok(PluginTag::new(protobuf.tag)) + } +} + +impl TryFrom<PluginTag> for ProtobufPluginTag { + type Error = &'static str; + fn try_from(internal: PluginTag) -> Result<Self, Self::Error> { + Ok(ProtobufPluginTag { + tag: internal.into(), + }) + } +} + +impl TryFrom<ProtobufPluginUserConfiguration> for PluginUserConfiguration { + type Error = &'static str; + fn try_from(protobuf: ProtobufPluginUserConfiguration) -> Result<Self, Self::Error> { + let btree_map: BTreeMap<String, String> = protobuf.configuration.into_iter().collect(); + Ok(PluginUserConfiguration::new(btree_map)) + } +} + +impl TryFrom<PluginUserConfiguration> for ProtobufPluginUserConfiguration { + type Error = &'static str; + fn try_from(internal: PluginUserConfiguration) -> Result<Self, Self::Error> { + let configuration = internal.inner().clone().into_iter().collect(); + Ok(ProtobufPluginUserConfiguration { configuration }) + } +} + +impl TryFrom<ProtobufRunPluginLocationData> for RunPluginLocation { + type Error = &'static str; + fn try_from(protobuf: ProtobufRunPluginLocationData) -> Result<Self, Self::Error> { + use super::generated_api::api::action::run_plugin_location_data::LocationData; + match protobuf.location_data { + Some(LocationData::FilePath(path)) => Ok(RunPluginLocation::File(PathBuf::from(path))), + Some(LocationData::ZellijTag(tag)) => Ok(RunPluginLocation::Zellij(tag.try_into()?)), + Some(LocationData::RemoteUrl(url)) => Ok(RunPluginLocation::Remote(url)), + None => Err("RunPluginLocationData must have location_data"), + } + } +} + +impl TryFrom<RunPluginLocation> for ProtobufRunPluginLocationData { + type Error = &'static str; + fn try_from(internal: RunPluginLocation) -> Result<Self, Self::Error> { + use super::generated_api::api::action::{ + run_plugin_location_data::LocationData, + RunPluginLocation as ProtobufRunPluginLocationType, + }; + let (location_type, location_data) = match internal { + RunPluginLocation::File(path) => ( + ProtobufRunPluginLocationType::File as i32, + Some(LocationData::FilePath(path.display().to_string())), + ), + RunPluginLocation::Zellij(tag) => ( + ProtobufRunPluginLocationType::Zellij as i32, + Some(LocationData::ZellijTag(tag.try_into()?)), + ), + RunPluginLocation::Remote(url) => ( + ProtobufRunPluginLocationType::Remote as i32, + Some(LocationData::RemoteUrl(url)), + ), + }; + Ok(ProtobufRunPluginLocationData { + location_type, + location_data, + }) + } +} + +impl TryFrom<ProtobufRunPlugin> for RunPlugin { + type Error = &'static str; + fn try_from(protobuf: ProtobufRunPlugin) -> Result<Self, Self::Error> { + let location = protobuf + .location + .ok_or("RunPlugin must have location")? + .try_into()?; + let configuration = protobuf + .configuration + .ok_or("RunPlugin must have configuration")? + .try_into()?; + let initial_cwd = protobuf.initial_cwd.map(PathBuf::from); + Ok(RunPlugin { + _allow_exec_host_cmd: protobuf.allow_exec_host_cmd, + location, + configuration, + initial_cwd, + }) + } +} + +impl TryFrom<RunPlugin> for ProtobufRunPlugin { + type Error = &'static str; + fn try_from(internal: RunPlugin) -> Result<Self, Self::Error> { + Ok(ProtobufRunPlugin { + allow_exec_host_cmd: internal._allow_exec_host_cmd, + location: Some(internal.location.try_into()?), + configuration: Some(internal.configuration.try_into()?), + initial_cwd: internal.initial_cwd.map(|p| p.display().to_string()), + }) + } +} + +impl TryFrom<ProtobufPluginAlias> for PluginAlias { + type Error = &'static str; + fn try_from(protobuf: ProtobufPluginAlias) -> Result<Self, Self::Error> { + let configuration = protobuf.configuration.map(|c| c.try_into()).transpose()?; + let initial_cwd = protobuf.initial_cwd.map(PathBuf::from); + let run_plugin = protobuf.run_plugin.map(|r| r.try_into()).transpose()?; + Ok(PluginAlias { + name: protobuf.name, + configuration, + initial_cwd, + run_plugin, + }) + } +} + +impl TryFrom<PluginAlias> for ProtobufPluginAlias { + type Error = &'static str; + fn try_from(internal: PluginAlias) -> Result<Self, Self::Error> { + Ok(ProtobufPluginAlias { + name: internal.name, + configuration: internal.configuration.map(|c| c.try_into()).transpose()?, + initial_cwd: internal.initial_cwd.map(|p| p.display().to_string()), + run_plugin: internal.run_plugin.map(|r| r.try_into()).transpose()?, + }) + } +} + +impl TryFrom<ProtobufRunPluginOrAlias> for RunPluginOrAlias { + type Error = &'static str; + fn try_from(protobuf: ProtobufRunPluginOrAlias) -> Result<Self, Self::Error> { + use super::generated_api::api::action::run_plugin_or_alias::PluginType; + match protobuf.plugin_type { + Some(PluginType::Plugin(plugin)) => Ok(RunPluginOrAlias::RunPlugin(plugin.try_into()?)), + Some(PluginType::Alias(alias)) => Ok(RunPluginOrAlias::Alias(alias.try_into()?)), + None => Err("RunPluginOrAlias must have plugin_type"), + } + } +} + +impl TryFrom<RunPluginOrAlias> for ProtobufRunPluginOrAlias { + type Error = &'static str; + fn try_from(internal: RunPluginOrAlias) -> Result<Self, Self::Error> { + use super::generated_api::api::action::run_plugin_or_alias::PluginType; + let plugin_type = match internal { + RunPluginOrAlias::RunPlugin(plugin) => Some(PluginType::Plugin(plugin.try_into()?)), + RunPluginOrAlias::Alias(alias) => Some(PluginType::Alias(alias.try_into()?)), + }; + Ok(ProtobufRunPluginOrAlias { plugin_type }) + } +} + +impl TryFrom<ProtobufRunEditFileAction> for Run { + type Error = &'static str; + fn try_from(protobuf: ProtobufRunEditFileAction) -> Result<Self, Self::Error> { + let file_path = PathBuf::from(protobuf.file_path); + let line_number = protobuf.line_number.map(|n| n as usize); + let cwd = protobuf.cwd.map(PathBuf::from); + Ok(Run::EditFile(file_path, line_number, cwd)) + } +} + +impl TryFrom<ProtobufPaneRun> for Run { + type Error = &'static str; + fn try_from(protobuf: ProtobufPaneRun) -> Result<Self, Self::Error> { + use super::generated_api::api::action::pane_run::RunType; + use crate::input::command::RunCommand; + match protobuf.run_type { + Some(RunType::Command(cmd)) => { + let run_command_action: RunCommandAction = cmd.try_into()?; + let run_command: RunCommand = run_command_action.into(); + Ok(Run::Command(run_command)) + }, + Some(RunType::Plugin(plugin)) => Ok(Run::Plugin(plugin.try_into()?)), + Some(RunType::EditFile(edit)) => edit.try_into(), + Some(RunType::Cwd(cwd)) => Ok(Run::Cwd(PathBuf::from(cwd))), + None => Err("PaneRun must have run_type"), + } + } +} + +impl TryFrom<Run> for ProtobufPaneRun { + type Error = &'static str; + fn try_from(internal: Run) -> Result<Self, Self::Error> { + use super::generated_api::api::action::pane_run::RunType; + let run_type = match internal { + Run::Command(cmd) => { + let run_command_action: RunCommandAction = cmd.into(); + Some(RunType::Command(run_command_action.try_into()?)) + }, + Run::Plugin(plugin) => Some(RunType::Plugin(plugin.try_into()?)), + Run::EditFile(file_path, line_number, cwd) => { + Some(RunType::EditFile(ProtobufRunEditFileAction { + file_path: file_path.display().to_string(), + line_number: line_number.map(|n| n as u32), + cwd: cwd.map(|p| p.display().to_string()), + })) + }, + Run::Cwd(cwd) => Some(RunType::Cwd(cwd.display().to_string())), + }; + Ok(ProtobufPaneRun { run_type }) + } +} + +impl TryFrom<ProtobufCommandOrPlugin> for CommandOrPlugin { + type Error = &'static str; + fn try_from(protobuf: ProtobufCommandOrPlugin) -> Result<Self, Self::Error> { + use super::generated_api::api::action::command_or_plugin::CommandOrPluginType; + match protobuf.command_or_plugin_type { + Some(CommandOrPluginType::Command(cmd)) => { + Ok(CommandOrPlugin::Command(cmd.try_into()?)) + }, + Some(CommandOrPluginType::Plugin(plugin)) => { + Ok(CommandOrPlugin::Plugin(plugin.try_into()?)) + }, + None => Err("CommandOrPlugin must have command_or_plugin_type"), + } + } +} + +impl TryFrom<CommandOrPlugin> for ProtobufCommandOrPlugin { + type Error = &'static str; + fn try_from(internal: CommandOrPlugin) -> Result<Self, Self::Error> { + use super::generated_api::api::action::command_or_plugin::CommandOrPluginType; + let command_or_plugin_type = match internal { + CommandOrPlugin::Command(cmd) => Some(CommandOrPluginType::Command(cmd.try_into()?)), + CommandOrPlugin::Plugin(plugin) => { + Some(CommandOrPluginType::Plugin(plugin.try_into()?)) + }, + }; + Ok(ProtobufCommandOrPlugin { + command_or_plugin_type, + }) + } +} + +impl TryFrom<ProtobufTiledPaneLayout> for TiledPaneLayout { + type Error = &'static str; + fn try_from(protobuf: ProtobufTiledPaneLayout) -> Result<Self, Self::Error> { + let children_split_direction = + ProtobufSplitDirection::from_i32(protobuf.children_split_direction) + .ok_or("Invalid split direction")? + .try_into()?; + let children = protobuf + .children + .into_iter() + .map(|c| c.try_into()) + .collect::<Result<Vec<_>, _>>()?; + let split_size = protobuf.split_size.map(|s| s.try_into()).transpose()?; + let run = protobuf.run.map(|r| r.try_into()).transpose()?; + let focus = protobuf.focus.and_then(|f| { + if f == "true" { + Some(true) + } else if f == "false" { + Some(false) + } else { + None + } + }); + let run_instructions_to_ignore = vec![]; // Not serialized in protobuf + Ok(TiledPaneLayout { + children_split_direction, + name: protobuf.name, + children, + split_size, + run, + borderless: protobuf.borderless, + focus, + external_children_index: protobuf.external_children_index.map(|i| i as usize), + children_are_stacked: protobuf.children_are_stacked, + is_expanded_in_stack: protobuf.is_expanded_in_stack, + exclude_from_sync: protobuf.exclude_from_sync, + run_instructions_to_ignore, + hide_floating_panes: protobuf.hide_floating_panes, + pane_initial_contents: protobuf.pane_initial_contents, + }) + } +} + +impl TryFrom<TiledPaneLayout> for ProtobufTiledPaneLayout { + type Error = &'static str; + fn try_from(internal: TiledPaneLayout) -> Result<Self, Self::Error> { + let children_split_direction: ProtobufSplitDirection = + internal.children_split_direction.try_into()?; + let children = internal + .children + .into_iter() + .map(|c| c.try_into()) + .collect::<Result<Vec<_>, _>>()?; + let split_size = internal.split_size.map(|s| s.try_into()).transpose()?; + let run = internal.run.map(|r| r.try_into()).transpose()?; + let focus = internal.focus.map(|f| f.to_string()); + Ok(ProtobufTiledPaneLayout { + children_split_direction: children_split_direction as i32, + name: internal.name, + children, + split_size, + run, + borderless: internal.borderless, + focus, + external_children_index: internal.external_children_index.map(|i| i as u32), + children_are_stacked: internal.children_are_stacked, + is_expanded_in_stack: internal.is_expanded_in_stack, + exclude_from_sync: internal.exclude_from_sync, + hide_floating_panes: internal.hide_floating_panes, + pane_initial_contents: internal.pane_initial_contents, + }) + } +} + +impl TryFrom<ProtobufFloatingPaneLayout> for FloatingPaneLayout { + type Error = &'static str; + fn try_from(protobuf: ProtobufFloatingPaneLayout) -> Result<Self, Self::Error> { + let height = protobuf.height.map(|h| h.try_into()).transpose()?; + let width = protobuf.width.map(|w| w.try_into()).transpose()?; + let x = protobuf.x.map(|x| x.try_into()).transpose()?; + let y = protobuf.y.map(|y| y.try_into()).transpose()?; + let run = protobuf.run.map(|r| r.try_into()).transpose()?; + Ok(FloatingPaneLayout { + name: protobuf.name, + height, + width, + x, + y, + pinned: protobuf.pinned, + run, + focus: protobuf.focus, + already_running: protobuf.already_running, + pane_initial_contents: protobuf.pane_initial_contents, + logical_position: protobuf.logical_position.map(|p| p as usize), + }) + } +} + +impl TryFrom<FloatingPaneLayout> for ProtobufFloatingPaneLayout { + type Error = &'static str; + fn try_from(internal: FloatingPaneLayout) -> Result<Self, Self::Error> { + let height = internal.height.map(|h| h.try_into()).transpose()?; + let width = internal.width.map(|w| w.try_into()).transpose()?; + let x = internal.x.map(|x| x.try_into()).transpose()?; + let y = internal.y.map(|y| y.try_into()).transpose()?; + let run = internal.run.map(|r| r.try_into()).transpose()?; + Ok(ProtobufFloatingPaneLayout { + name: internal.name, + height, + width, + x, + y, + pinned: internal.pinned, + run, + focus: internal.focus, + already_running: internal.already_running, + pane_initial_contents: internal.pane_initial_contents, + logical_position: internal.logical_position.map(|p| p as u32), + }) + } +} + +impl TryFrom<ProtobufLayoutConstraintTiledPair> for (LayoutConstraint, TiledPaneLayout) { + type Error = &'static str; + fn try_from(protobuf: ProtobufLayoutConstraintTiledPair) -> Result<Self, Self::Error> { + let constraint = protobuf + .constraint + .ok_or("LayoutConstraintTiledPair must have constraint")? + .try_into()?; + let layout = protobuf + .layout + .ok_or("LayoutConstraintTiledPair must have layout")? + .try_into()?; + Ok((constraint, layout)) + } +} + +impl TryFrom<(LayoutConstraint, TiledPaneLayout)> for ProtobufLayoutConstraintTiledPair { + type Error = &'static str; + fn try_from(internal: (LayoutConstraint, TiledPaneLayout)) -> Result<Self, Self::Error> { + Ok(ProtobufLayoutConstraintTiledPair { + constraint: Some(internal.0.try_into()?), + layout: Some(internal.1.try_into()?), + }) + } +} + +impl TryFrom<ProtobufLayoutConstraintFloatingPair> for (LayoutConstraint, Vec<FloatingPaneLayout>) { + type Error = &'static str; + fn try_from(protobuf: ProtobufLayoutConstraintFloatingPair) -> Result<Self, Self::Error> { + let constraint = protobuf + .constraint + .ok_or("LayoutConstraintFloatingPair must have constraint")? + .try_into()?; + let layouts = protobuf + .layouts + .into_iter() + .map(|l| l.try_into()) + .collect::<Result<Vec<_>, _>>()?; + Ok((constraint, layouts)) + } +} + +impl TryFrom<(LayoutConstraint, Vec<FloatingPaneLayout>)> for ProtobufLayoutConstraintFloatingPair { + type Error = &'static str; + fn try_from( + internal: (LayoutConstraint, Vec<FloatingPaneLayout>), + ) -> Result<Self, Self::Error> { + Ok(ProtobufLayoutConstraintFloatingPair { + constraint: Some(internal.0.try_into()?), + layouts: internal + .1 + .into_iter() + .map(|l| l.try_into()) + .collect::<Result<Vec<_>, _>>()?, + }) + } +} + +impl TryFrom<ProtobufSwapTiledLayout> for SwapTiledLayout { + type Error = &'static str; + fn try_from(protobuf: ProtobufSwapTiledLayout) -> Result<Self, Self::Error> { + let constraint_map: BTreeMap<LayoutConstraint, TiledPaneLayout> = protobuf + .constraint_map + .into_iter() + .map(|pair| pair.try_into()) + .collect::<Result<BTreeMap<_, _>, _>>()?; + Ok((constraint_map, protobuf.name)) + } +} + +impl TryFrom<SwapTiledLayout> for ProtobufSwapTiledLayout { + type Error = &'static str; + fn try_from(internal: SwapTiledLayout) -> Result<Self, Self::Error> { + let constraint_map = internal + .0 + .into_iter() + .map(|(constraint, layout)| (constraint, layout).try_into()) + .collect::<Result<Vec<_>, _>>()?; + Ok(ProtobufSwapTiledLayout { + constraint_map, + name: internal.1, + }) + } +} + +impl TryFrom<ProtobufSwapFloatingLayout> for SwapFloatingLayout { + type Error = &'static str; + fn try_from(protobuf: ProtobufSwapFloatingLayout) -> Result<Self, Self::Error> { + let constraint_map: BTreeMap<LayoutConstraint, Vec<FloatingPaneLayout>> = protobuf + .constraint_map + .into_iter() + .map(|pair| pair.try_into()) + .collect::<Result<BTreeMap<_, _>, _>>()?; + Ok((constraint_map, protobuf.name)) + } +} + +impl TryFrom<SwapFloatingLayout> for ProtobufSwapFloatingLayout { + type Error = &'static str; + fn try_from(internal: SwapFloatingLayout) -> Result<Self, Self::Error> { + let constraint_map = internal + .0 + .into_iter() + .map(|(constraint, layouts)| (constraint, layouts).try_into()) + .collect::<Result<Vec<_>, _>>()?; + Ok(ProtobufSwapFloatingLayout { + constraint_map, + name: internal.1, + }) + } +} |
