feat(plugins): plugin run_command api (#2862)
* prototype * add tests * style(fmt): rustfmt * update comments * deprecation warning for execcmd
This commit is contained in:
parent
36237f0414
commit
d2b6fb5c5b
16 changed files with 620 additions and 9 deletions
|
|
@ -240,6 +240,23 @@ impl ZellijPlugin for State {
|
||||||
Key::Ctrl('1') => {
|
Key::Ctrl('1') => {
|
||||||
request_permission(&[PermissionType::ReadApplicationState]);
|
request_permission(&[PermissionType::ReadApplicationState]);
|
||||||
},
|
},
|
||||||
|
Key::Ctrl('2') => {
|
||||||
|
let mut context = BTreeMap::new();
|
||||||
|
context.insert("user_key_1".to_owned(), "user_value_1".to_owned());
|
||||||
|
run_command(&["ls", "-l"], context);
|
||||||
|
},
|
||||||
|
Key::Ctrl('3') => {
|
||||||
|
let mut context = BTreeMap::new();
|
||||||
|
context.insert("user_key_2".to_owned(), "user_value_2".to_owned());
|
||||||
|
let mut env_vars = BTreeMap::new();
|
||||||
|
env_vars.insert("VAR1".to_owned(), "some_value".to_owned());
|
||||||
|
run_command_with_env_variables_and_cwd(
|
||||||
|
&["ls", "-l"],
|
||||||
|
env_vars,
|
||||||
|
std::path::PathBuf::from("/some/custom/folder"),
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
},
|
},
|
||||||
Event::CustomMessage(message, payload) => {
|
Event::CustomMessage(message, payload) => {
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ impl ZellijPlugin for State {
|
||||||
EventType::ModeUpdate,
|
EventType::ModeUpdate,
|
||||||
EventType::SessionUpdate,
|
EventType::SessionUpdate,
|
||||||
EventType::Key,
|
EventType::Key,
|
||||||
|
EventType::RunCommandResult,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,14 @@ use zellij_utils::consts::{
|
||||||
session_info_cache_file_name, session_info_folder_for_session, session_layout_cache_file_name,
|
session_info_cache_file_name, session_info_folder_for_session, session_layout_cache_file_name,
|
||||||
ZELLIJ_SOCK_DIR,
|
ZELLIJ_SOCK_DIR,
|
||||||
};
|
};
|
||||||
use zellij_utils::data::SessionInfo;
|
use zellij_utils::data::{Event, SessionInfo};
|
||||||
use zellij_utils::errors::{prelude::*, BackgroundJobContext, ContextType};
|
use zellij_utils::errors::{prelude::*, BackgroundJobContext, ContextType};
|
||||||
|
|
||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::{BTreeMap, HashMap};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::os::unix::fs::FileTypeExt;
|
use std::os::unix::fs::FileTypeExt;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::sync::{
|
use std::sync::{
|
||||||
atomic::{AtomicBool, Ordering},
|
atomic::{AtomicBool, Ordering},
|
||||||
Arc, Mutex,
|
Arc, Mutex,
|
||||||
|
|
@ -17,8 +18,10 @@ use std::sync::{
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use crate::panes::PaneId;
|
use crate::panes::PaneId;
|
||||||
|
use crate::plugins::{PluginId, PluginInstruction};
|
||||||
use crate::screen::ScreenInstruction;
|
use crate::screen::ScreenInstruction;
|
||||||
use crate::thread_bus::Bus;
|
use crate::thread_bus::Bus;
|
||||||
|
use crate::ClientId;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
pub enum BackgroundJob {
|
pub enum BackgroundJob {
|
||||||
|
|
@ -28,6 +31,15 @@ pub enum BackgroundJob {
|
||||||
ReadAllSessionInfosOnMachine, // u32 - plugin_id
|
ReadAllSessionInfosOnMachine, // u32 - plugin_id
|
||||||
ReportSessionInfo(String, SessionInfo), // String - session name
|
ReportSessionInfo(String, SessionInfo), // String - session name
|
||||||
ReportLayoutInfo((String, BTreeMap<String, String>)), // HashMap<file_name, pane_contents>
|
ReportLayoutInfo((String, BTreeMap<String, String>)), // HashMap<file_name, pane_contents>
|
||||||
|
RunCommand(
|
||||||
|
PluginId,
|
||||||
|
ClientId,
|
||||||
|
String,
|
||||||
|
Vec<String>,
|
||||||
|
BTreeMap<String, String>,
|
||||||
|
PathBuf,
|
||||||
|
BTreeMap<String, String>,
|
||||||
|
), // command, args, env_variables, cwd, context
|
||||||
Exit,
|
Exit,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -44,6 +56,7 @@ impl From<&BackgroundJob> for BackgroundJobContext {
|
||||||
},
|
},
|
||||||
BackgroundJob::ReportSessionInfo(..) => BackgroundJobContext::ReportSessionInfo,
|
BackgroundJob::ReportSessionInfo(..) => BackgroundJobContext::ReportSessionInfo,
|
||||||
BackgroundJob::ReportLayoutInfo(..) => BackgroundJobContext::ReportLayoutInfo,
|
BackgroundJob::ReportLayoutInfo(..) => BackgroundJobContext::ReportLayoutInfo,
|
||||||
|
BackgroundJob::RunCommand(..) => BackgroundJobContext::RunCommand,
|
||||||
BackgroundJob::Exit => BackgroundJobContext::Exit,
|
BackgroundJob::Exit => BackgroundJobContext::Exit,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -226,6 +239,52 @@ pub(crate) fn background_jobs_main(bus: Bus<BackgroundJob>) -> Result<()> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
BackgroundJob::RunCommand(
|
||||||
|
plugin_id,
|
||||||
|
client_id,
|
||||||
|
command,
|
||||||
|
args,
|
||||||
|
env_variables,
|
||||||
|
cwd,
|
||||||
|
context,
|
||||||
|
) => {
|
||||||
|
// when async_std::process stabilizes, we should change this to be async
|
||||||
|
std::thread::spawn({
|
||||||
|
let senders = bus.senders.clone();
|
||||||
|
move || {
|
||||||
|
let output = std::process::Command::new(&command)
|
||||||
|
.args(&args)
|
||||||
|
.envs(env_variables)
|
||||||
|
.current_dir(cwd)
|
||||||
|
.stdout(std::process::Stdio::piped())
|
||||||
|
.stderr(std::process::Stdio::piped())
|
||||||
|
.output();
|
||||||
|
match output {
|
||||||
|
Ok(output) => {
|
||||||
|
let stdout = output.stdout.to_vec();
|
||||||
|
let stderr = output.stderr.to_vec();
|
||||||
|
let exit_code = output.status.code();
|
||||||
|
let _ = senders.send_to_plugin(PluginInstruction::Update(vec![(
|
||||||
|
Some(plugin_id),
|
||||||
|
Some(client_id),
|
||||||
|
Event::RunCommandResult(exit_code, stdout, stderr, context),
|
||||||
|
)]));
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to run command: {}", e);
|
||||||
|
let stdout = vec![];
|
||||||
|
let stderr = format!("{}", e).as_bytes().to_vec();
|
||||||
|
let exit_code = Some(2);
|
||||||
|
let _ = senders.send_to_plugin(PluginInstruction::Update(vec![(
|
||||||
|
Some(plugin_id),
|
||||||
|
Some(client_id),
|
||||||
|
Event::RunCommandResult(exit_code, stdout, stderr, context),
|
||||||
|
)]));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
BackgroundJob::Exit => {
|
BackgroundJob::Exit => {
|
||||||
for loading_plugin in loading_plugins.values() {
|
for loading_plugin in loading_plugins.values() {
|
||||||
loading_plugin.store(false, Ordering::SeqCst);
|
loading_plugin.store(false, Ordering::SeqCst);
|
||||||
|
|
|
||||||
|
|
@ -473,6 +473,90 @@ fn create_plugin_thread_with_pty_receiver(
|
||||||
(to_plugin, pty_receiver, screen_receiver, Box::new(teardown))
|
(to_plugin, pty_receiver, screen_receiver, Box::new(teardown))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_plugin_thread_with_background_jobs_receiver(
|
||||||
|
zellij_cwd: Option<PathBuf>,
|
||||||
|
) -> (
|
||||||
|
SenderWithContext<PluginInstruction>,
|
||||||
|
Receiver<(BackgroundJob, ErrorContext)>,
|
||||||
|
Receiver<(ScreenInstruction, ErrorContext)>,
|
||||||
|
Box<dyn FnOnce()>,
|
||||||
|
) {
|
||||||
|
let zellij_cwd = zellij_cwd.unwrap_or_else(|| PathBuf::from("."));
|
||||||
|
let (to_server, _server_receiver): ChannelWithContext<ServerInstruction> =
|
||||||
|
channels::bounded(50);
|
||||||
|
let to_server = SenderWithContext::new(to_server);
|
||||||
|
|
||||||
|
let (to_screen, screen_receiver): ChannelWithContext<ScreenInstruction> = channels::unbounded();
|
||||||
|
let to_screen = SenderWithContext::new(to_screen);
|
||||||
|
|
||||||
|
let (to_plugin, plugin_receiver): ChannelWithContext<PluginInstruction> = channels::unbounded();
|
||||||
|
let to_plugin = SenderWithContext::new(to_plugin);
|
||||||
|
let (to_pty, _pty_receiver): ChannelWithContext<PtyInstruction> = channels::unbounded();
|
||||||
|
let to_pty = SenderWithContext::new(to_pty);
|
||||||
|
|
||||||
|
let (to_pty_writer, _pty_writer_receiver): ChannelWithContext<PtyWriteInstruction> =
|
||||||
|
channels::unbounded();
|
||||||
|
let to_pty_writer = SenderWithContext::new(to_pty_writer);
|
||||||
|
|
||||||
|
let (to_background_jobs, background_jobs_receiver): ChannelWithContext<BackgroundJob> =
|
||||||
|
channels::unbounded();
|
||||||
|
let to_background_jobs = SenderWithContext::new(to_background_jobs);
|
||||||
|
|
||||||
|
let plugin_bus = Bus::new(
|
||||||
|
vec![plugin_receiver],
|
||||||
|
Some(&to_screen),
|
||||||
|
Some(&to_pty),
|
||||||
|
Some(&to_plugin),
|
||||||
|
Some(&to_server),
|
||||||
|
Some(&to_pty_writer),
|
||||||
|
Some(&to_background_jobs),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.should_silently_fail();
|
||||||
|
let store = Store::new(wasmer::Singlepass::default());
|
||||||
|
let data_dir = PathBuf::from(tempdir().unwrap().path());
|
||||||
|
let default_shell = PathBuf::from(".");
|
||||||
|
let plugin_capabilities = PluginCapabilities::default();
|
||||||
|
let client_attributes = ClientAttributes::default();
|
||||||
|
let default_shell_action = None; // TODO: change me
|
||||||
|
let plugin_thread = std::thread::Builder::new()
|
||||||
|
.name("plugin_thread".to_string())
|
||||||
|
.spawn(move || {
|
||||||
|
set_var("ZELLIJ_SESSION_NAME", "zellij-test");
|
||||||
|
plugin_thread_main(
|
||||||
|
plugin_bus,
|
||||||
|
store,
|
||||||
|
data_dir,
|
||||||
|
PluginsConfig::default(),
|
||||||
|
Box::new(Layout::default()),
|
||||||
|
default_shell,
|
||||||
|
zellij_cwd,
|
||||||
|
plugin_capabilities,
|
||||||
|
client_attributes,
|
||||||
|
default_shell_action,
|
||||||
|
)
|
||||||
|
.expect("TEST")
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
let teardown = {
|
||||||
|
let to_plugin = to_plugin.clone();
|
||||||
|
move || {
|
||||||
|
let _ = to_pty.send(PtyInstruction::Exit);
|
||||||
|
let _ = to_pty_writer.send(PtyWriteInstruction::Exit);
|
||||||
|
let _ = to_screen.send(ScreenInstruction::Exit);
|
||||||
|
let _ = to_server.send(ServerInstruction::KillSession);
|
||||||
|
let _ = to_plugin.send(PluginInstruction::Exit);
|
||||||
|
let _ = plugin_thread.join();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(
|
||||||
|
to_plugin,
|
||||||
|
background_jobs_receiver,
|
||||||
|
screen_receiver,
|
||||||
|
Box::new(teardown),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref PLUGIN_FIXTURE: String = format!(
|
static ref PLUGIN_FIXTURE: String = format!(
|
||||||
// to populate this file, make sure to run the build-e2e CI job
|
// to populate this file, make sure to run the build-e2e CI job
|
||||||
|
|
@ -5184,3 +5268,153 @@ pub fn denied_permission_request_result() {
|
||||||
|
|
||||||
assert_snapshot!(format!("{:#?}", permissions));
|
assert_snapshot!(format!("{:#?}", permissions));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
pub fn run_command_plugin_command() {
|
||||||
|
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
|
||||||
|
// destructor removes the directory
|
||||||
|
let plugin_host_folder = PathBuf::from(temp_folder.path());
|
||||||
|
let cache_path = plugin_host_folder.join("permissions_test.kdl");
|
||||||
|
let (plugin_thread_sender, background_jobs_receiver, screen_receiver, teardown) =
|
||||||
|
create_plugin_thread_with_background_jobs_receiver(Some(plugin_host_folder));
|
||||||
|
let plugin_should_float = Some(false);
|
||||||
|
let plugin_title = Some("test_plugin".to_owned());
|
||||||
|
let run_plugin = RunPlugin {
|
||||||
|
_allow_exec_host_cmd: false,
|
||||||
|
location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
|
||||||
|
configuration: Default::default(),
|
||||||
|
};
|
||||||
|
let tab_index = 1;
|
||||||
|
let client_id = 1;
|
||||||
|
let size = Size {
|
||||||
|
cols: 121,
|
||||||
|
rows: 20,
|
||||||
|
};
|
||||||
|
let received_background_jobs_instructions = Arc::new(Mutex::new(vec![]));
|
||||||
|
let background_jobs_thread = log_actions_in_thread!(
|
||||||
|
received_background_jobs_instructions,
|
||||||
|
BackgroundJob::RunCommand,
|
||||||
|
background_jobs_receiver,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
|
||||||
|
let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!(
|
||||||
|
received_screen_instructions,
|
||||||
|
ScreenInstruction::Exit,
|
||||||
|
screen_receiver,
|
||||||
|
1,
|
||||||
|
&PermissionType::ChangeApplicationState,
|
||||||
|
cache_path,
|
||||||
|
plugin_thread_sender,
|
||||||
|
client_id
|
||||||
|
);
|
||||||
|
|
||||||
|
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
|
||||||
|
let _ = plugin_thread_sender.send(PluginInstruction::Load(
|
||||||
|
plugin_should_float,
|
||||||
|
false,
|
||||||
|
plugin_title,
|
||||||
|
run_plugin,
|
||||||
|
tab_index,
|
||||||
|
None,
|
||||||
|
client_id,
|
||||||
|
size,
|
||||||
|
));
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||||
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
|
None,
|
||||||
|
Some(client_id),
|
||||||
|
Event::Key(Key::Ctrl('2')), // this triggers the enent in the fixture plugin
|
||||||
|
)]));
|
||||||
|
background_jobs_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
|
teardown();
|
||||||
|
let new_tab_event = received_background_jobs_instructions
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.find_map(|i| {
|
||||||
|
if let BackgroundJob::RunCommand(..) = i {
|
||||||
|
Some(i.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.clone();
|
||||||
|
assert_snapshot!(format!("{:#?}", new_tab_event));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
pub fn run_command_with_env_vars_and_cwd_plugin_command() {
|
||||||
|
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
|
||||||
|
// destructor removes the directory
|
||||||
|
let plugin_host_folder = PathBuf::from(temp_folder.path());
|
||||||
|
let cache_path = plugin_host_folder.join("permissions_test.kdl");
|
||||||
|
let (plugin_thread_sender, background_jobs_receiver, screen_receiver, teardown) =
|
||||||
|
create_plugin_thread_with_background_jobs_receiver(Some(plugin_host_folder));
|
||||||
|
let plugin_should_float = Some(false);
|
||||||
|
let plugin_title = Some("test_plugin".to_owned());
|
||||||
|
let run_plugin = RunPlugin {
|
||||||
|
_allow_exec_host_cmd: false,
|
||||||
|
location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
|
||||||
|
configuration: Default::default(),
|
||||||
|
};
|
||||||
|
let tab_index = 1;
|
||||||
|
let client_id = 1;
|
||||||
|
let size = Size {
|
||||||
|
cols: 121,
|
||||||
|
rows: 20,
|
||||||
|
};
|
||||||
|
let received_background_jobs_instructions = Arc::new(Mutex::new(vec![]));
|
||||||
|
let background_jobs_thread = log_actions_in_thread!(
|
||||||
|
received_background_jobs_instructions,
|
||||||
|
BackgroundJob::RunCommand,
|
||||||
|
background_jobs_receiver,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
|
||||||
|
let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!(
|
||||||
|
received_screen_instructions,
|
||||||
|
ScreenInstruction::Exit,
|
||||||
|
screen_receiver,
|
||||||
|
1,
|
||||||
|
&PermissionType::ChangeApplicationState,
|
||||||
|
cache_path,
|
||||||
|
plugin_thread_sender,
|
||||||
|
client_id
|
||||||
|
);
|
||||||
|
|
||||||
|
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
|
||||||
|
let _ = plugin_thread_sender.send(PluginInstruction::Load(
|
||||||
|
plugin_should_float,
|
||||||
|
false,
|
||||||
|
plugin_title,
|
||||||
|
run_plugin,
|
||||||
|
tab_index,
|
||||||
|
None,
|
||||||
|
client_id,
|
||||||
|
size,
|
||||||
|
));
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||||
|
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
|
||||||
|
None,
|
||||||
|
Some(client_id),
|
||||||
|
Event::Key(Key::Ctrl('3')), // this triggers the enent in the fixture plugin
|
||||||
|
)]));
|
||||||
|
background_jobs_thread.join().unwrap(); // this might take a while if the cache is cold
|
||||||
|
teardown();
|
||||||
|
let new_tab_event = received_background_jobs_instructions
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.find_map(|i| {
|
||||||
|
if let BackgroundJob::RunCommand(..) = i {
|
||||||
|
Some(i.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.clone();
|
||||||
|
assert_snapshot!(format!("{:#?}", new_tab_event));
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
---
|
||||||
|
source: zellij-server/src/plugins/./unit/plugin_tests.rs
|
||||||
|
assertion_line: 5339
|
||||||
|
expression: "format!(\"{:#?}\", new_tab_event)"
|
||||||
|
---
|
||||||
|
Some(
|
||||||
|
RunCommand(
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
"ls",
|
||||||
|
[
|
||||||
|
"-l",
|
||||||
|
],
|
||||||
|
{},
|
||||||
|
".",
|
||||||
|
{
|
||||||
|
"user_key_1": "user_value_1",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
---
|
||||||
|
source: zellij-server/src/plugins/./unit/plugin_tests.rs
|
||||||
|
assertion_line: 5414
|
||||||
|
expression: "format!(\"{:#?}\", new_tab_event)"
|
||||||
|
---
|
||||||
|
Some(
|
||||||
|
RunCommand(
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
"ls",
|
||||||
|
[
|
||||||
|
"-l",
|
||||||
|
],
|
||||||
|
{
|
||||||
|
"VAR1": "some_value",
|
||||||
|
},
|
||||||
|
"/some/custom/folder",
|
||||||
|
{
|
||||||
|
"user_key_2": "user_value_2",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use super::PluginInstruction;
|
use super::PluginInstruction;
|
||||||
|
use crate::background_jobs::BackgroundJob;
|
||||||
use crate::plugins::plugin_map::{PluginEnv, Subscriptions};
|
use crate::plugins::plugin_map::{PluginEnv, Subscriptions};
|
||||||
use crate::plugins::wasm_bridge::handle_plugin_crash;
|
use crate::plugins::wasm_bridge::handle_plugin_crash;
|
||||||
use crate::route::route_action;
|
use crate::route::route_action;
|
||||||
|
|
@ -126,6 +127,9 @@ fn host_run_plugin_command(env: FunctionEnvMut<ForeignFunctionEnv>) {
|
||||||
PluginCommand::SwitchTabTo(tab_index) => switch_tab_to(env, tab_index),
|
PluginCommand::SwitchTabTo(tab_index) => switch_tab_to(env, tab_index),
|
||||||
PluginCommand::SetTimeout(seconds) => set_timeout(env, seconds),
|
PluginCommand::SetTimeout(seconds) => set_timeout(env, seconds),
|
||||||
PluginCommand::ExecCmd(command_line) => exec_cmd(env, command_line),
|
PluginCommand::ExecCmd(command_line) => exec_cmd(env, command_line),
|
||||||
|
PluginCommand::RunCommand(command_line, env_variables, cwd, context) => {
|
||||||
|
run_command(env, command_line, env_variables, cwd, context)
|
||||||
|
},
|
||||||
PluginCommand::PostMessageTo(plugin_message) => {
|
PluginCommand::PostMessageTo(plugin_message) => {
|
||||||
post_message_to(env, plugin_message)?
|
post_message_to(env, plugin_message)?
|
||||||
},
|
},
|
||||||
|
|
@ -572,6 +576,7 @@ fn set_timeout(env: &ForeignFunctionEnv, secs: f64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec_cmd(env: &ForeignFunctionEnv, mut command_line: Vec<String>) {
|
fn exec_cmd(env: &ForeignFunctionEnv, mut command_line: Vec<String>) {
|
||||||
|
log::warn!("The ExecCmd plugin command is deprecated and will be removed in a future version. Please use RunCmd instead (it has all the things and can even show you STDOUT/STDERR and an exit code!)");
|
||||||
let err_context = || {
|
let err_context = || {
|
||||||
format!(
|
format!(
|
||||||
"failed to execute command on host for plugin '{}'",
|
"failed to execute command on host for plugin '{}'",
|
||||||
|
|
@ -595,6 +600,38 @@ fn exec_cmd(env: &ForeignFunctionEnv, mut command_line: Vec<String>) {
|
||||||
.non_fatal();
|
.non_fatal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_command(
|
||||||
|
env: &ForeignFunctionEnv,
|
||||||
|
mut command_line: Vec<String>,
|
||||||
|
env_variables: BTreeMap<String, String>,
|
||||||
|
cwd: PathBuf,
|
||||||
|
context: BTreeMap<String, String>,
|
||||||
|
) {
|
||||||
|
let err_context = || {
|
||||||
|
format!(
|
||||||
|
"failed to execute command on host for plugin '{}'",
|
||||||
|
env.plugin_env.name()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if command_line.is_empty() {
|
||||||
|
log::error!("Command cannot be empty");
|
||||||
|
} else {
|
||||||
|
let command = command_line.remove(0);
|
||||||
|
let _ = env
|
||||||
|
.plugin_env
|
||||||
|
.senders
|
||||||
|
.send_to_background_jobs(BackgroundJob::RunCommand(
|
||||||
|
env.plugin_env.plugin_id,
|
||||||
|
env.plugin_env.client_id,
|
||||||
|
command,
|
||||||
|
command_line,
|
||||||
|
env_variables,
|
||||||
|
cwd,
|
||||||
|
context,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn post_message_to(env: &ForeignFunctionEnv, plugin_message: PluginMessage) -> Result<()> {
|
fn post_message_to(env: &ForeignFunctionEnv, plugin_message: PluginMessage) -> Result<()> {
|
||||||
let worker_name = plugin_message
|
let worker_name = plugin_message
|
||||||
.worker_name
|
.worker_name
|
||||||
|
|
@ -1159,6 +1196,7 @@ fn check_command_permission(
|
||||||
PluginCommand::OpenCommandPane(..)
|
PluginCommand::OpenCommandPane(..)
|
||||||
| PluginCommand::OpenCommandPaneFloating(..)
|
| PluginCommand::OpenCommandPaneFloating(..)
|
||||||
| PluginCommand::OpenCommandPaneInPlace(..)
|
| PluginCommand::OpenCommandPaneInPlace(..)
|
||||||
|
| PluginCommand::RunCommand(..)
|
||||||
| PluginCommand::ExecCmd(..) => PermissionType::RunCommands,
|
| PluginCommand::ExecCmd(..) => PermissionType::RunCommands,
|
||||||
PluginCommand::Write(..) | PluginCommand::WriteChars(..) => PermissionType::WriteToStdin,
|
PluginCommand::Write(..) | PluginCommand::WriteChars(..) => PermissionType::WriteToStdin,
|
||||||
PluginCommand::SwitchTabTo(..)
|
PluginCommand::SwitchTabTo(..)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
use std::collections::HashSet;
|
use std::collections::{BTreeMap, HashSet};
|
||||||
use std::{io, path::Path};
|
use std::{
|
||||||
|
io,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
use zellij_utils::data::*;
|
use zellij_utils::data::*;
|
||||||
use zellij_utils::errors::prelude::*;
|
use zellij_utils::errors::prelude::*;
|
||||||
pub use zellij_utils::plugin_api;
|
pub use zellij_utils::plugin_api;
|
||||||
|
|
@ -171,6 +174,39 @@ pub fn exec_cmd(cmd: &[&str]) {
|
||||||
unsafe { host_run_plugin_command() };
|
unsafe { host_run_plugin_command() };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Run this command in the background on the host machine, optionally being notified of its output
|
||||||
|
/// if subscribed to the `RunCommandResult` Event
|
||||||
|
pub fn run_command(cmd: &[&str], context: BTreeMap<String, String>) {
|
||||||
|
let plugin_command = PluginCommand::RunCommand(
|
||||||
|
cmd.iter().cloned().map(|s| s.to_owned()).collect(),
|
||||||
|
BTreeMap::new(),
|
||||||
|
PathBuf::from("."),
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
|
||||||
|
object_to_stdout(&protobuf_plugin_command.encode_to_vec());
|
||||||
|
unsafe { host_run_plugin_command() };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run this command in the background on the host machine, providing environment variables and a
|
||||||
|
/// cwd. Optionally being notified of its output if subscribed to the `RunCommandResult` Event
|
||||||
|
pub fn run_command_with_env_variables_and_cwd(
|
||||||
|
cmd: &[&str],
|
||||||
|
env_variables: BTreeMap<String, String>,
|
||||||
|
cwd: PathBuf,
|
||||||
|
context: BTreeMap<String, String>,
|
||||||
|
) {
|
||||||
|
let plugin_command = PluginCommand::RunCommand(
|
||||||
|
cmd.iter().cloned().map(|s| s.to_owned()).collect(),
|
||||||
|
env_variables,
|
||||||
|
cwd,
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
|
||||||
|
object_to_stdout(&protobuf_plugin_command.encode_to_vec());
|
||||||
|
unsafe { host_run_plugin_command() };
|
||||||
|
}
|
||||||
|
|
||||||
/// Hide the plugin pane (suppress it) from the UI
|
/// Hide the plugin pane (suppress it) from the UI
|
||||||
pub fn hide_self() {
|
pub fn hide_self() {
|
||||||
let plugin_command = PluginCommand::HideSelf;
|
let plugin_command = PluginCommand::HideSelf;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,10 @@ pub struct EventNameList {
|
||||||
pub struct Event {
|
pub struct Event {
|
||||||
#[prost(enumeration = "EventType", tag = "1")]
|
#[prost(enumeration = "EventType", tag = "1")]
|
||||||
pub name: i32,
|
pub name: i32,
|
||||||
#[prost(oneof = "event::Payload", tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13")]
|
#[prost(
|
||||||
|
oneof = "event::Payload",
|
||||||
|
tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14"
|
||||||
|
)]
|
||||||
pub payload: ::core::option::Option<event::Payload>,
|
pub payload: ::core::option::Option<event::Payload>,
|
||||||
}
|
}
|
||||||
/// Nested message and enum types in `Event`.
|
/// Nested message and enum types in `Event`.
|
||||||
|
|
@ -41,6 +44,8 @@ pub mod event {
|
||||||
PermissionRequestResultPayload(super::PermissionRequestResultPayload),
|
PermissionRequestResultPayload(super::PermissionRequestResultPayload),
|
||||||
#[prost(message, tag = "13")]
|
#[prost(message, tag = "13")]
|
||||||
SessionUpdatePayload(super::SessionUpdatePayload),
|
SessionUpdatePayload(super::SessionUpdatePayload),
|
||||||
|
#[prost(message, tag = "14")]
|
||||||
|
RunCommandResultPayload(super::RunCommandResultPayload),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
|
@ -51,6 +56,26 @@ pub struct SessionUpdatePayload {
|
||||||
}
|
}
|
||||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct RunCommandResultPayload {
|
||||||
|
#[prost(int32, optional, tag = "1")]
|
||||||
|
pub exit_code: ::core::option::Option<i32>,
|
||||||
|
#[prost(bytes = "vec", tag = "2")]
|
||||||
|
pub stdout: ::prost::alloc::vec::Vec<u8>,
|
||||||
|
#[prost(bytes = "vec", tag = "3")]
|
||||||
|
pub stderr: ::prost::alloc::vec::Vec<u8>,
|
||||||
|
#[prost(message, repeated, tag = "4")]
|
||||||
|
pub context: ::prost::alloc::vec::Vec<ContextItem>,
|
||||||
|
}
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct ContextItem {
|
||||||
|
#[prost(string, tag = "1")]
|
||||||
|
pub name: ::prost::alloc::string::String,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub value: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
pub struct PermissionRequestResultPayload {
|
pub struct PermissionRequestResultPayload {
|
||||||
#[prost(bool, tag = "1")]
|
#[prost(bool, tag = "1")]
|
||||||
pub granted: bool,
|
pub granted: bool,
|
||||||
|
|
@ -261,6 +286,7 @@ pub enum EventType {
|
||||||
FileSystemDelete = 14,
|
FileSystemDelete = 14,
|
||||||
PermissionRequestResult = 15,
|
PermissionRequestResult = 15,
|
||||||
SessionUpdate = 16,
|
SessionUpdate = 16,
|
||||||
|
RunCommandResult = 17,
|
||||||
}
|
}
|
||||||
impl EventType {
|
impl EventType {
|
||||||
/// String value of the enum field names used in the ProtoBuf definition.
|
/// String value of the enum field names used in the ProtoBuf definition.
|
||||||
|
|
@ -286,6 +312,7 @@ impl EventType {
|
||||||
EventType::FileSystemDelete => "FileSystemDelete",
|
EventType::FileSystemDelete => "FileSystemDelete",
|
||||||
EventType::PermissionRequestResult => "PermissionRequestResult",
|
EventType::PermissionRequestResult => "PermissionRequestResult",
|
||||||
EventType::SessionUpdate => "SessionUpdate",
|
EventType::SessionUpdate => "SessionUpdate",
|
||||||
|
EventType::RunCommandResult => "RunCommandResult",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||||
|
|
@ -308,6 +335,7 @@ impl EventType {
|
||||||
"FileSystemDelete" => Some(Self::FileSystemDelete),
|
"FileSystemDelete" => Some(Self::FileSystemDelete),
|
||||||
"PermissionRequestResult" => Some(Self::PermissionRequestResult),
|
"PermissionRequestResult" => Some(Self::PermissionRequestResult),
|
||||||
"SessionUpdate" => Some(Self::SessionUpdate),
|
"SessionUpdate" => Some(Self::SessionUpdate),
|
||||||
|
"RunCommandResult" => Some(Self::RunCommandResult),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ pub struct PluginCommand {
|
||||||
pub name: i32,
|
pub name: i32,
|
||||||
#[prost(
|
#[prost(
|
||||||
oneof = "plugin_command::Payload",
|
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"
|
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"
|
||||||
)]
|
)]
|
||||||
pub payload: ::core::option::Option<plugin_command::Payload>,
|
pub payload: ::core::option::Option<plugin_command::Payload>,
|
||||||
}
|
}
|
||||||
|
|
@ -96,6 +96,8 @@ pub mod plugin_command {
|
||||||
OpenTerminalInPlacePayload(super::OpenFilePayload),
|
OpenTerminalInPlacePayload(super::OpenFilePayload),
|
||||||
#[prost(message, tag = "42")]
|
#[prost(message, tag = "42")]
|
||||||
OpenCommandPaneInPlacePayload(super::OpenCommandPanePayload),
|
OpenCommandPaneInPlacePayload(super::OpenCommandPanePayload),
|
||||||
|
#[prost(message, tag = "43")]
|
||||||
|
RunCommandPayload(super::RunCommandPayload),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
|
@ -164,6 +166,34 @@ pub struct ExecCmdPayload {
|
||||||
}
|
}
|
||||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct RunCommandPayload {
|
||||||
|
#[prost(string, repeated, tag = "1")]
|
||||||
|
pub command_line: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
|
||||||
|
#[prost(message, repeated, tag = "2")]
|
||||||
|
pub env_variables: ::prost::alloc::vec::Vec<EnvVariable>,
|
||||||
|
#[prost(string, tag = "3")]
|
||||||
|
pub cwd: ::prost::alloc::string::String,
|
||||||
|
#[prost(message, repeated, tag = "4")]
|
||||||
|
pub context: ::prost::alloc::vec::Vec<ContextItem>,
|
||||||
|
}
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct EnvVariable {
|
||||||
|
#[prost(string, tag = "1")]
|
||||||
|
pub name: ::prost::alloc::string::String,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub value: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct ContextItem {
|
||||||
|
#[prost(string, tag = "1")]
|
||||||
|
pub name: ::prost::alloc::string::String,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub value: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
pub struct PluginMessagePayload {
|
pub struct PluginMessagePayload {
|
||||||
#[prost(message, optional, tag = "1")]
|
#[prost(message, optional, tag = "1")]
|
||||||
pub message: ::core::option::Option<super::message::Message>,
|
pub message: ::core::option::Option<super::message::Message>,
|
||||||
|
|
@ -263,6 +293,7 @@ pub enum CommandName {
|
||||||
OpenTerminalInPlace = 68,
|
OpenTerminalInPlace = 68,
|
||||||
OpenCommandInPlace = 69,
|
OpenCommandInPlace = 69,
|
||||||
OpenFileInPlace = 70,
|
OpenFileInPlace = 70,
|
||||||
|
RunCommand = 71,
|
||||||
}
|
}
|
||||||
impl CommandName {
|
impl CommandName {
|
||||||
/// String value of the enum field names used in the ProtoBuf definition.
|
/// String value of the enum field names used in the ProtoBuf definition.
|
||||||
|
|
@ -342,6 +373,7 @@ impl CommandName {
|
||||||
CommandName::OpenTerminalInPlace => "OpenTerminalInPlace",
|
CommandName::OpenTerminalInPlace => "OpenTerminalInPlace",
|
||||||
CommandName::OpenCommandInPlace => "OpenCommandInPlace",
|
CommandName::OpenCommandInPlace => "OpenCommandInPlace",
|
||||||
CommandName::OpenFileInPlace => "OpenFileInPlace",
|
CommandName::OpenFileInPlace => "OpenFileInPlace",
|
||||||
|
CommandName::RunCommand => "RunCommand",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||||
|
|
@ -418,6 +450,7 @@ impl CommandName {
|
||||||
"OpenTerminalInPlace" => Some(Self::OpenTerminalInPlace),
|
"OpenTerminalInPlace" => Some(Self::OpenTerminalInPlace),
|
||||||
"OpenCommandInPlace" => Some(Self::OpenCommandInPlace),
|
"OpenCommandInPlace" => Some(Self::OpenCommandInPlace),
|
||||||
"OpenFileInPlace" => Some(Self::OpenFileInPlace),
|
"OpenFileInPlace" => Some(Self::OpenFileInPlace),
|
||||||
|
"RunCommand" => Some(Self::RunCommand),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use crate::input::actions::Action;
|
||||||
use crate::input::config::ConversionError;
|
use crate::input::config::ConversionError;
|
||||||
use clap::ArgEnum;
|
use clap::ArgEnum;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
@ -496,6 +496,8 @@ pub enum Event {
|
||||||
/// A Result of plugin permission request
|
/// A Result of plugin permission request
|
||||||
PermissionRequestResult(PermissionStatus),
|
PermissionRequestResult(PermissionStatus),
|
||||||
SessionUpdate(Vec<SessionInfo>),
|
SessionUpdate(Vec<SessionInfo>),
|
||||||
|
RunCommandResult(Option<i32>, Vec<u8>, Vec<u8>, BTreeMap<String, String>), // exit_code, STDOUT, STDERR,
|
||||||
|
// context
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
|
|
@ -1064,4 +1066,13 @@ pub enum PluginCommand {
|
||||||
OpenTerminalInPlace(FileToOpen), // only used for the path as cwd
|
OpenTerminalInPlace(FileToOpen), // only used for the path as cwd
|
||||||
OpenFileInPlace(FileToOpen),
|
OpenFileInPlace(FileToOpen),
|
||||||
OpenCommandPaneInPlace(CommandToRun),
|
OpenCommandPaneInPlace(CommandToRun),
|
||||||
|
RunCommand(
|
||||||
|
Vec<String>,
|
||||||
|
BTreeMap<String, String>,
|
||||||
|
PathBuf,
|
||||||
|
BTreeMap<String, String>,
|
||||||
|
), // command,
|
||||||
|
// env_Variables,
|
||||||
|
// cwd,
|
||||||
|
// context
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -445,6 +445,7 @@ pub enum BackgroundJobContext {
|
||||||
ReadAllSessionInfosOnMachine,
|
ReadAllSessionInfosOnMachine,
|
||||||
ReportSessionInfo,
|
ReportSessionInfo,
|
||||||
ReportLayoutInfo,
|
ReportLayoutInfo,
|
||||||
|
RunCommand,
|
||||||
Exit,
|
Exit,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ enum EventType {
|
||||||
FileSystemDelete = 14;
|
FileSystemDelete = 14;
|
||||||
PermissionRequestResult = 15;
|
PermissionRequestResult = 15;
|
||||||
SessionUpdate = 16;
|
SessionUpdate = 16;
|
||||||
|
RunCommandResult = 17;
|
||||||
}
|
}
|
||||||
|
|
||||||
message EventNameList {
|
message EventNameList {
|
||||||
|
|
@ -61,6 +62,7 @@ message Event {
|
||||||
FileListPayload file_list_payload = 11;
|
FileListPayload file_list_payload = 11;
|
||||||
PermissionRequestResultPayload permission_request_result_payload = 12;
|
PermissionRequestResultPayload permission_request_result_payload = 12;
|
||||||
SessionUpdatePayload session_update_payload = 13;
|
SessionUpdatePayload session_update_payload = 13;
|
||||||
|
RunCommandResultPayload run_command_result_payload = 14;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,6 +70,18 @@ message SessionUpdatePayload {
|
||||||
repeated SessionManifest session_manifests = 1;
|
repeated SessionManifest session_manifests = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message RunCommandResultPayload {
|
||||||
|
optional int32 exit_code = 1;
|
||||||
|
bytes stdout = 2;
|
||||||
|
bytes stderr = 3;
|
||||||
|
repeated ContextItem context = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ContextItem {
|
||||||
|
string name = 1;
|
||||||
|
string value = 2;
|
||||||
|
}
|
||||||
|
|
||||||
message PermissionRequestResultPayload {
|
message PermissionRequestResultPayload {
|
||||||
bool granted = 1;
|
bool granted = 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -183,6 +183,21 @@ impl TryFrom<ProtobufEvent> for Event {
|
||||||
},
|
},
|
||||||
_ => Err("Malformed payload for the SessionUpdate Event"),
|
_ => Err("Malformed payload for the SessionUpdate Event"),
|
||||||
},
|
},
|
||||||
|
Some(ProtobufEventType::RunCommandResult) => match protobuf_event.payload {
|
||||||
|
Some(ProtobufEventPayload::RunCommandResultPayload(run_command_result_payload)) => {
|
||||||
|
Ok(Event::RunCommandResult(
|
||||||
|
run_command_result_payload.exit_code,
|
||||||
|
run_command_result_payload.stdout,
|
||||||
|
run_command_result_payload.stderr,
|
||||||
|
run_command_result_payload
|
||||||
|
.context
|
||||||
|
.into_iter()
|
||||||
|
.map(|c_i| (c_i.name, c_i.value))
|
||||||
|
.collect(),
|
||||||
|
))
|
||||||
|
},
|
||||||
|
_ => Err("Malformed payload for the RunCommandResult Event"),
|
||||||
|
},
|
||||||
None => Err("Unknown Protobuf Event"),
|
None => Err("Unknown Protobuf Event"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -338,6 +353,23 @@ impl TryFrom<Event> for ProtobufEvent {
|
||||||
payload: Some(event::Payload::SessionUpdatePayload(session_update_payload)),
|
payload: Some(event::Payload::SessionUpdatePayload(session_update_payload)),
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
Event::RunCommandResult(exit_code, stdout, stderr, context) => {
|
||||||
|
let run_command_result_payload = RunCommandResultPayload {
|
||||||
|
exit_code,
|
||||||
|
stdout,
|
||||||
|
stderr,
|
||||||
|
context: context
|
||||||
|
.into_iter()
|
||||||
|
.map(|(name, value)| ContextItem { name, value })
|
||||||
|
.collect(),
|
||||||
|
};
|
||||||
|
Ok(ProtobufEvent {
|
||||||
|
name: ProtobufEventType::RunCommandResult as i32,
|
||||||
|
payload: Some(event::Payload::RunCommandResultPayload(
|
||||||
|
run_command_result_payload,
|
||||||
|
)),
|
||||||
|
})
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -783,6 +815,7 @@ impl TryFrom<ProtobufEventType> for EventType {
|
||||||
ProtobufEventType::FileSystemDelete => EventType::FileSystemDelete,
|
ProtobufEventType::FileSystemDelete => EventType::FileSystemDelete,
|
||||||
ProtobufEventType::PermissionRequestResult => EventType::PermissionRequestResult,
|
ProtobufEventType::PermissionRequestResult => EventType::PermissionRequestResult,
|
||||||
ProtobufEventType::SessionUpdate => EventType::SessionUpdate,
|
ProtobufEventType::SessionUpdate => EventType::SessionUpdate,
|
||||||
|
ProtobufEventType::RunCommandResult => EventType::RunCommandResult,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -808,6 +841,7 @@ impl TryFrom<EventType> for ProtobufEventType {
|
||||||
EventType::FileSystemDelete => ProtobufEventType::FileSystemDelete,
|
EventType::FileSystemDelete => ProtobufEventType::FileSystemDelete,
|
||||||
EventType::PermissionRequestResult => ProtobufEventType::PermissionRequestResult,
|
EventType::PermissionRequestResult => ProtobufEventType::PermissionRequestResult,
|
||||||
EventType::SessionUpdate => ProtobufEventType::SessionUpdate,
|
EventType::SessionUpdate => ProtobufEventType::SessionUpdate,
|
||||||
|
EventType::RunCommandResult => ProtobufEventType::RunCommandResult,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,7 @@ enum CommandName {
|
||||||
OpenTerminalInPlace = 68;
|
OpenTerminalInPlace = 68;
|
||||||
OpenCommandInPlace = 69;
|
OpenCommandInPlace = 69;
|
||||||
OpenFileInPlace = 70;
|
OpenFileInPlace = 70;
|
||||||
|
RunCommand = 71;
|
||||||
}
|
}
|
||||||
|
|
||||||
message PluginCommand {
|
message PluginCommand {
|
||||||
|
|
@ -128,6 +129,7 @@ message PluginCommand {
|
||||||
OpenFilePayload open_file_in_place_payload = 40;
|
OpenFilePayload open_file_in_place_payload = 40;
|
||||||
OpenFilePayload open_terminal_in_place_payload = 41;
|
OpenFilePayload open_terminal_in_place_payload = 41;
|
||||||
OpenCommandPanePayload open_command_pane_in_place_payload = 42;
|
OpenCommandPanePayload open_command_pane_in_place_payload = 42;
|
||||||
|
RunCommandPayload run_command_payload = 43;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -170,6 +172,23 @@ message ExecCmdPayload {
|
||||||
repeated string command_line = 1;
|
repeated string command_line = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message RunCommandPayload {
|
||||||
|
repeated string command_line = 1;
|
||||||
|
repeated EnvVariable env_variables = 2;
|
||||||
|
string cwd = 3;
|
||||||
|
repeated ContextItem context = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message EnvVariable {
|
||||||
|
string name = 1;
|
||||||
|
string value = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ContextItem {
|
||||||
|
string name = 1;
|
||||||
|
string value = 2;
|
||||||
|
}
|
||||||
|
|
||||||
message PluginMessagePayload {
|
message PluginMessagePayload {
|
||||||
api.message.Message message = 1;
|
api.message.Message message = 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,10 @@ pub use super::generated_api::api::{
|
||||||
event::EventNameList as ProtobufEventNameList,
|
event::EventNameList as ProtobufEventNameList,
|
||||||
input_mode::InputMode as ProtobufInputMode,
|
input_mode::InputMode as ProtobufInputMode,
|
||||||
plugin_command::{
|
plugin_command::{
|
||||||
plugin_command::Payload, CommandName, ExecCmdPayload, IdAndNewName, MovePayload,
|
plugin_command::Payload, CommandName, ContextItem, EnvVariable, ExecCmdPayload,
|
||||||
OpenCommandPanePayload, OpenFilePayload, PluginCommand as ProtobufPluginCommand,
|
IdAndNewName, MovePayload, OpenCommandPanePayload, OpenFilePayload,
|
||||||
PluginMessagePayload, RequestPluginPermissionPayload, ResizePayload, SetTimeoutPayload,
|
PluginCommand as ProtobufPluginCommand, PluginMessagePayload,
|
||||||
|
RequestPluginPermissionPayload, ResizePayload, RunCommandPayload, SetTimeoutPayload,
|
||||||
SubscribePayload, SwitchSessionPayload, SwitchTabToPayload, UnsubscribePayload,
|
SubscribePayload, SwitchSessionPayload, SwitchTabToPayload, UnsubscribePayload,
|
||||||
},
|
},
|
||||||
plugin_permission::PermissionType as ProtobufPermissionType,
|
plugin_permission::PermissionType as ProtobufPermissionType,
|
||||||
|
|
@ -14,7 +15,9 @@ pub use super::generated_api::api::{
|
||||||
|
|
||||||
use crate::data::{ConnectToSession, PermissionType, PluginCommand};
|
use crate::data::{ConnectToSession, PermissionType, PluginCommand};
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
impl TryFrom<ProtobufPluginCommand> for PluginCommand {
|
impl TryFrom<ProtobufPluginCommand> for PluginCommand {
|
||||||
type Error = &'static str;
|
type Error = &'static str;
|
||||||
|
|
@ -553,6 +556,27 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand {
|
||||||
},
|
},
|
||||||
_ => Err("Mismatched payload for OpenCommandPaneInPlace"),
|
_ => Err("Mismatched payload for OpenCommandPaneInPlace"),
|
||||||
},
|
},
|
||||||
|
Some(CommandName::RunCommand) => match protobuf_plugin_command.payload {
|
||||||
|
Some(Payload::RunCommandPayload(run_command_payload)) => {
|
||||||
|
let env_variables: BTreeMap<String, String> = run_command_payload
|
||||||
|
.env_variables
|
||||||
|
.into_iter()
|
||||||
|
.map(|e| (e.name, e.value))
|
||||||
|
.collect();
|
||||||
|
let context: BTreeMap<String, String> = run_command_payload
|
||||||
|
.context
|
||||||
|
.into_iter()
|
||||||
|
.map(|e| (e.name, e.value))
|
||||||
|
.collect();
|
||||||
|
Ok(PluginCommand::RunCommand(
|
||||||
|
run_command_payload.command_line,
|
||||||
|
env_variables,
|
||||||
|
PathBuf::from(run_command_payload.cwd),
|
||||||
|
context,
|
||||||
|
))
|
||||||
|
},
|
||||||
|
_ => Err("Mismatched payload for RunCommand"),
|
||||||
|
},
|
||||||
None => Err("Unrecognized plugin command"),
|
None => Err("Unrecognized plugin command"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -928,6 +952,26 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand {
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
}),
|
}),
|
||||||
|
PluginCommand::RunCommand(command_line, env_variables, cwd, context) => {
|
||||||
|
let env_variables: Vec<_> = env_variables
|
||||||
|
.into_iter()
|
||||||
|
.map(|(name, value)| EnvVariable { name, value })
|
||||||
|
.collect();
|
||||||
|
let context: Vec<_> = context
|
||||||
|
.into_iter()
|
||||||
|
.map(|(name, value)| ContextItem { name, value })
|
||||||
|
.collect();
|
||||||
|
let cwd = cwd.display().to_string();
|
||||||
|
Ok(ProtobufPluginCommand {
|
||||||
|
name: CommandName::RunCommand as i32,
|
||||||
|
payload: Some(Payload::RunCommandPayload(RunCommandPayload {
|
||||||
|
command_line,
|
||||||
|
env_variables,
|
||||||
|
cwd,
|
||||||
|
context,
|
||||||
|
})),
|
||||||
|
})
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue