1use std::fmt;
46
47#[cfg(windows)]
48use crate::Result;
49use crate::{csi, impl_display, Command};
50
51pub use sys::position;
52
53pub(crate) mod sys;
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub struct MoveTo(pub u16, pub u16);
63
64impl Command for MoveTo {
65 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
66 write!(f, csi!("{};{}H"), self.1 + 1, self.0 + 1)
67 }
68
69 #[cfg(windows)]
70 fn execute_winapi(&self) -> Result<()> {
71 sys::move_to(self.0, self.1)
72 }
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82pub struct MoveToNextLine(pub u16);
83
84impl Command for MoveToNextLine {
85 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
86 if self.0 != 0 {
87 write!(f, csi!("{}E"), self.0)?;
88 }
89 Ok(())
90 }
91
92 #[cfg(windows)]
93 fn execute_winapi(&self) -> Result<()> {
94 if self.0 != 0 {
95 sys::move_to_next_line(self.0)?;
96 }
97 Ok(())
98 }
99}
100
101#[derive(Debug, Clone, Copy, PartialEq, Eq)]
108pub struct MoveToPreviousLine(pub u16);
109
110impl Command for MoveToPreviousLine {
111 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
112 if self.0 != 0 {
113 write!(f, csi!("{}F"), self.0)?;
114 }
115 Ok(())
116 }
117
118 #[cfg(windows)]
119 fn execute_winapi(&self) -> Result<()> {
120 if self.0 != 0 {
121 sys::move_to_previous_line(self.0)?;
122 }
123 Ok(())
124 }
125}
126
127#[derive(Debug, Clone, Copy, PartialEq, Eq)]
133pub struct MoveToColumn(pub u16);
134
135impl Command for MoveToColumn {
136 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
137 if self.0 != 0 {
138 write!(f, csi!("{}G"), self.0)?;
139 }
140 Ok(())
141 }
142
143 #[cfg(windows)]
144 fn execute_winapi(&self) -> Result<()> {
145 sys::move_to_column(self.0)
146 }
147}
148
149#[derive(Debug, Clone, Copy, PartialEq, Eq)]
155pub struct MoveToRow(pub u16);
156
157impl Command for MoveToRow {
158 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
159 if self.0 != 0 {
160 write!(f, csi!("{}d"), self.0)?
161 }
162 Ok(())
163 }
164
165 #[cfg(windows)]
166 fn execute_winapi(&self) -> Result<()> {
167 sys::move_to_row(self.0)
168 }
169}
170
171#[derive(Debug, Clone, Copy, PartialEq, Eq)]
177pub struct MoveUp(pub u16);
178
179impl Command for MoveUp {
180 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
181 if self.0 != 0 {
182 write!(f, csi!("{}A"), self.0)?;
183 }
184 Ok(())
185 }
186
187 #[cfg(windows)]
188 fn execute_winapi(&self) -> Result<()> {
189 sys::move_up(self.0)
190 }
191}
192
193#[derive(Debug, Clone, Copy, PartialEq, Eq)]
199pub struct MoveRight(pub u16);
200
201impl Command for MoveRight {
202 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
203 if self.0 != 0 {
204 write!(f, csi!("{}C"), self.0)?;
205 }
206 Ok(())
207 }
208
209 #[cfg(windows)]
210 fn execute_winapi(&self) -> Result<()> {
211 sys::move_right(self.0)
212 }
213}
214
215#[derive(Debug, Clone, Copy, PartialEq, Eq)]
221pub struct MoveDown(pub u16);
222
223impl Command for MoveDown {
224 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
225 if self.0 != 0 {
226 write!(f, csi!("{}B"), self.0)?;
227 }
228 Ok(())
229 }
230
231 #[cfg(windows)]
232 fn execute_winapi(&self) -> Result<()> {
233 sys::move_down(self.0)
234 }
235}
236
237#[derive(Debug, Clone, Copy, PartialEq, Eq)]
243pub struct MoveLeft(pub u16);
244
245impl Command for MoveLeft {
246 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
247 if self.0 != 0 {
248 write!(f, csi!("{}D"), self.0)?;
249 }
250 Ok(())
251 }
252
253 #[cfg(windows)]
254 fn execute_winapi(&self) -> Result<()> {
255 sys::move_left(self.0)
256 }
257}
258
259#[derive(Debug, Clone, Copy, PartialEq, Eq)]
268pub struct SavePosition;
269
270impl Command for SavePosition {
271 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
272 f.write_str("\x1B7")
273 }
274
275 #[cfg(windows)]
276 fn execute_winapi(&self) -> Result<()> {
277 sys::save_position()
278 }
279}
280
281#[derive(Debug, Clone, Copy, PartialEq, Eq)]
290pub struct RestorePosition;
291
292impl Command for RestorePosition {
293 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
294 f.write_str("\x1B8")
295 }
296
297 #[cfg(windows)]
298 fn execute_winapi(&self) -> Result<()> {
299 sys::restore_position()
300 }
301}
302
303#[derive(Debug, Clone, Copy, PartialEq, Eq)]
309pub struct Hide;
310
311impl Command for Hide {
312 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
313 f.write_str(csi!("?25l"))
314 }
315
316 #[cfg(windows)]
317 fn execute_winapi(&self) -> Result<()> {
318 sys::show_cursor(false)
319 }
320}
321
322#[derive(Debug, Clone, Copy, PartialEq, Eq)]
328pub struct Show;
329
330impl Command for Show {
331 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
332 f.write_str(csi!("?25h"))
333 }
334
335 #[cfg(windows)]
336 fn execute_winapi(&self) -> Result<()> {
337 sys::show_cursor(true)
338 }
339}
340
341#[derive(Debug, Clone, Copy, PartialEq, Eq)]
348pub struct EnableBlinking;
349
350impl Command for EnableBlinking {
351 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
352 f.write_str(csi!("?12h"))
353 }
354
355 #[cfg(windows)]
356 fn execute_winapi(&self) -> Result<()> {
357 Ok(())
358 }
359}
360
361#[derive(Debug, Clone, Copy, PartialEq, Eq)]
368pub struct DisableBlinking;
369
370impl Command for DisableBlinking {
371 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
372 f.write_str(csi!("?12l"))
373 }
374
375 #[cfg(windows)]
376 fn execute_winapi(&self) -> Result<()> {
377 Ok(())
378 }
379}
380
381#[derive(Debug, Clone, Copy, PartialEq, Eq)]
387pub enum CursorShape {
388 UnderScore,
389 Line,
390 Block,
391}
392
393#[derive(Debug, Clone, Copy, PartialEq, Eq)]
399pub struct SetCursorShape(pub CursorShape);
400
401impl Command for SetCursorShape {
402 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
403 use CursorShape::*;
404 match self.0 {
405 UnderScore => f.write_str("\x1b[3 q"),
406 Line => f.write_str("\x1b[5 q"),
407 Block => f.write_str("\x1b[2 q"),
408 }
409 }
410
411 #[cfg(windows)]
412 fn execute_winapi(&self) -> Result<()> {
413 Ok(())
414 }
415}
416
417impl_display!(for MoveTo);
418impl_display!(for MoveToColumn);
419impl_display!(for MoveToRow);
420impl_display!(for MoveToNextLine);
421impl_display!(for MoveToPreviousLine);
422impl_display!(for MoveUp);
423impl_display!(for MoveDown);
424impl_display!(for MoveLeft);
425impl_display!(for MoveRight);
426impl_display!(for SavePosition);
427impl_display!(for RestorePosition);
428impl_display!(for Hide);
429impl_display!(for Show);
430impl_display!(for EnableBlinking);
431impl_display!(for DisableBlinking);
432impl_display!(for SetCursorShape);
433
434#[cfg(test)]
435mod tests {
436 use std::io::{self, stdout};
437
438 use crate::execute;
439
440 use super::{
441 position, MoveDown, MoveLeft, MoveRight, MoveTo, MoveUp, RestorePosition, SavePosition,
442 };
443
444 #[test]
446 #[ignore]
447 fn test_move_to() {
448 let (saved_x, saved_y) = position().unwrap();
449
450 execute!(stdout(), MoveTo(saved_x + 1, saved_y + 1)).unwrap();
451 assert_eq!(position().unwrap(), (saved_x + 1, saved_y + 1));
452
453 execute!(stdout(), MoveTo(saved_x, saved_y)).unwrap();
454 assert_eq!(position().unwrap(), (saved_x, saved_y));
455 }
456
457 #[test]
459 #[ignore]
460 fn test_move_right() {
461 let (saved_x, saved_y) = position().unwrap();
462 execute!(io::stdout(), MoveRight(1)).unwrap();
463 assert_eq!(position().unwrap(), (saved_x + 1, saved_y));
464 }
465
466 #[test]
468 #[ignore]
469 fn test_move_left() {
470 execute!(stdout(), MoveTo(2, 0), MoveLeft(2)).unwrap();
471 assert_eq!(position().unwrap(), (0, 0));
472 }
473
474 #[test]
476 #[ignore]
477 fn test_move_up() {
478 execute!(stdout(), MoveTo(0, 2), MoveUp(2)).unwrap();
479 assert_eq!(position().unwrap(), (0, 0));
480 }
481
482 #[test]
484 #[ignore]
485 fn test_move_down() {
486 execute!(stdout(), MoveTo(0, 0), MoveDown(2)).unwrap();
487
488 assert_eq!(position().unwrap(), (0, 2));
489 }
490
491 #[test]
493 #[ignore]
494 fn test_save_restore_position() {
495 let (saved_x, saved_y) = position().unwrap();
496
497 execute!(
498 stdout(),
499 SavePosition,
500 MoveTo(saved_x + 1, saved_y + 1),
501 RestorePosition
502 )
503 .unwrap();
504
505 let (x, y) = position().unwrap();
506
507 assert_eq!(x, saved_x);
508 assert_eq!(y, saved_y);
509 }
510}