use crate::command::{Context, ScripttyCommand};
use crate::pty::PtySession;
use anyhow::Result;
use std::io::Write;
use std::sync::mpsc::Receiver;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use tokio::time::sleep;
pub struct Engine {
ctx: Context,
_output_task: tokio::task::JoinHandle<()>,
}
impl Engine {
pub fn spawn<S: AsRef<str>>(command: &str, args: &[S]) -> Result<Self> {
Self::spawn_with_handler(command, args, |data| {
let stdout = std::io::stdout();
let mut stdout = stdout.lock();
stdout.write_all(data).ok();
stdout.flush().ok();
})
}
pub fn spawn_with_handler<S, F>(command: &str, args: &[S], handler: F) -> Result<Self>
where
S: AsRef<str>,
F: Fn(&[u8]) + Send + Sync + 'static,
{
let string_args: Vec<String> = args.iter().map(|s| s.as_ref().to_string()).collect();
let (pty, reader) = PtySession::spawn(command, &string_args)?;
let output_rx = crate::pty_reader::spawn_reader(reader);
Ok(Self::from_parts(pty, output_rx, handler))
}
fn from_parts<F>(pty: PtySession, output_rx: Receiver<Vec<u8>>, handler: F) -> Self
where
F: Fn(&[u8]) + Send + Sync + 'static,
{
let output_buffer = Arc::new(Mutex::new(String::new()));
let buffer_clone = output_buffer.clone();
let handler = Arc::new(handler);
let handler_clone = handler.clone();
let output_task = tokio::task::spawn_blocking(move || {
while let Ok(data) = output_rx.recv() {
handler_clone(&data);
if let Ok(mut buffer) = buffer_clone.lock() {
buffer.push_str(&String::from_utf8_lossy(&data));
if buffer.len() > 10_000 {
buffer.drain(..5_000);
}
}
}
});
Engine {
ctx: Context {
pty,
output_buffer,
output_handler: handler,
},
_output_task: output_task,
}
}
pub async fn execute(&mut self, commands: Vec<Box<dyn ScripttyCommand>>) -> Result<()> {
for cmd in commands {
cmd.execute(&mut self.ctx).await?;
}
sleep(Duration::from_millis(300)).await;
Ok(())
}
pub fn wait_for_exit(&mut self) -> Result<()> {
self.ctx.pty.wait()
}
}