1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
//! # Scriptty
//!
//! A PTY scripting engine for automating interactive terminal sessions.
//!
//! Scriptty lets you write simple scripts that drive any interactive terminal
//! program with precise control over input timing and output presentation.
//! It is useful for testing CLI tools, building reproducible terminal
//! walkthroughs, and constructing terminal automation pipelines.
//!
//! ## Quick start
//!
//! ```no_run
//! use scriptty::{Engine, parse_str};
//!
//! #[tokio::main]
//! async fn main() -> anyhow::Result<()> {
//! let script = r#"
//! expect "$ "
//! type "echo hello"
//! key Enter
//! expect "hello"
//! send "exit"
//! key Enter
//! "#;
//!
//! let commands = parse_str(script)?;
//! let mut engine = Engine::spawn("bash", &[] as &[&str])?;
//! engine.execute(commands).await?;
//! Ok(())
//! }
//! ```
//!
//! ## Parsing scripts
//!
//! Use [`parse_str`] to parse a script from an in-memory string, or
//! [`parse_file`] to read one from a file path. Both return a
//! `Vec<Box<dyn `[`ScripttyCommand`]`>>` ready to pass to [`Engine::execute`].
//!
//! ## Script syntax
//!
//! | Command | Description |
//! |---------|-------------|
//! | `type "text"` | Simulate typing with per-character delays |
//! | `send "text"` | Send text to the program immediately (no typing simulation) |
//! | `key Enter` | Send a key press (supports `Ctrl+`, `Alt+`, `Shift+` modifiers) |
//! | `show "text"` | Write text directly to the output handler |
//! | `expect "pattern"` | Wait until `pattern` appears in the program output |
//! | `expect "pattern" 5s` | Wait up to 5 seconds for the pattern |
//! | `wait 500ms` | Pause for a duration (`ms` or `s` units, floats allowed) |
//! | `# comment` | Full-line or inline comment |
//!
//! ## Custom output handling
//!
//! By default [`Engine::spawn`] writes all output to stdout. Use
//! [`Engine::spawn_with_handler`] to redirect output to any sink:
//!
//! ```no_run
//! use scriptty::{Engine, parse_str};
//!
//! #[tokio::main]
//! async fn main() -> anyhow::Result<()> {
//! let commands = parse_str(r#"type "hello""#)?;
//!
//! let captured = std::sync::Arc::new(std::sync::Mutex::new(Vec::<u8>::new()));
//! let sink = captured.clone();
//!
//! let mut engine = Engine::spawn_with_handler("bash", &[] as &[&str], move |data| {
//! sink.lock().unwrap().extend_from_slice(data);
//! })?;
//!
//! engine.execute(commands).await?;
//! println!("{}", String::from_utf8_lossy(&captured.lock().unwrap()));
//! Ok(())
//! }
//! ```
//!
//! ## Implementing a custom command
//!
//! Implement [`ScripttyCommand`] to add new commands to the engine:
//!
//! ```no_run
//! use scriptty::command::{Context, ScripttyCommand};
//! use async_trait::async_trait;
//! use anyhow::Result;
//!
//! pub struct Beep;
//!
//! impl Beep {
//! pub const NAME: &'static str = "beep";
//! }
//!
//! #[async_trait(?Send)]
//! impl ScripttyCommand for Beep {
//! fn name(&self) -> &'static str { Self::NAME }
//!
//! fn parse(_args: &str) -> Result<Self> {
//! Ok(Self)
//! }
//!
//! async fn execute(&self, ctx: &mut Context) -> Result<()> {
//! ctx.emit(b"\x07"); // BEL character
//! Ok(())
//! }
//! }
//! ```
pub
pub
pub use ;
pub use ;
pub use Engine;
pub use ;