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