use crate::event::EventMessage; use crate::CommandOptions; use anyhow::bail; use anyhow::Context; use anyhow::Result; use crossbeam::channel::Receiver; use crossbeam::channel::Sender; use parking_lot::RwLock; use std::sync::Arc; use std::{ io::Write, process::{Command, Stdio}, }; pub type CommandRequest = (Arc>, Arc); use crate::App; #[derive(Debug)] pub enum CommandCompleted { Success(CommandResult), Failure(Vec), } #[derive(Debug)] pub struct CommandResult { pub status_success: bool, pub stdout: Vec, pub stderr: Vec, } impl Default for CommandResult { fn default() -> Self { Self { status_success: true, stdout: vec![], stderr: vec![], } } } pub fn command_event_loop( command_request_receiver: Receiver, event_sender: Sender, ) -> Result<()> { loop { match command_request_receiver.recv() { Ok(command_request) => { event_sender.send(EventMessage::CommandCompleted( match run_inner(command_request) { Ok(c) => { // If there was no stdout and the command failed, don't touch stdout if !c.status_success && c.stdout.is_empty() { CommandCompleted::Failure(c.stderr) } else { CommandCompleted::Success(c) } } Err(e) => CommandCompleted::Failure(e.to_string().as_bytes().to_vec()), }, ))?; } Err(e) => { event_sender.send(EventMessage::CommandLoopStopped)?; bail!("Crossterm read error: {}", e); } } } } // pub fn run(app: Arc) { // match run_inner(app) { // Ok(c) => { // // If there was no stdout and the command failed, don't touch stdout // if !c.status_success && c.stdout.is_empty() { // app.command_result.stderr = c.stderr; // app.command_result.status_success = c.status_success; // } else { // app.command_result = c; // } // } // Err(e) => { // app.command_result.status_success = false; // app.command_result.stderr = e.to_string().as_bytes().to_vec(); // } // } // } fn run_inner(command_request: CommandRequest) -> Result { // Spawn the child let mut child = { let request = command_request.0.read(); let mut command = Command::new(&request.command); command .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .args(&request.hidden_options); if request.wordsplit { match shellwords::split(&request.cmdline) { Ok(a) => { command.args(a); } Err(e) => { bail!("Argument error: {e:?}"); } } } else { // TODO: Avoid cloning here command.arg(&request.cmdline); } std::mem::drop(request); command.spawn() }?; let mut stdin = child.stdin.take().context("Could not take stdin")?; let text_orig_clone = command_request.1.clone(); std::thread::spawn(move || { let _result = stdin.write_all(text_orig_clone.as_bytes()); }); // Collect the output let output = child.wait_with_output()?; Ok(CommandResult { status_success: output.status.success(), stdout: output.stdout, stderr: output.stderr, }) }