use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;
use rust_expect::interact::{InteractAction, ResizeContext, TerminalSize};
use rust_expect::prelude::*;
#[tokio::main]
async fn main() -> Result<()> {
println!("rust-expect Interactive Pattern Hooks Example");
println!("==============================================\n");
demonstrate_pattern_matching().await?;
demonstrate_resize_context();
demonstrate_action_types();
println!("\nAll pattern hooks examples completed!");
Ok(())
}
async fn demonstrate_pattern_matching() -> Result<()> {
println!("1. Pattern Matching Demonstration");
println!(" --------------------------------");
let mut session = Session::spawn("/bin/sh", &[]).await?;
session
.expect_timeout(Pattern::shell_prompt(), Duration::from_secs(2))
.await?;
let output_match_count = Arc::new(AtomicUsize::new(0));
let counter_clone = Arc::clone(&output_match_count);
println!(" Simulating pattern-based response to 'password:' prompts\n");
session.send_line("echo 'Enter password: test'").await?;
let result = session
.expect_timeout(Pattern::literal("password:"), Duration::from_secs(2))
.await;
match result {
Ok(m) => {
counter_clone.fetch_add(1, Ordering::SeqCst);
println!(" Matched pattern 'password:' in output");
println!(
" Text before match: {:?}",
m.before.chars().take(30).collect::<String>()
);
println!(" Would respond with: 'my_secret_password\\n'");
}
Err(_) => println!(" Pattern not found (expected in some shells)"),
}
let _ = session
.expect_timeout(Pattern::shell_prompt(), Duration::from_secs(2))
.await;
session.send_line("exit").await?;
let _ = session.wait().await;
println!(
" Pattern matches recorded: {}",
output_match_count.load(Ordering::SeqCst)
);
println!();
Ok(())
}
fn demonstrate_resize_context() {
println!("2. Resize Context Demonstration");
println!(" -----------------------------");
let old_size = TerminalSize::new(80, 24);
let new_size = TerminalSize::new(120, 40);
let ctx = ResizeContext {
size: new_size,
previous: Some(old_size),
};
println!(" Simulating terminal resize event:");
println!(" Previous size: {}x{}", old_size.cols, old_size.rows);
println!(" New size: {}x{}", ctx.size.cols, ctx.size.rows);
let action = example_resize_handler(&ctx);
match action {
InteractAction::Continue => {
println!(" Handler action: Continue (resize handled silently)");
}
InteractAction::Send(ref data) => println!(" Handler action: Send {} bytes", data.len()),
InteractAction::Stop => println!(" Handler action: Stop interaction"),
InteractAction::Error(ref msg) => println!(" Handler action: Error - {msg}"),
}
let initial_ctx = ResizeContext {
size: TerminalSize::new(80, 24),
previous: None,
};
println!(
" Initial size (no previous): {}x{}",
initial_ctx.size.cols, initial_ctx.size.rows
);
println!();
}
fn example_resize_handler(ctx: &ResizeContext) -> InteractAction {
eprintln!(
"[resize] Terminal resized to {}x{}",
ctx.size.cols, ctx.size.rows
);
if ctx.size.cols < 40 || ctx.size.rows < 10 {
return InteractAction::Error("Terminal too small".into());
}
InteractAction::Continue
}
fn demonstrate_action_types() {
println!("3. InteractAction Types Demonstration");
println!(" -----------------------------------");
let continue_action = InteractAction::Continue;
println!(" Continue: {continue_action:?}");
let send_action = InteractAction::send("hello\n");
match &send_action {
InteractAction::Send(data) => {
println!(
" Send: {} bytes - {:?}",
data.len(),
String::from_utf8_lossy(data)
);
}
_ => unreachable!(),
}
let send_bytes_action = InteractAction::send_bytes(vec![0x03]); match &send_bytes_action {
InteractAction::Send(data) => {
println!(" Send bytes: {data:?} (Ctrl+C)");
}
_ => unreachable!(),
}
let stop_action = InteractAction::Stop;
println!(" Stop: {stop_action:?}");
let error_action = InteractAction::Error("Something went wrong".into());
match &error_action {
InteractAction::Error(msg) => println!(" Error: {msg:?}"),
_ => unreachable!(),
}
println!();
}