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 ropey::Rope; use std::sync::Arc; use std::{ io::Write, process::{Command, Stdio}, }; pub type CommandRequest = (Arc>, Arc); #[derive(Debug)] pub enum CommandCompleted { Success(CommandResult), Failure(Rope), } #[derive(Debug)] pub struct CommandResult { pub status_success: bool, pub stdout: Rope, pub stderr: Rope, } 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.len_bytes() == 0 { CommandCompleted::Failure(c.stderr) } else { // CommandCompleted::Success((c, FormattedCommandResult::from(&c))) CommandCompleted::Success(c) } } Err(e) => CommandCompleted::Failure(Rope::from_str(&e.to_string())), }, ))?; } Err(e) => { event_sender.send(EventMessage::CommandLoopStopped)?; bail!("Crossterm read error: {}", e); } } } } 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: Rope::from_reader(&output.stdout[..])?, stderr: Rope::from_reader(&output.stderr[..])?, }) }