Skip to content

Commit 12733e5

Browse files
committed
feat: add running interactive command
1 parent 352624b commit 12733e5

File tree

2 files changed

+62
-3
lines changed

2 files changed

+62
-3
lines changed

src/action_menu.rs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub enum Operation {
3232
CopyToClipboard { is_relative_path: bool },
3333
FileDetails,
3434
CustomCommand,
35+
CustomInteractiveCommand,
3536
ViewContent,
3637
}
3738

@@ -99,6 +100,10 @@ pub fn generate_known_actions() -> Vec<MenuAction> {
99100
is_relative_path: true,
100101
},
101102
},
103+
MenuAction {
104+
name: "Run interactive command",
105+
operation: Operation::CustomInteractiveCommand,
106+
},
102107
MenuAction {
103108
name: "Run command",
104109
operation: Operation::CustomCommand,
@@ -117,8 +122,9 @@ pub fn execute_interactive_shell_operation(
117122
tui: &mut Tui,
118123
) -> Result<()> {
119124
let cmd = String::from(command_template).replace("{}", path);
125+
log(format!("Executing command: {:?}", cmd).as_str());
120126
tui.exit().context("failed to exit TUI mode")?;
121-
let mut output = std::process::Command::new("sh")
127+
let mut output = Command::new("sh")
122128
.arg("-c")
123129
.arg(cmd.clone())
124130
.stdin(Stdio::inherit())
@@ -277,6 +283,43 @@ pub fn run_custom_command(workdir: String, cmd: &String) -> Result<String> {
277283
))
278284
}
279285

286+
pub fn run_custom_interactive_command(
287+
workdir: String,
288+
cmd: &String,
289+
tui: &mut Tui,
290+
) -> Result<String> {
291+
log(format!("Executing command: {:?}", cmd).as_str());
292+
tui.exit().context("failed to exit TUI mode")?;
293+
let c = Command::new("sh")
294+
.arg("-c")
295+
.arg(cmd.clone())
296+
.current_dir(workdir)
297+
.stdin(Stdio::inherit())
298+
.stdout(Stdio::inherit())
299+
.stderr(Stdio::inherit())
300+
.spawn()
301+
.context("failed to start a command")?;
302+
let output = c
303+
.wait_with_output()
304+
.context("failed to read command output")?;
305+
tui.enter().context("failed to enter TUI mode again")?;
306+
307+
let stderr = String::from_utf8_lossy(&output.stderr);
308+
let stdout = String::from_utf8_lossy(&output.stdout);
309+
if !output.status.success() {
310+
let error = format!(
311+
"Failed to execute command: {:?}\nExit code: {}\n{}\n{}",
312+
cmd,
313+
output.status.code().unwrap_or(0),
314+
stderr,
315+
stdout,
316+
);
317+
log(error.as_str());
318+
return Err(anyhow!(error));
319+
}
320+
Ok(format!("Command \"{}\" executed successfully.", cmd))
321+
}
322+
280323
pub fn read_file_content(abs_path: &String) -> Result<String> {
281324
let content: String = fs::read_to_string(abs_path).context("Unable to read file")?;
282325
Ok(content)

src/app_logic/logic_action_menu.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use std::str::Chars;
33
use crate::action_menu::{
44
copy_path_to_clipboard, create_directory, create_file, delete_tree_node,
55
execute_interactive_shell_operation, execute_shell_operation, get_file_details,
6-
read_file_content, rename_file, run_custom_command, MenuAction, Operation,
6+
read_file_content, rename_file, run_custom_command, run_custom_interactive_command, MenuAction,
7+
Operation,
78
};
89
use crate::app::App;
910
use crate::appdata::WindowFocus;
@@ -114,6 +115,13 @@ impl App {
114115
self.action_menu_buffer = format!("\"{}\"", abs_path);
115116
self.action_menu_cursor_x = self.action_menu_buffer.chars().count();
116117
}
118+
Operation::CustomInteractiveCommand => {
119+
self.window_focus = WindowFocus::ActionMenuStep2;
120+
self.action_menu_operation = Some(action.operation.clone());
121+
self.action_menu_title = format!("Run interactive command at {}", current_dir_path);
122+
self.action_menu_buffer = format!("\"{}\"", abs_path);
123+
self.action_menu_cursor_x = self.action_menu_buffer.chars().count();
124+
}
117125
Operation::ViewContent => {
118126
self.window_focus = WindowFocus::Tree;
119127
if !is_directory {
@@ -129,7 +137,7 @@ impl App {
129137
self.populate_current_child_nodes();
130138
}
131139

132-
pub fn execute_dialog_action_step2(&mut self, _: &mut Tui) {
140+
pub fn execute_dialog_action_step2(&mut self, tui: &mut Tui) {
133141
let abs_path: String = match self.get_selected_abs_path() {
134142
Some(abs_path) => abs_path,
135143
None => return,
@@ -175,6 +183,14 @@ impl App {
175183
}
176184
});
177185
}
186+
Some(Operation::CustomInteractiveCommand) => {
187+
let res =
188+
run_custom_interactive_command(current_dir_path, &self.action_menu_buffer, tui);
189+
match res {
190+
Ok(output) => self.show_info(output),
191+
Err(err) => self.error_message = Some(err.to_string()),
192+
}
193+
}
178194
_ => {}
179195
}
180196
self.window_focus = WindowFocus::Tree;

0 commit comments

Comments
 (0)