use crate::pty::PtySession;
use anyhow::Result;
use async_trait::async_trait;
use std::sync::{Arc, Mutex};
use std::time::Duration;
type OutputHandler = Arc<dyn Fn(&[u8]) + Send + Sync>;
pub struct Context {
pub(crate) pty: PtySession,
pub(crate) output_buffer: Arc<Mutex<String>>,
pub(crate) output_handler: OutputHandler,
}
impl Context {
pub fn write_to_pty(&mut self, data: &[u8]) -> Result<()> {
self.pty.write(data)
}
pub fn emit(&self, data: &[u8]) {
(self.output_handler)(data);
}
pub async fn wait_for_pattern(&self, pattern: &str, timeout: Duration) -> Result<()> {
let deadline = tokio::time::Instant::now() + timeout;
loop {
{
let mut buffer = self.output_buffer.lock().unwrap();
if let Some(idx) = buffer.find(pattern) {
buffer.drain(..idx + pattern.len());
return Ok(());
}
}
if tokio::time::Instant::now() >= deadline {
return Err(anyhow::anyhow!(
"Timeout waiting for pattern: '{}'",
pattern
));
}
tokio::time::sleep(Duration::from_millis(10)).await;
}
}
}
#[async_trait(?Send)]
pub trait ScripttyCommand: 'static {
fn name(&self) -> &'static str;
fn parse(args: &str) -> Result<Self>
where
Self: Sized;
fn parse_boxed(args: &str) -> Result<Box<dyn ScripttyCommand>>
where
Self: Sized,
{
Ok(Box::new(Self::parse(args)?))
}
async fn execute(&self, ctx: &mut Context) -> Result<()>;
}