rust_expect_macros/lib.rs
1//! rust-expect-macros: Procedural macros for rust-expect
2//!
3//! This crate provides compile-time macros for the rust-expect terminal automation library:
4//!
5//! - [`patterns!`] - Define pattern sets for expect operations
6//! - [`regex!`] - Compile-time validated regex patterns
7//! - [`dialog!`] - Define interactive dialog scripts
8//! - [`timeout!`] - Parse timeout duration specifications
9//!
10//! # Example: Pattern Matching
11//!
12//! ```ignore
13//! use rust_expect_macros::patterns;
14//!
15//! let patterns = patterns! {
16//! "login:",
17//! "password:",
18//! regex(r"\$\s*$"),
19//! };
20//! ```
21//!
22//! # Example: Dialog Script
23//!
24//! ```ignore
25//! use rust_expect_macros::dialog;
26//!
27//! let script = dialog! {
28//! expect "login:";
29//! sendln "admin";
30//! expect "password:";
31//! sendln "secret";
32//! expect_re r"\$\s*$"
33//! };
34//! ```
35//!
36//! # Example: Validated Regex
37//!
38//! ```ignore
39//! use rust_expect_macros::regex;
40//!
41//! // Compile-time validated regex
42//! let prompt = regex!(r"^\w+@\w+:\S+\$\s*$");
43//! ```
44//!
45//! # Example: Human-Readable Timeout
46//!
47//! ```ignore
48//! use rust_expect_macros::timeout;
49//!
50//! let duration = timeout!(5 s);
51//! let long_timeout = timeout!(2 m + 30 s);
52//! ```
53
54// In proc-macro crates, passing parsed input by value is idiomatic
55#![allow(clippy::needless_pass_by_value)]
56
57use proc_macro::TokenStream;
58use syn::parse_macro_input;
59
60mod dialog;
61mod patterns;
62mod regex;
63mod timeout;
64
65/// Define a set of patterns for use with expect operations.
66///
67/// This macro creates a `PatternSet` with compile-time validated patterns.
68///
69/// # Syntax
70///
71/// ```ignore
72/// patterns! {
73/// "literal pattern",
74/// name: "named pattern",
75/// regex(r"regex\s+pattern"),
76/// glob("glob*pattern"),
77/// "pattern" => action_expression,
78/// }
79/// ```
80///
81/// # Examples
82///
83/// ```ignore
84/// let login_patterns = patterns! {
85/// login: "login:",
86/// password: "password:",
87/// prompt: regex(r"\$\s*$"),
88/// };
89///
90/// // Use with session.expect()
91/// let matched = session.expect(&login_patterns).await?;
92/// ```
93#[proc_macro]
94pub fn patterns(input: TokenStream) -> TokenStream {
95 let input = parse_macro_input!(input as patterns::PatternsInput);
96 patterns::expand(input).into()
97}
98
99/// Compile-time validated regex pattern.
100///
101/// Creates a lazily-initialized `regex::Regex` that is validated at compile time.
102/// Invalid regex patterns will cause a compilation error.
103///
104/// # Examples
105///
106/// ```ignore
107/// use rust_expect_macros::regex;
108///
109/// // Valid regex - compiles successfully
110/// let prompt = regex!(r"^\w+@\w+:\S+\$\s*$");
111///
112/// // Invalid regex - compilation error
113/// // let bad = regex!(r"[invalid");
114/// ```
115#[proc_macro]
116pub fn regex(input: TokenStream) -> TokenStream {
117 let input = parse_macro_input!(input as regex::RegexInput);
118 regex::expand(input).into()
119}
120
121/// Define an interactive dialog script.
122///
123/// Creates a `Dialog` that can be executed against a session, automating
124/// send/expect sequences.
125///
126/// # Commands
127///
128/// - `send "text"` - Send text without newline
129/// - `sendln "text"` - Send text with newline
130/// - `expect "pattern"` - Wait for literal pattern
131/// - `expect_re "regex"` - Wait for regex pattern (validated at compile time)
132/// - `wait duration` - Wait for a duration
133/// - `timeout duration` - Set timeout for subsequent operations
134///
135/// # Examples
136///
137/// ```ignore
138/// use rust_expect_macros::dialog;
139/// use std::time::Duration;
140///
141/// let login_script = dialog! {
142/// timeout Duration::from_secs(30);
143/// expect "login:";
144/// sendln "admin";
145/// expect "password:";
146/// sendln "secret123";
147/// expect_re r"\$\s*$"
148/// };
149///
150/// // Execute the dialog
151/// session.run_dialog(&login_script).await?;
152/// ```
153#[proc_macro]
154pub fn dialog(input: TokenStream) -> TokenStream {
155 let input = parse_macro_input!(input as dialog::DialogInput);
156 dialog::expand(input).into()
157}
158
159/// Parse a human-readable timeout specification.
160///
161/// Creates a `std::time::Duration` from a human-readable format.
162///
163/// # Supported Units
164///
165/// - `ns`, `nanos`, `nanoseconds` - Nanoseconds
166/// - `us`, `micros`, `microseconds` - Microseconds
167/// - `ms`, `millis`, `milliseconds` - Milliseconds
168/// - `s`, `sec`, `secs`, `seconds` - Seconds
169/// - `m`, `min`, `mins`, `minutes` - Minutes
170/// - `h`, `hr`, `hrs`, `hours` - Hours
171///
172/// # Examples
173///
174/// ```ignore
175/// use rust_expect_macros::timeout;
176///
177/// let short = timeout!(100 ms);
178/// let medium = timeout!(5 s);
179/// let long = timeout!(2 m);
180/// let compound = timeout!(1 m + 30 s + 500 ms);
181/// ```
182#[proc_macro]
183pub fn timeout(input: TokenStream) -> TokenStream {
184 let input = parse_macro_input!(input as timeout::TimeoutInput);
185 timeout::expand(input).into()
186}