live-cli/src/command.rs

134 lines
3.8 KiB
Rust
Raw Normal View History

2022-12-17 12:02:15 -05:00
use crate::event::EventMessage;
use crate::CommandOptions;
use anyhow::bail;
use anyhow::Context;
use anyhow::Result;
2022-12-17 12:02:15 -05:00
use crossbeam::channel::Receiver;
use crossbeam::channel::Sender;
use parking_lot::RwLock;
use std::sync::Arc;
2022-12-15 23:19:17 -05:00
use std::{
io::Write,
process::{Command, Stdio},
};
pub type CommandRequest = (Arc<RwLock<CommandOptions>>, Arc<String>);
2022-12-15 23:19:17 -05:00
use crate::App;
#[derive(Debug)]
pub enum CommandCompleted {
Success(CommandResult),
Failure(Vec<u8>),
2022-12-17 12:02:15 -05:00
}
#[derive(Debug)]
2022-12-16 21:28:40 -05:00
pub struct CommandResult {
2022-12-15 23:19:17 -05:00
pub status_success: bool,
2022-12-16 21:28:40 -05:00
pub stdout: Vec<u8>,
pub stderr: Vec<u8>,
2022-12-15 23:19:17 -05:00
}
2022-12-16 21:28:40 -05:00
impl Default for CommandResult {
2022-12-15 23:19:17 -05:00
fn default() -> Self {
Self {
status_success: true,
2022-12-16 21:28:40 -05:00
stdout: vec![],
stderr: vec![],
2022-12-15 23:19:17 -05:00
}
}
}
pub fn command_event_loop(
command_request_receiver: Receiver<CommandRequest>,
event_sender: Sender<EventMessage>,
) -> 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<App>) {
// 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;
2022-12-17 12:02:15 -05:00
// }
// }
// Err(e) => {
// app.command_result.status_success = false;
// app.command_result.stderr = e.to_string().as_bytes().to_vec();
// }
2022-12-17 12:02:15 -05:00
// }
// }
fn run_inner(command_request: CommandRequest) -> Result<CommandResult> {
// Spawn the child
let mut child = {
let request = command_request.0.read();
2022-12-16 20:41:16 -05:00
let mut command = Command::new(&request.command);
command
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.args(&request.hidden_options);
2022-12-17 11:25:15 -05:00
if request.wordsplit {
match shellwords::split(&request.cmdline) {
Ok(a) => {
command.args(a);
}
Err(e) => {
bail!("Argument error: {e:?}");
}
2022-12-17 11:25:15 -05:00
}
} else {
// TODO: Avoid cloning here
command.arg(&request.cmdline);
2022-12-17 11:25:15 -05:00
}
std::mem::drop(request);
command.spawn()
}?;
2022-12-15 23:19:17 -05:00
let mut stdin = child.stdin.take().context("Could not take stdin")?;
let text_orig_clone = command_request.1.clone();
2022-12-15 23:19:17 -05:00
std::thread::spawn(move || {
2022-12-17 00:10:17 -05:00
let _result = stdin.write_all(text_orig_clone.as_bytes());
2022-12-15 23:19:17 -05:00
});
// Collect the output
let output = child.wait_with_output()?;
2022-12-15 23:19:17 -05:00
Ok(CommandResult {
2022-12-15 23:19:17 -05:00
status_success: output.status.success(),
2022-12-16 21:28:40 -05:00
stdout: output.stdout,
stderr: output.stderr,
})
2022-12-15 23:19:17 -05:00
}