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
80pub struct Sender<W> {
82 writer: W,
83 line_ending: LineEnding,
84 char_delay: Option<Duration>,
86}
87
88impl<W: AsyncWriteExt + Unpin + Send> Sender<W> {
89 pub const fn new(writer: W) -> Self {
91 Self {
92 writer,
93 line_ending: LineEnding::Lf,
94 char_delay: None,
95 }
96 }
97
98 pub const fn set_line_ending(&mut self, ending: LineEnding) {
100 self.line_ending = ending;
101 }
102
103 pub const fn set_char_delay(&mut self, delay: Option<Duration>) {
105 self.char_delay = delay;
106 }
107
108 #[must_use]
110 pub const fn line_ending(&self) -> LineEnding {
111 self.line_ending
112 }
113
114 pub async fn send_with_delay(&mut self, data: &[u8]) -> Result<()> {
116 if let Some(delay) = self.char_delay {
117 for byte in data {
118 self.writer
119 .write_all(&[*byte])
120 .await
121 .map_err(crate::error::ExpectError::Io)?;
122 self.writer
123 .flush()
124 .await
125 .map_err(crate::error::ExpectError::Io)?;
126 tokio::time::sleep(delay).await;
127 }
128 } else {
129 self.writer
130 .write_all(data)
131 .await
132 .map_err(crate::error::ExpectError::Io)?;
133 self.writer
134 .flush()
135 .await
136 .map_err(crate::error::ExpectError::Io)?;
137 }
138 Ok(())
139 }
140
141 pub const fn writer_mut(&mut self) -> &mut W {
143 &mut self.writer
144 }
145}
146
147impl<W: AsyncWriteExt + Unpin + Send> BasicSend for Sender<W> {
148 async fn send_bytes(&mut self, data: &[u8]) -> Result<()> {
149 self.send_with_delay(data).await
150 }
151
152 async fn send_line(&mut self, line: &str) -> Result<()> {
153 self.send_line_with(line, self.line_ending).await
154 }
155}
156
157pub struct AnsiSequences;
159
160impl AnsiSequences {
161 pub const CURSOR_UP: &'static [u8] = b"\x1b[A";
163 pub const CURSOR_DOWN: &'static [u8] = b"\x1b[B";
165 pub const CURSOR_RIGHT: &'static [u8] = b"\x1b[C";
167 pub const CURSOR_LEFT: &'static [u8] = b"\x1b[D";
169 pub const HOME: &'static [u8] = b"\x1b[H";
171 pub const END: &'static [u8] = b"\x1b[F";
173 pub const PAGE_UP: &'static [u8] = b"\x1b[5~";
175 pub const PAGE_DOWN: &'static [u8] = b"\x1b[6~";
177 pub const INSERT: &'static [u8] = b"\x1b[2~";
179 pub const DELETE: &'static [u8] = b"\x1b[3~";
181 pub const F1: &'static [u8] = b"\x1bOP";
183 pub const F2: &'static [u8] = b"\x1bOQ";
185 pub const F3: &'static [u8] = b"\x1bOR";
187 pub const F4: &'static [u8] = b"\x1bOS";
189 pub const F5: &'static [u8] = b"\x1b[15~";
191 pub const F6: &'static [u8] = b"\x1b[17~";
193 pub const F7: &'static [u8] = b"\x1b[18~";
195 pub const F8: &'static [u8] = b"\x1b[19~";
197 pub const F9: &'static [u8] = b"\x1b[20~";
199 pub const F10: &'static [u8] = b"\x1b[21~";
201 pub const F11: &'static [u8] = b"\x1b[23~";
203 pub const F12: &'static [u8] = b"\x1b[24~";
205
206 #[must_use]
208 pub fn cursor_move(rows: i32, cols: i32) -> Vec<u8> {
209 let mut result = Vec::new();
210
211 if rows != 0 {
212 let dir = if rows > 0 { 'B' } else { 'A' };
213 let count = rows.unsigned_abs();
214 result.extend(format!("\x1b[{count}{dir}").as_bytes());
215 }
216
217 if cols != 0 {
218 let dir = if cols > 0 { 'C' } else { 'D' };
219 let count = cols.unsigned_abs();
220 result.extend(format!("\x1b[{count}{dir}").as_bytes());
221 }
222
223 result
224 }
225
226 #[must_use]
228 pub fn cursor_position(row: u32, col: u32) -> Vec<u8> {
229 format!("\x1b[{row};{col}H").into_bytes()
230 }
231}
232
233pub trait AnsiSend: BasicSend {
235 fn send_cursor_up(&mut self) -> impl std::future::Future<Output = Result<()>> + Send
237 where
238 Self: Send,
239 {
240 async move { self.send_bytes(AnsiSequences::CURSOR_UP).await }
241 }
242
243 fn send_cursor_down(&mut self) -> impl std::future::Future<Output = Result<()>> + Send
245 where
246 Self: Send,
247 {
248 async move { self.send_bytes(AnsiSequences::CURSOR_DOWN).await }
249 }
250
251 fn send_cursor_right(&mut self) -> impl std::future::Future<Output = Result<()>> + Send
253 where
254 Self: Send,
255 {
256 async move { self.send_bytes(AnsiSequences::CURSOR_RIGHT).await }
257 }
258
259 fn send_cursor_left(&mut self) -> impl std::future::Future<Output = Result<()>> + Send
261 where
262 Self: Send,
263 {
264 async move { self.send_bytes(AnsiSequences::CURSOR_LEFT).await }
265 }
266
267 fn send_home(&mut self) -> impl std::future::Future<Output = Result<()>> + Send
269 where
270 Self: Send,
271 {
272 async move { self.send_bytes(AnsiSequences::HOME).await }
273 }
274
275 fn send_end(&mut self) -> impl std::future::Future<Output = Result<()>> + Send
277 where
278 Self: Send,
279 {
280 async move { self.send_bytes(AnsiSequences::END).await }
281 }
282
283 fn send_delete(&mut self) -> impl std::future::Future<Output = Result<()>> + Send
285 where
286 Self: Send,
287 {
288 async move { self.send_bytes(AnsiSequences::DELETE).await }
289 }
290
291 fn send_function_key(&mut self, n: u8) -> impl std::future::Future<Output = Result<()>> + Send
293 where
294 Self: Send,
295 {
296 async move {
297 let seq = match n {
298 1 => AnsiSequences::F1,
299 2 => AnsiSequences::F2,
300 3 => AnsiSequences::F3,
301 4 => AnsiSequences::F4,
302 5 => AnsiSequences::F5,
303 6 => AnsiSequences::F6,
304 7 => AnsiSequences::F7,
305 8 => AnsiSequences::F8,
306 9 => AnsiSequences::F9,
307 10 => AnsiSequences::F10,
308 11 => AnsiSequences::F11,
309 12 => AnsiSequences::F12,
310 _ => return Ok(()),
311 };
312 self.send_bytes(seq).await
313 }
314 }
315}
316
317impl<T: BasicSend> AnsiSend for T {}
318
319#[cfg(test)]
320mod tests {
321 use super::*;
322
323 #[test]
324 fn ansi_cursor_move() {
325 assert_eq!(AnsiSequences::cursor_move(3, 0), b"\x1b[3B");
326 assert_eq!(AnsiSequences::cursor_move(-2, 0), b"\x1b[2A");
327 assert_eq!(AnsiSequences::cursor_move(0, 5), b"\x1b[5C");
328 assert_eq!(AnsiSequences::cursor_move(0, -4), b"\x1b[4D");
329 }
330
331 #[test]
332 fn ansi_cursor_position() {
333 assert_eq!(AnsiSequences::cursor_position(1, 1), b"\x1b[1;1H");
334 assert_eq!(AnsiSequences::cursor_position(10, 20), b"\x1b[10;20H");
335 }
336}