1#![no_std]
2
3use core::fmt;
4
5macro_rules! escape_code {
6 ($doc:expr, $name:ident, $value:expr) => {
7 #[doc = $doc]
8 pub struct $name;
9
10 impl fmt::Display for $name {
11 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
12 write!(f, $value)
13 }
14 }
15 };
16}
17
18pub enum CursorTo {
20 TopLeft,
21 AbsoluteX(u16),
22 AbsoluteXY(u16, u16),
23}
24
25impl fmt::Display for CursorTo {
26 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
27 match *self {
28 CursorTo::TopLeft => write!(f, "\x1B[{};{}H", 1, 1),
29 CursorTo::AbsoluteX(x) => write!(f, "\x1B[{}G", x + 1),
30 CursorTo::AbsoluteXY(x, y) => write!(f, "\x1B[{};{}H", y + 1, x + 1),
31 }
32 }
33}
34
35pub enum CursorMove {
37 X(i16),
38 XY(i16, i16),
39 Y(i16),
40}
41
42impl fmt::Display for CursorMove {
43 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
44 match *self {
45 CursorMove::X(x) if x > 0 => write!(f, "\x1B[{}C", x),
46 CursorMove::X(x) if x < 0 => write!(f, "\x1B[{}D", -x),
47 CursorMove::X(_) => fmt::Result::Ok(()),
48
49 CursorMove::XY(x, y) => {
50 CursorMove::X(x).fmt(f)?;
51 CursorMove::Y(y).fmt(f)?;
52 fmt::Result::Ok(())
53 }
54
55 CursorMove::Y(y) if y > 0 => write!(f, "\x1B[{}B", y),
56 CursorMove::Y(y) if y < 0 => write!(f, "\x1B[{}A", -y),
57 CursorMove::Y(_) => fmt::Result::Ok(()),
58 }
59 }
60}
61
62pub struct CursorUp(pub u16);
64
65impl fmt::Display for CursorUp {
66 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
67 write!(f, "\x1B[{}A", self.0)
68 }
69}
70
71pub struct CursorDown(pub u16);
73
74impl fmt::Display for CursorDown {
75 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
76 write!(f, "\x1B[{}B", self.0)
77 }
78}
79
80pub struct CursorForward(pub u16);
82
83impl fmt::Display for CursorForward {
84 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
85 write!(f, "\x1B[{}C", self.0)
86 }
87}
88
89pub struct CursorBackward(pub u16);
91
92impl fmt::Display for CursorBackward {
93 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
94 write!(f, "\x1B[{}D", self.0)
95 }
96}
97
98escape_code!("Move cursor to the left side.", CursorLeft, "\x1B[1000D");
99escape_code!("Save cursor position.", CursorSavePosition, "\x1B[s");
100escape_code!("Restore saved cursor position.", CursorRestorePosition, "\x1B[u");
101escape_code!("Get cursor position.", CursorGetPosition, "\x1B[6n");
102escape_code!("Move cursor to the next line.", CursorNextLine, "\x1B[E");
103escape_code!("Move cursor to the previous line.", CursorPrevLine, "\x1B[F");
104escape_code!("Hide cursor.", CursorHide, "\x1B[?25l");
105escape_code!("Show cursor.", CursorShow, "\x1B[?25h");
106
107pub struct EraseLines(pub u16);
109
110impl fmt::Display for EraseLines {
111 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
112 for idx in 0..self.0 {
113 if idx > 0 {
114 write!(f, "{}", CursorUp(1))?;
115 }
116
117 write!(f, "{}", CursorLeft)?;
118 write!(f, "{}", EraseEndLine)?;
119 }
120
121 fmt::Result::Ok(())
122 }
123}
124
125escape_code!("Erase from the current cursor position to the end of the current line.", EraseEndLine, "\x1B[K");
126escape_code!("Erase from the current cursor position to the start of the current line.", EraseStartLine, "\x1B[1K");
127escape_code!("Erase the entire current line.", EraseLine, "\x1B[2K");
128
129escape_code!("Erase the screen from the current line down to the bottom of the screen.", EraseDown, "\x1B[J");
130escape_code!("Erase the screen from the current line up to the top of the screen.", EraseUp, "\x1B[1J");
131escape_code!("Erase the screen and move the cursor the top left position.", EraseScreen, "\x1B[2J");
132escape_code!("Scroll display up one line.", ScrollUp, "\x1B[S");
133escape_code!("Scroll display down one line.", ScrollDown, "\x1B[T");
134
135escape_code!("Clear the terminal screen.", ClearScreen, "\u{001b}c");
136escape_code!("Enter the [alternative screen](https://terminalguide.namepad.de/mode/p47/).", EnterAlternativeScreen, "\x1B[?1049h");
137escape_code!("Exit the [alternative screen](https://terminalguide.namepad.de/mode/p47/).", ExitAlternativeScreen, "\x1B[?1049l");
138escape_code!("Output a beeping sound.", Beep, "\u{0007}");
139
140#[cfg(test)]
141extern crate std;
142
143#[cfg(test)]
144mod tests {
145 use std::{io::Write, string::String, vec::Vec};
146
147 macro_rules! assert_escape_output {
148 ($name:ident, $code:expr, $expected:expr) => {
149 #[test]
150 fn $name() {
151 let mut buf = Vec::new();
152 write!(buf, "{}", $code).unwrap();
153
154 let result = String::from_utf8(buf).unwrap();
155 assert_eq!(result, $expected);
156 }
157 };
158 }
159
160 assert_escape_output!(cursor_up_1, super::CursorUp(1), "\x1B[1A");
161 assert_escape_output!(cursor_up_23, super::CursorUp(23), "\x1B[23A");
162
163 assert_escape_output!(cursor_down_1, super::CursorDown(1), "\x1B[1B");
164 assert_escape_output!(cursor_down_23, super::CursorDown(23), "\x1B[23B");
165
166 assert_escape_output!(cursor_forward_1, super::CursorForward(1), "\x1B[1C");
167 assert_escape_output!(cursor_forward_23, super::CursorForward(23), "\x1B[23C");
168
169 assert_escape_output!(cursor_backward_1, super::CursorBackward(1), "\x1B[1D");
170 assert_escape_output!(cursor_backward_23, super::CursorBackward(23), "\x1B[23D");
171
172 assert_escape_output!(cursor_left, super::CursorLeft, "\x1B[1000D");
173 assert_escape_output!(cursor_save_position, super::CursorSavePosition, "\x1B[s");
174 assert_escape_output!(cursor_restore_position, super::CursorRestorePosition, "\x1B[u");
175 assert_escape_output!(cursor_get_position, super::CursorGetPosition, "\x1B[6n");
176 assert_escape_output!(cursor_next_line, super::CursorNextLine, "\x1B[E");
177 assert_escape_output!(cursor_prev_line, super::CursorPrevLine, "\x1B[F");
178 assert_escape_output!(cursor_hide, super::CursorHide, "\x1B[?25l");
179 assert_escape_output!(cursor_show, super::CursorShow, "\x1B[?25h");
180
181 assert_escape_output!(erase_lines_1, super::EraseLines(1), "\x1B[1000D\x1B[K");
182 assert_escape_output!(erase_lines_2, super::EraseLines(2), "\x1B[1000D\x1B[K\x1B[1A\x1B[1000D\x1B[K");
183 assert_escape_output!(erase_lines_3, super::EraseLines(3), "\x1B[1000D\x1B[K\x1B[1A\x1B[1000D\x1B[K\x1B[1A\x1B[1000D\x1B[K");
184}