feat(plugins): allow changing the plugin's /host folder (under a new permission) (#3827)

* working without notifying plugins

* permissions and events

* cleanups and formatting

* style(fmt): rustfmt
This commit is contained in:
Aram Drevekenin 2024-12-01 11:06:44 +01:00 committed by GitHub
parent 90ecd8fb4b
commit 0c21eae664
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 301 additions and 37 deletions

View file

@ -158,6 +158,7 @@ pub enum PluginInstruction {
},
WatchFilesystem,
ListClientsToPlugin(SessionLayoutMetadata, PluginId, ClientId),
ChangePluginHostDir(PathBuf, PluginId, ClientId),
Exit,
}
@ -204,6 +205,7 @@ impl From<&PluginInstruction> for PluginContext {
PluginContext::FailedToWriteConfigToDisk
},
PluginInstruction::ListClientsToPlugin(..) => PluginContext::ListClientsToPlugin,
PluginInstruction::ChangePluginHostDir(..) => PluginContext::ChangePluginHostDir,
}
}
}
@ -886,6 +888,11 @@ pub(crate) fn plugin_thread_main(
PluginInstruction::WatchFilesystem => {
wasm_bridge.start_fs_watcher_if_not_started();
},
PluginInstruction::ChangePluginHostDir(new_host_folder, plugin_id, client_id) => {
wasm_bridge
.change_plugin_host_dir(new_host_folder, plugin_id, client_id)
.non_fatal();
},
PluginInstruction::Exit => {
break;
},

View file

@ -14,7 +14,7 @@ use std::{
};
use url::Url;
use wasmtime::{Engine, Instance, Linker, Module, Store};
use wasmtime_wasi::{DirPerms, FilePerms, WasiCtxBuilder};
use wasmtime_wasi::{preview1::WasiP1Ctx, DirPerms, FilePerms, WasiCtxBuilder};
use zellij_utils::consts::ZELLIJ_PLUGIN_ARTIFACT_DIR;
use zellij_utils::prost::Message;
@ -64,7 +64,7 @@ pub struct PluginLoader<'a> {
size: Size,
wasm_blob_on_hd: Option<(Vec<u8>, PathBuf)>,
path_to_default_shell: PathBuf,
zellij_cwd: PathBuf,
plugin_cwd: PathBuf,
capabilities: PluginCapabilities,
client_attributes: ClientAttributes,
default_shell: Option<TerminalAction>,
@ -85,7 +85,7 @@ impl<'a> PluginLoader<'a> {
connected_clients: Arc<Mutex<Vec<ClientId>>>,
loading_indication: &mut LoadingIndication,
path_to_default_shell: PathBuf,
zellij_cwd: PathBuf,
plugin_cwd: PathBuf,
capabilities: PluginCapabilities,
client_attributes: ClientAttributes,
default_shell: Option<TerminalAction>,
@ -110,7 +110,7 @@ impl<'a> PluginLoader<'a> {
engine,
&plugin_dir,
path_to_default_shell,
Some(zellij_cwd),
Some(plugin_cwd),
capabilities,
client_attributes,
default_shell,
@ -145,7 +145,7 @@ impl<'a> PluginLoader<'a> {
connected_clients: Arc<Mutex<Vec<ClientId>>>,
loading_indication: &mut LoadingIndication,
path_to_default_shell: PathBuf,
zellij_cwd: PathBuf,
plugin_cwd: PathBuf,
capabilities: PluginCapabilities,
client_attributes: ClientAttributes,
default_shell: Option<TerminalAction>,
@ -168,7 +168,7 @@ impl<'a> PluginLoader<'a> {
tab_index,
size,
path_to_default_shell,
zellij_cwd,
plugin_cwd,
capabilities,
client_attributes,
default_shell,
@ -222,7 +222,7 @@ impl<'a> PluginLoader<'a> {
connected_clients: Arc<Mutex<Vec<ClientId>>>,
loading_indication: &mut LoadingIndication,
path_to_default_shell: PathBuf,
zellij_cwd: PathBuf,
plugin_cwd: PathBuf,
capabilities: PluginCapabilities,
client_attributes: ClientAttributes,
default_shell: Option<TerminalAction>,
@ -246,7 +246,7 @@ impl<'a> PluginLoader<'a> {
engine.clone(),
&plugin_dir,
path_to_default_shell.clone(),
zellij_cwd.clone(),
plugin_cwd.clone(),
capabilities.clone(),
client_attributes.clone(),
default_shell.clone(),
@ -333,7 +333,7 @@ impl<'a> PluginLoader<'a> {
tab_index: Option<usize>,
size: Size,
path_to_default_shell: PathBuf,
zellij_cwd: PathBuf,
plugin_cwd: PathBuf,
capabilities: PluginCapabilities,
client_attributes: ClientAttributes,
default_shell: Option<TerminalAction>,
@ -366,7 +366,7 @@ impl<'a> PluginLoader<'a> {
size,
wasm_blob_on_hd: None,
path_to_default_shell,
zellij_cwd,
plugin_cwd,
capabilities,
client_attributes,
default_shell,
@ -412,7 +412,7 @@ impl<'a> PluginLoader<'a> {
// prefer the explicitly given cwd, otherwise copy it from the running plugin
// (when reloading a plugin, we want to copy it, when starting a new plugin instance from
// meomory, we want to reset it)
let zellij_cwd = cwd.unwrap_or_else(|| running_plugin.store.data().plugin_cwd.clone());
let plugin_cwd = cwd.unwrap_or_else(|| running_plugin.store.data().plugin_cwd.clone());
loading_indication.set_name(running_plugin.store.data().name());
PluginLoader::new(
plugin_cache,
@ -426,7 +426,7 @@ impl<'a> PluginLoader<'a> {
tab_index,
size,
path_to_default_shell,
zellij_cwd,
plugin_cwd,
capabilities,
client_attributes,
default_shell,
@ -446,7 +446,7 @@ impl<'a> PluginLoader<'a> {
engine: Engine,
plugin_dir: &'a PathBuf,
path_to_default_shell: PathBuf,
zellij_cwd: PathBuf,
plugin_cwd: PathBuf,
capabilities: PluginCapabilities,
client_attributes: ClientAttributes,
default_shell: Option<TerminalAction>,
@ -483,7 +483,7 @@ impl<'a> PluginLoader<'a> {
tab_index,
size,
path_to_default_shell,
zellij_cwd,
plugin_cwd,
capabilities,
client_attributes,
default_shell,
@ -736,7 +736,7 @@ impl<'a> PluginLoader<'a> {
self.engine.clone(),
&self.plugin_dir,
self.path_to_default_shell.clone(),
self.zellij_cwd.clone(),
self.plugin_cwd.clone(),
self.capabilities.clone(),
self.client_attributes.clone(),
self.default_shell.clone(),
@ -784,18 +784,22 @@ impl<'a> PluginLoader<'a> {
},
}
}
fn create_plugin_instance_env(&self, module: &Module) -> Result<(Store<PluginEnv>, Instance)> {
let err_context = || {
format!(
"Failed to create instance, plugin env and subscriptions for plugin {}",
self.plugin_id
)
};
pub fn create_wasi_ctx(
host_dir: &PathBuf,
data_dir: &PathBuf,
cache_dir: &PathBuf,
tmp_dir: &PathBuf,
plugin_url: &String,
plugin_id: PluginId,
stdin_pipe: Arc<Mutex<VecDeque<u8>>>,
stdout_pipe: Arc<Mutex<VecDeque<u8>>>,
) -> Result<WasiP1Ctx> {
let err_context = || format!("Failed to create wasi_ctx");
let dirs = vec![
("/host".to_owned(), self.zellij_cwd.clone()),
("/data".to_owned(), self.plugin_own_data_dir.clone()),
("/cache".to_owned(), self.plugin_own_cache_dir.clone()),
("/tmp".to_owned(), ZELLIJ_TMP_DIR.clone()),
("/host".to_owned(), host_dir.clone()),
("/data".to_owned(), data_dir.clone()),
("/cache".to_owned(), cache_dir.clone()),
("/tmp".to_owned(), tmp_dir.clone()),
];
let dirs = dirs.into_iter().filter(|(_dir_name, dir)| {
// note that this does not protect against TOCTOU errors
@ -812,16 +816,35 @@ impl<'a> PluginLoader<'a> {
.preopened_dir(host_path, guest_path, DirPerms::all(), FilePerms::all())
.with_context(err_context)?;
}
let stdin_pipe = Arc::new(Mutex::new(VecDeque::new()));
let stdout_pipe = Arc::new(Mutex::new(VecDeque::new()));
wasi_ctx_builder
.stdin(VecDequeInputStream(stdin_pipe.clone()))
.stdout(WriteOutputStream(stdout_pipe.clone()))
.stderr(WriteOutputStream(Arc::new(Mutex::new(LoggingPipe::new(
&self.plugin.location.to_string(),
self.plugin_id,
plugin_url, plugin_id,
)))));
let wasi_ctx = wasi_ctx_builder.build_p1();
Ok(wasi_ctx)
}
fn create_plugin_instance_env(&self, module: &Module) -> Result<(Store<PluginEnv>, Instance)> {
let err_context = || {
format!(
"Failed to create instance, plugin env and subscriptions for plugin {}",
self.plugin_id
)
};
let stdin_pipe = Arc::new(Mutex::new(VecDeque::new()));
let stdout_pipe = Arc::new(Mutex::new(VecDeque::new()));
let wasi_ctx = PluginLoader::create_wasi_ctx(
&self.plugin_cwd,
&self.plugin_own_data_dir,
&self.plugin_own_cache_dir,
&ZELLIJ_TMP_DIR,
&self.plugin.location.to_string(),
self.plugin_id,
stdin_pipe.clone(),
stdout_pipe.clone(),
)?;
let plugin = self.plugin.clone();
let plugin_env = PluginEnv {
plugin_id: self.plugin_id,
@ -838,7 +861,7 @@ impl<'a> PluginLoader<'a> {
client_attributes: self.client_attributes.clone(),
default_shell: self.default_shell.clone(),
default_layout: self.default_layout.clone(),
plugin_cwd: self.zellij_cwd.clone(),
plugin_cwd: self.plugin_cwd.clone(),
input_pipes_to_unblock: Arc::new(Mutex::new(HashSet::new())),
input_pipes_to_block: Arc::new(Mutex::new(HashSet::new())),
layout_dir: self.layout_dir.clone(),

View file

@ -4,6 +4,7 @@ use crate::plugins::pipes::{
};
use crate::plugins::plugin_loader::PluginLoader;
use crate::plugins::plugin_map::{AtomicEvent, PluginEnv, PluginMap, RunningPlugin, Subscriptions};
use crate::plugins::plugin_worker::MessageToWorker;
use crate::plugins::watch_filesystem::watch_filesystem;
use crate::plugins::zellij_exports::{wasi_read_string, wasi_write_object};
@ -18,7 +19,7 @@ use std::{
use wasmtime::{Engine, Module};
use zellij_utils::async_channel::Sender;
use zellij_utils::async_std::task::{self, JoinHandle};
use zellij_utils::consts::ZELLIJ_CACHE_DIR;
use zellij_utils::consts::{ZELLIJ_CACHE_DIR, ZELLIJ_TMP_DIR};
use zellij_utils::data::{InputMode, PermissionStatus, PermissionType, PipeMessage, PipeSource};
use zellij_utils::downloader::Downloader;
use zellij_utils::input::keybinds::Keybinds;
@ -736,6 +737,107 @@ impl WasmBridge {
}
Ok(())
}
pub fn change_plugin_host_dir(
&mut self,
new_host_dir: PathBuf,
plugin_id_to_update: PluginId,
client_id_to_update: ClientId,
) -> Result<()> {
let plugins_to_change: Vec<(
PluginId,
ClientId,
Arc<Mutex<RunningPlugin>>,
Arc<Mutex<Subscriptions>>,
)> = self
.plugin_map
.lock()
.unwrap()
.running_plugins_and_subscriptions()
.iter()
.cloned()
.filter(|(plugin_id, _client_id, _running_plugin, _subscriptions)| {
// TODO: cache this somehow in this case...
!&self
.cached_events_for_pending_plugins
.contains_key(&plugin_id)
})
.collect();
task::spawn({
let senders = self.senders.clone();
async move {
match new_host_dir.try_exists() {
Ok(false) => {
log::error!(
"Failed to change folder to {},: folder does not exist",
new_host_dir.display()
);
let _ = senders.send_to_plugin(PluginInstruction::Update(vec![(
Some(plugin_id_to_update),
Some(client_id_to_update),
Event::FailedToChangeHostFolder(Some(format!(
"Folder {} does not exist",
new_host_dir.display()
))),
)]));
return;
},
Err(e) => {
log::error!(
"Failed to change folder to {},: {}",
new_host_dir.display(),
e
);
let _ = senders.send_to_plugin(PluginInstruction::Update(vec![(
Some(plugin_id_to_update),
Some(client_id_to_update),
Event::FailedToChangeHostFolder(Some(e.to_string())),
)]));
return;
},
_ => {},
}
for (plugin_id, client_id, running_plugin, _subscriptions) in &plugins_to_change {
if plugin_id == &plugin_id_to_update && client_id == &client_id_to_update {
let mut running_plugin = running_plugin.lock().unwrap();
let plugin_env = running_plugin.store.data_mut();
let stdin_pipe = plugin_env.stdin_pipe.clone();
let stdout_pipe = plugin_env.stdout_pipe.clone();
let wasi_ctx = PluginLoader::create_wasi_ctx(
&new_host_dir,
&plugin_env.plugin_own_data_dir,
&plugin_env.plugin_own_cache_dir,
&ZELLIJ_TMP_DIR,
&plugin_env.plugin.location.to_string(),
plugin_env.plugin_id,
stdin_pipe.clone(),
stdout_pipe.clone(),
);
match wasi_ctx {
Ok(wasi_ctx) => {
drop(std::mem::replace(&mut plugin_env.wasi_ctx, wasi_ctx));
plugin_env.plugin_cwd = new_host_dir.clone();
let _ = senders.send_to_plugin(PluginInstruction::Update(vec![(
Some(*plugin_id),
Some(*client_id),
Event::HostFolderChanged(new_host_dir.clone()),
)]));
},
Err(e) => {
let _ = senders.send_to_plugin(PluginInstruction::Update(vec![(
Some(*plugin_id),
Some(*client_id),
Event::FailedToChangeHostFolder(Some(e.to_string())),
)]));
log::error!("Failed to create wasi ctx: {}", e);
},
}
}
}
}
});
Ok(())
}
pub fn pipe_messages(
&mut self,
mut messages: Vec<(Option<PluginId>, Option<ClientId>, PipeMessage)>,

View file

@ -352,6 +352,9 @@ fn host_run_plugin_command(caller: Caller<'_, PluginEnv>) {
write_config_to_disk,
} => rebind_keys(env, keys_to_rebind, keys_to_unbind, write_config_to_disk)?,
PluginCommand::ListClients => list_clients(env),
PluginCommand::ChangeHostFolder(new_host_folder) => {
change_host_folder(env, new_host_folder)
},
},
(PermissionStatus::Denied, permission) => {
log::error!(
@ -1484,6 +1487,16 @@ fn list_clients(env: &PluginEnv) {
});
}
fn change_host_folder(env: &PluginEnv, new_host_folder: PathBuf) {
let _ = env.senders.to_plugin.as_ref().map(|sender| {
sender.send(PluginInstruction::ChangePluginHostDir(
new_host_folder,
env.plugin_id,
env.client_id,
))
});
}
fn scan_host_folder(env: &PluginEnv, folder_to_scan: PathBuf) {
if !folder_to_scan.starts_with("/host") {
log::error!(
@ -1913,6 +1926,7 @@ fn check_command_permission(
PluginCommand::RebindKeys { .. } | PluginCommand::Reconfigure(..) => {
PermissionType::Reconfigure
},
PluginCommand::ChangeHostFolder(..) => PermissionType::FullHdAccess,
_ => return (PermissionStatus::Granted, None),
};

View file

@ -1128,6 +1128,13 @@ pub fn rebind_keys(
unsafe { host_run_plugin_command() };
}
pub fn change_host_folder(new_host_folder: PathBuf) {
let plugin_command = PluginCommand::ChangeHostFolder(new_host_folder);
let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
object_to_stdout(&protobuf_plugin_command.encode_to_vec());
unsafe { host_run_plugin_command() };
}
// Utility Functions
#[allow(unused)]

View file

@ -11,7 +11,7 @@ pub struct Event {
pub name: i32,
#[prost(
oneof = "event::Payload",
tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23"
tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25"
)]
pub payload: ::core::option::Option<event::Payload>,
}
@ -64,10 +64,26 @@ pub mod event {
FailedToWriteConfigToDiskPayload(super::FailedToWriteConfigToDiskPayload),
#[prost(message, tag = "23")]
ListClientsPayload(super::ListClientsPayload),
#[prost(message, tag = "24")]
HostFolderChangedPayload(super::HostFolderChangedPayload),
#[prost(message, tag = "25")]
FailedToChangeHostFolderPayload(super::FailedToChangeHostFolderPayload),
}
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct FailedToChangeHostFolderPayload {
#[prost(string, optional, tag = "1")]
pub error_message: ::core::option::Option<::prost::alloc::string::String>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct HostFolderChangedPayload {
#[prost(string, tag = "1")]
pub new_host_folder_path: ::prost::alloc::string::String,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ListClientsPayload {
#[prost(message, repeated, tag = "1")]
pub client_info: ::prost::alloc::vec::Vec<ClientInfo>,
@ -468,6 +484,8 @@ pub enum EventType {
CommandPaneReRun = 24,
FailedToWriteConfigToDisk = 25,
ListClients = 26,
HostFolderChanged = 27,
FailedToChangeHostFolder = 28,
}
impl EventType {
/// String value of the enum field names used in the ProtoBuf definition.
@ -503,6 +521,8 @@ impl EventType {
EventType::CommandPaneReRun => "CommandPaneReRun",
EventType::FailedToWriteConfigToDisk => "FailedToWriteConfigToDisk",
EventType::ListClients => "ListClients",
EventType::HostFolderChanged => "HostFolderChanged",
EventType::FailedToChangeHostFolder => "FailedToChangeHostFolder",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
@ -535,6 +555,8 @@ impl EventType {
"CommandPaneReRun" => Some(Self::CommandPaneReRun),
"FailedToWriteConfigToDisk" => Some(Self::FailedToWriteConfigToDisk),
"ListClients" => Some(Self::ListClients),
"HostFolderChanged" => Some(Self::HostFolderChanged),
"FailedToChangeHostFolder" => Some(Self::FailedToChangeHostFolder),
_ => None,
}
}

View file

@ -5,7 +5,7 @@ pub struct PluginCommand {
pub name: i32,
#[prost(
oneof = "plugin_command::Payload",
tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88"
tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89"
)]
pub payload: ::core::option::Option<plugin_command::Payload>,
}
@ -174,10 +174,18 @@ pub mod plugin_command {
LoadNewPluginPayload(super::LoadNewPluginPayload),
#[prost(message, tag = "88")]
RebindKeysPayload(super::RebindKeysPayload),
#[prost(message, tag = "89")]
ChangeHostFolderPayload(super::ChangeHostFolderPayload),
}
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ChangeHostFolderPayload {
#[prost(string, tag = "1")]
pub new_host_folder: ::prost::alloc::string::String,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct RebindKeysPayload {
#[prost(message, repeated, tag = "1")]
pub keys_to_rebind: ::prost::alloc::vec::Vec<KeyToRebind>,
@ -716,6 +724,7 @@ pub enum CommandName {
LoadNewPlugin = 111,
RebindKeys = 112,
ListClients = 113,
ChangeHostFolder = 114,
}
impl CommandName {
/// String value of the enum field names used in the ProtoBuf definition.
@ -840,6 +849,7 @@ impl CommandName {
CommandName::LoadNewPlugin => "LoadNewPlugin",
CommandName::RebindKeys => "RebindKeys",
CommandName::ListClients => "ListClients",
CommandName::ChangeHostFolder => "ChangeHostFolder",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
@ -961,6 +971,7 @@ impl CommandName {
"LoadNewPlugin" => Some(Self::LoadNewPlugin),
"RebindKeys" => Some(Self::RebindKeys),
"ListClients" => Some(Self::ListClients),
"ChangeHostFolder" => Some(Self::ChangeHostFolder),
_ => None,
}
}

View file

@ -11,6 +11,7 @@ pub enum PermissionType {
ReadCliPipes = 7,
MessageAndLaunchOtherPlugins = 8,
Reconfigure = 9,
FullHdAccess = 10,
}
impl PermissionType {
/// String value of the enum field names used in the ProtoBuf definition.
@ -31,6 +32,7 @@ impl PermissionType {
"MessageAndLaunchOtherPlugins"
}
PermissionType::Reconfigure => "Reconfigure",
PermissionType::FullHdAccess => "FullHdAccess",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
@ -46,6 +48,7 @@ impl PermissionType {
"ReadCliPipes" => Some(Self::ReadCliPipes),
"MessageAndLaunchOtherPlugins" => Some(Self::MessageAndLaunchOtherPlugins),
"Reconfigure" => Some(Self::Reconfigure),
"FullHdAccess" => Some(Self::FullHdAccess),
_ => None,
}
}

View file

@ -912,6 +912,8 @@ pub enum Event {
CommandPaneReRun(u32, Context), // u32 - terminal_pane_id, Option<i32> -
FailedToWriteConfigToDisk(Option<String>), // String -> the file path we failed to write
ListClients(Vec<ClientInfo>),
HostFolderChanged(PathBuf), // PathBuf -> new host folder
FailedToChangeHostFolder(Option<String>), // String -> the error we got when changing
}
#[derive(
@ -942,6 +944,7 @@ pub enum Permission {
ReadCliPipes,
MessageAndLaunchOtherPlugins,
Reconfigure,
FullHdAccess,
}
impl PermissionType {
@ -963,6 +966,7 @@ impl PermissionType {
"Send messages to and launch other plugins".to_owned()
},
PermissionType::Reconfigure => "Change Zellij runtime configuration".to_owned(),
PermissionType::FullHdAccess => "Full access to the hard-drive".to_owned(),
}
}
}
@ -1893,4 +1897,5 @@ pub enum PluginCommand {
write_config_to_disk: bool,
},
ListClients,
ChangeHostFolder(PathBuf),
}

View file

@ -435,6 +435,7 @@ pub enum PluginContext {
Reconfigure,
FailedToWriteConfigToDisk,
ListClientsToPlugin,
ChangePluginHostDir,
}
/// Stack call representations corresponding to the different types of [`ClientInstruction`]s.

View file

@ -50,6 +50,8 @@ enum EventType {
CommandPaneReRun = 24;
FailedToWriteConfigToDisk = 25;
ListClients = 26;
HostFolderChanged = 27;
FailedToChangeHostFolder = 28;
}
message EventNameList {
@ -81,9 +83,19 @@ message Event {
CommandPaneReRunPayload command_pane_rerun_payload = 21;
FailedToWriteConfigToDiskPayload failed_to_write_config_to_disk_payload = 22;
ListClientsPayload list_clients_payload = 23;
HostFolderChangedPayload host_folder_changed_payload = 24;
FailedToChangeHostFolderPayload failed_to_change_host_folder_payload = 25;
}
}
message FailedToChangeHostFolderPayload {
optional string error_message = 1;
}
message HostFolderChangedPayload {
string new_host_folder_path = 1;
}
message ListClientsPayload {
repeated ClientInfo client_info = 1;
}

View file

@ -333,6 +333,22 @@ impl TryFrom<ProtobufEvent> for Event {
},
_ => Err("Malformed payload for the FailedToWriteConfigToDisk Event"),
},
Some(ProtobufEventType::HostFolderChanged) => match protobuf_event.payload {
Some(ProtobufEventPayload::HostFolderChangedPayload(
host_folder_changed_payload,
)) => Ok(Event::HostFolderChanged(PathBuf::from(
host_folder_changed_payload.new_host_folder_path,
))),
_ => Err("Malformed payload for the HostFolderChanged Event"),
},
Some(ProtobufEventType::FailedToChangeHostFolder) => match protobuf_event.payload {
Some(ProtobufEventPayload::FailedToChangeHostFolderPayload(
failed_to_change_host_folder_payload,
)) => Ok(Event::FailedToChangeHostFolder(
failed_to_change_host_folder_payload.error_message,
)),
_ => Err("Malformed payload for the FailedToChangeHostFolder Event"),
},
None => Err("Unknown Protobuf Event"),
}
}
@ -683,6 +699,20 @@ impl TryFrom<Event> for ProtobufEvent {
.collect(),
})),
}),
Event::HostFolderChanged(new_host_folder_path) => Ok(ProtobufEvent {
name: ProtobufEventType::HostFolderChanged as i32,
payload: Some(event::Payload::HostFolderChangedPayload(
HostFolderChangedPayload {
new_host_folder_path: new_host_folder_path.display().to_string(),
},
)),
}),
Event::FailedToChangeHostFolder(error_message) => Ok(ProtobufEvent {
name: ProtobufEventType::FailedToChangeHostFolder as i32,
payload: Some(event::Payload::FailedToChangeHostFolderPayload(
FailedToChangeHostFolderPayload { error_message },
)),
}),
}
}
}
@ -1228,6 +1258,8 @@ impl TryFrom<ProtobufEventType> for EventType {
ProtobufEventType::CommandPaneReRun => EventType::CommandPaneReRun,
ProtobufEventType::FailedToWriteConfigToDisk => EventType::FailedToWriteConfigToDisk,
ProtobufEventType::ListClients => EventType::ListClients,
ProtobufEventType::HostFolderChanged => EventType::HostFolderChanged,
ProtobufEventType::FailedToChangeHostFolder => EventType::FailedToChangeHostFolder,
})
}
}
@ -1263,6 +1295,8 @@ impl TryFrom<EventType> for ProtobufEventType {
EventType::CommandPaneReRun => ProtobufEventType::CommandPaneReRun,
EventType::FailedToWriteConfigToDisk => ProtobufEventType::FailedToWriteConfigToDisk,
EventType::ListClients => ProtobufEventType::ListClients,
EventType::HostFolderChanged => ProtobufEventType::HostFolderChanged,
EventType::FailedToChangeHostFolder => ProtobufEventType::FailedToChangeHostFolder,
})
}
}

View file

@ -127,6 +127,7 @@ enum CommandName {
LoadNewPlugin = 111;
RebindKeys = 112;
ListClients = 113;
ChangeHostFolder = 114;
}
message PluginCommand {
@ -210,9 +211,14 @@ message PluginCommand {
ReloadPluginPayload reload_plugin_payload = 86;
LoadNewPluginPayload load_new_plugin_payload = 87;
RebindKeysPayload rebind_keys_payload = 88;
ChangeHostFolderPayload change_host_folder_payload = 89;
}
}
message ChangeHostFolderPayload {
string new_host_folder = 1;
}
message RebindKeysPayload {
repeated KeyToRebind keys_to_rebind = 1;
repeated KeyToUnbind keys_to_unbind = 2;

View file

@ -4,9 +4,9 @@ pub use super::generated_api::api::{
input_mode::InputMode as ProtobufInputMode,
plugin_command::{
plugin_command::Payload, BreakPanesToNewTabPayload, BreakPanesToTabWithIndexPayload,
ClearScreenForPaneIdPayload, CliPipeOutputPayload, CloseTabWithIndexPayload, CommandName,
ContextItem, EditScrollbackForPaneWithIdPayload, EnvVariable, ExecCmdPayload,
FixedOrPercent as ProtobufFixedOrPercent,
ChangeHostFolderPayload, ClearScreenForPaneIdPayload, CliPipeOutputPayload,
CloseTabWithIndexPayload, CommandName, ContextItem, EditScrollbackForPaneWithIdPayload,
EnvVariable, ExecCmdPayload, FixedOrPercent as ProtobufFixedOrPercent,
FixedOrPercentValue as ProtobufFixedOrPercentValue,
FloatingPaneCoordinates as ProtobufFloatingPaneCoordinates, HidePaneWithIdPayload,
HttpVerb as ProtobufHttpVerb, IdAndNewName, KeyToRebind, KeyToUnbind, KillSessionsPayload,
@ -1303,6 +1303,14 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand {
Some(_) => Err("ListClients should have no payload, found a payload"),
None => Ok(PluginCommand::ListClients),
},
Some(CommandName::ChangeHostFolder) => match protobuf_plugin_command.payload {
Some(Payload::ChangeHostFolderPayload(change_host_folder_payload)) => {
Ok(PluginCommand::ChangeHostFolder(PathBuf::from(
change_host_folder_payload.new_host_folder,
)))
},
_ => Err("Mismatched payload for ChangeHostFolder"),
},
None => Err("Unrecognized plugin command"),
}
}
@ -2130,6 +2138,12 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand {
name: CommandName::ListClients as i32,
payload: None,
}),
PluginCommand::ChangeHostFolder(new_host_folder) => Ok(ProtobufPluginCommand {
name: CommandName::ChangeHostFolder as i32,
payload: Some(Payload::ChangeHostFolderPayload(ChangeHostFolderPayload {
new_host_folder: new_host_folder.display().to_string(), // TODO: not accurate?
})),
}),
}
}
}

View file

@ -13,4 +13,5 @@ enum PermissionType {
ReadCliPipes = 7;
MessageAndLaunchOtherPlugins = 8;
Reconfigure = 9;
FullHdAccess = 10;
}

View file

@ -25,6 +25,7 @@ impl TryFrom<ProtobufPermissionType> for PermissionType {
Ok(PermissionType::MessageAndLaunchOtherPlugins)
},
ProtobufPermissionType::Reconfigure => Ok(PermissionType::Reconfigure),
ProtobufPermissionType::FullHdAccess => Ok(PermissionType::FullHdAccess),
}
}
}
@ -51,6 +52,7 @@ impl TryFrom<PermissionType> for ProtobufPermissionType {
Ok(ProtobufPermissionType::MessageAndLaunchOtherPlugins)
},
PermissionType::Reconfigure => Ok(ProtobufPermissionType::Reconfigure),
PermissionType::FullHdAccess => Ok(ProtobufPermissionType::FullHdAccess),
}
}
}