rust_expect/send/
basic.rs1use std::time::Duration;
7
8use tokio::io::AsyncWriteExt;
9
10use crate::config::LineEnding;
11use crate::error::Result;
12use crate::types::ControlChar;
13
14pub trait BasicSend: Send {
16 fn send_bytes(&mut self, data: &[u8]) -> impl std::future::Future<Output = Result<()>> + Send;
18
19 fn send_str(&mut self, s: &str) -> impl std::future::Future<Output = Result<()>> + Send {
21 async move { self.send_bytes(s.as_bytes()).await }
22 }
23
24 fn send_line_with(
26 &mut self,
27 line: &str,
28 ending: LineEnding,
29 ) -> impl std::future::Future<Output = Result<()>> + Send {
30 async move {
31 self.send_str(line).await?;
32 self.send_str(ending.as_str()).await
33 }
34 }
35
36 fn send_line(&mut self, line: &str) -> impl std::future::Future<Output = Result<()>> + Send {
38 self.send_line_with(line, LineEnding::Lf)
39 }
40
41 fn send_control(
43 &mut self,
44 ctrl: ControlChar,
45 ) -> impl std::future::Future<Output = Result<()>> + Send {
46 async move { self.send_bytes(&[ctrl.as_byte()]).await }
47 }
48
49 fn send_interrupt(&mut self) -> impl std::future::Future<Output = Result<()>> + Send {
51 self.send_control(ControlChar::CtrlC)
52 }
53
54 fn send_eof(&mut self) -> impl std::future::Future<Output = Result<()>> + Send {
56 self.send_control(ControlChar::CtrlD)
57 }
58
59 fn send_suspend(&mut self) -> impl std::future::Future<Output = Result<()>> + Send {
61 self.send_control(ControlChar::CtrlZ)
62 }
63
64 fn send_escape(&mut self) -> impl std::future::Future<Output = Result<()>> + Send {
66 self.send_control(ControlChar::Escape)
67 }
68
69 fn send_tab(&mut self) -> impl std::future::Future<Output = Result<()>> + Send {
71 self.send_control(ControlChar::CtrlI)
72 }
73
74 fn send_backspace(&mut self) -> impl std::future::Future<Output = Result<()>> + Send {
76 self.send_control(ControlChar::CtrlH)
77 }
78
79 fn send_paste(&mut self, text: &str) -> impl std::future::Future<Output = Result<()>> + Send {
92 async move {
93 self.send_bytes(b"\x1b[200~").await?;
94 self.send_str(text).await?;
95 self.send_bytes(b"\x1b[201~").await
96 }
97 }
98}
99
100pub struct Sender<W> {
102 writer: W,
103 line_ending: LineEnding,
104 char_delay: Option<Duration>,
106}
107
108impl<W: AsyncWriteExt + Unpin + Send> Sender<W> {
109 pub const fn new(writer: W) -> Self {
111 Self {
112 writer,
113 line_ending: LineEnding::Lf,
114 char_delay: None,
115 }
116 }
117
118 pub const fn set_line_ending(&mut self, ending: LineEnding) {
120 self.line_ending = ending;
121 }
122
123 pub const fn set_char_delay(&mut self, delay: Option<Duration>) {
125 self.char_delay = delay;
126 }
127
128 #[must_use]
130 pub const fn line_ending(&self) -> LineEnding {
131 self.line_ending
132 }
133
134 pub async fn send_with_delay(&mut self, data: &[u8]) -> Result<()> {
136 if let Some(delay) = self.char_delay {
137 for byte in data {
138 self.writer
139 .write_all(&[*byte])
140 .await
141 .map_err(crate::error::ExpectError::Io)?;
142 self.writer
143 .flush()
144 .await
145 .map_err(crate::error::ExpectError::Io)?;
146 tokio::time::sleep(delay).await;
147 }
148 } else {
149 self.writer
150 .write_all(data)
151 .await
152 .map_err(crate::error::ExpectError::Io)?;
153 self.writer
154 .flush()
155 .await
156 .map_err(crate::error::ExpectError::Io)?;
157 }
158 Ok(())
159 }
160
161 pub const fn writer_mut(&mut self) -> &mut W {
163 &mut self.writer
164 }
165}
166
167impl<W: AsyncWriteExt + Unpin + Send> BasicSend for Sender<W> {
168 async fn send_bytes(&mut self, data: &[u8]) -> Result<()> {
169 self.send_with_delay(data).await
170 }
171
172 async fn send_line(&mut self, line: &str) -> Result<()> {
173 self.send_line_with(line, self.line_ending).await
174 }
175}
176
177pub struct AnsiSequences;
179
180impl AnsiSequences {
181 pub const CURSOR_UP: &'static [u8] = b"\x1b[A";
183 pub const CURSOR_DOWN: &'static [u8] = b"\x1b[B";
185 pub const CURSOR_RIGHT: &'static [u8] = b"\x1b[C";
187 pub const CURSOR_LEFT: &'static [u8] = b"\x1b[D";
189 pub const HOME: &'static [u8] = b"\x1b[H";
191 pub const END: &'static [u8] = b"\x1b[F";
193 pub const PAGE_UP: &'static [u8] = b"\x1b[5~";
195 pub const PAGE_DOWN: &'static [u8] = b"\x1b[6~";
197 pub const INSERT: &'static [u8] = b"\x1b[2~";
199 pub const DELETE: &'static [u8] = b"\x1b[3~";
201 pub const F1: &'static [u8] = b"\x1bOP";
203 pub const F2: &'static [u8] = b"\x1bOQ";
205 pub const F3: &'static [u8] = b"\x1bOR";
207 pub const F4: &'static [u8] = b"\x1bOS";
209 pub const F5: &'static [u8] = b"\x1b[15~";
211 pub const F6: &'static [u8] = b"\x1b[17~";
213 pub const F7: &'static [u8] = b"\x1b[18~";
215 pub const F8: &'static [u8] = b"\x1b[19~";
217 pub const F9: &'static [u8] = b"\x1b[20~";
219 pub const F10: &'static [u8] = b"\x1b[21~";
221 pub const F11: &'static [u8] = b"\x1b[23~";
223 pub const F12: &'static [u8] = b"\x1b[24~";
225
226 #[must_use]
228 pub fn cursor_move(rows: i32, cols: i32) -> Vec<u8> {
229 let mut result = Vec::new();
230
231 if rows != 0 {
232 let dir = if rows > 0 { 'B' } else { 'A' };
233 let count = rows.unsigned_abs();
234 result.extend(format!("\x1b[{count}{dir}").as_bytes());
235 }
236
237 if cols != 0 {
238 let dir = if cols > 0 { 'C' } else { 'D' };
239 let count = cols.unsigned_abs();
240 result.extend(format!("\x1b[{count}{dir}").as_bytes());
241 }
242
243 result
244 }
245
246 #[must_use]
248 pub fn cursor_position(row: u32, col: u32) -> Vec<u8> {
249 format!("\x1b[{row};{col}H").into_bytes()
250 }
251}
252
253pub trait AnsiSend: BasicSend {
255 fn send_cursor_up(&mut self) -> impl std::future::Future<Output = Result<()>> + Send
257 where
258 Self: Send,
259 {
260 async move { self.send_bytes(AnsiSequences::CURSOR_UP).await }
261 }
262
263 fn send_cursor_down(&mut self) -> impl std::future::Future<Output = Result<()>> + Send
265 where
266 Self: Send,
267 {
268 async move { self.send_bytes(AnsiSequences::CURSOR_DOWN).await }
269 }
270
271 fn send_cursor_right(&mut self) -> impl std::future::Future<Output = Result<()>> + Send
273 where
274 Self: Send,
275 {
276 async move { self.send_bytes(AnsiSequences::CURSOR_RIGHT).await }
277 }
278
279 fn send_cursor_left(&mut self) -> impl std::future::Future<Output = Result<()>> + Send
281 where
282 Self: Send,
283 {
284 async move { self.send_bytes(AnsiSequences::CURSOR_LEFT).await }
285 }
286
287 fn send_home(&mut self) -> impl std::future::Future<Output = Result<()>> + Send
289 where
290 Self: Send,
291 {
292 async move { self.send_bytes(AnsiSequences::HOME).await }
293 }
294
295 fn send_end(&mut self) -> impl std::future::Future<Output = Result<()>> + Send
297 where
298 Self: Send,
299 {
300 async move { self.send_bytes(AnsiSequences::END).await }
301 }
302
303 fn send_delete(&mut self) -> impl std::future::Future<Output = Result<()>> + Send
305 where
306 Self: Send,
307 {
308 async move { self.send_bytes(AnsiSequences::DELETE).await }
309 }
310
311 fn send_function_key(&mut self, n: u8) -> impl std::future::Future<Output = Result<()>> + Send
313 where
314 Self: Send,
315 {
316 async move {
317 let seq = match n {
318 1 => AnsiSequences::F1,
319 2 => AnsiSequences::F2,
320 3 => AnsiSequences::F3,
321 4 => AnsiSequences::F4,
322 5 => AnsiSequences::F5,
323 6 => AnsiSequences::F6,
324 7 => AnsiSequences::F7,
325 8 => AnsiSequences::F8,
326 9 => AnsiSequences::F9,
327 10 => AnsiSequences::F10,
328 11 => AnsiSequences::F11,
329 12 => AnsiSequences::F12,
330 _ => return Ok(()),
331 };
332 self.send_bytes(seq).await
333 }
334 }
335}
336
337impl<T: BasicSend> AnsiSend for T {}
338
339#[cfg(test)]
340mod tests {
341 use super::*;
342
343 #[test]
344 fn ansi_cursor_move() {
345 assert_eq!(AnsiSequences::cursor_move(3, 0), b"\x1b[3B");
346 assert_eq!(AnsiSequences::cursor_move(-2, 0), b"\x1b[2A");
347 assert_eq!(AnsiSequences::cursor_move(0, 5), b"\x1b[5C");
348 assert_eq!(AnsiSequences::cursor_move(0, -4), b"\x1b[4D");
349 }
350
351 #[test]
352 fn ansi_cursor_position() {
353 assert_eq!(AnsiSequences::cursor_position(1, 1), b"\x1b[1;1H");
354 assert_eq!(AnsiSequences::cursor_position(10, 20), b"\x1b[10;20H");
355 }
356}