1use crate::segment::{ControlType, Segment, Segments};
6use crate::{Console, ConsoleOptions, Measurement, Renderable};
7
8#[derive(Debug, Clone, Default)]
9pub struct Control {
10 controls: Vec<ControlType>,
11}
12
13impl Control {
14 pub fn new() -> Self {
15 Self {
16 controls: Vec::new(),
17 }
18 }
19
20 pub fn home() -> Self {
21 Self {
22 controls: vec![ControlType::Home],
23 }
24 }
25
26 pub fn carriage_return() -> Self {
27 Self {
28 controls: vec![ControlType::CarriageReturn],
29 }
30 }
31
32 pub fn erase_in_line(mode: u8) -> Self {
33 Self {
34 controls: vec![ControlType::EraseInLine(mode)],
35 }
36 }
37
38 pub fn cursor_up(n: u16) -> Self {
39 Self {
40 controls: vec![ControlType::CursorUp(n)],
41 }
42 }
43
44 pub fn move_to(x: u16, y: u16) -> Self {
45 Self {
46 controls: vec![ControlType::MoveTo { x, y }],
47 }
48 }
49
50 pub fn bell() -> Self {
52 Self {
53 controls: vec![ControlType::Bell],
54 }
55 }
56
57 pub fn clear() -> Self {
59 Self {
60 controls: vec![ControlType::Clear],
61 }
62 }
63
64 pub fn show_cursor(show: bool) -> Self {
66 Self {
67 controls: vec![if show {
68 ControlType::ShowCursor
69 } else {
70 ControlType::HideCursor
71 }],
72 }
73 }
74
75 pub fn alt_screen(enable: bool) -> Self {
77 if enable {
78 Self {
79 controls: vec![ControlType::EnableAltScreen, ControlType::Home],
80 }
81 } else {
82 Self {
83 controls: vec![ControlType::DisableAltScreen],
84 }
85 }
86 }
87
88 pub fn title(_title: impl Into<String>) -> Self {
90 Self {
91 controls: vec![ControlType::SetTitle],
92 }
93 }
94
95 pub fn extend(&mut self, controls: impl IntoIterator<Item = ControlType>) {
96 self.controls.extend(controls);
97 }
98
99 pub fn push(&mut self, control: ControlType) {
100 self.controls.push(control);
101 }
102
103 pub fn is_empty(&self) -> bool {
104 self.controls.is_empty()
105 }
106
107 pub fn into_segments(self) -> Segments {
108 Segments::from_iter(self.controls.into_iter().map(Segment::control))
109 }
110}
111
112pub fn strip_control_codes(text: &str) -> String {
117 text.chars()
118 .filter(|&c| !matches!(c, '\x07' | '\x08' | '\x0B' | '\x0C' | '\r'))
119 .collect()
120}
121
122pub fn escape_control_codes(text: &str) -> String {
127 let mut result = String::with_capacity(text.len());
128 for c in text.chars() {
129 match c {
130 '\x07' => result.push_str("\\a"),
131 '\x08' => result.push_str("\\b"),
132 '\x0B' => result.push_str("\\v"),
133 '\x0C' => result.push_str("\\f"),
134 '\r' => result.push_str("\\r"),
135 _ => result.push(c),
136 }
137 }
138 result
139}
140
141impl Renderable for Control {
142 fn render(&self, _console: &Console, _options: &ConsoleOptions) -> Segments {
143 Segments::from_iter(self.controls.iter().cloned().map(Segment::control))
144 }
145
146 fn measure(&self, _console: &Console, _options: &ConsoleOptions) -> Measurement {
147 Measurement::new(0, 0)
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn test_bell() {
157 let ctrl = Control::bell();
158 assert!(!ctrl.is_empty());
159 }
160
161 #[test]
162 fn test_clear() {
163 let ctrl = Control::clear();
164 assert!(!ctrl.is_empty());
165 }
166
167 #[test]
168 fn test_show_cursor() {
169 let show = Control::show_cursor(true);
170 assert!(!show.is_empty());
171 let hide = Control::show_cursor(false);
172 assert!(!hide.is_empty());
173 }
174
175 #[test]
176 fn test_alt_screen() {
177 let enable = Control::alt_screen(true);
178 assert!(!enable.is_empty());
179 let disable = Control::alt_screen(false);
180 assert!(!disable.is_empty());
181 }
182
183 #[test]
184 fn test_title() {
185 let ctrl = Control::title("Hello");
186 assert!(!ctrl.is_empty());
187 }
188
189 #[test]
190 fn test_strip_control_codes() {
191 assert_eq!(strip_control_codes("hello"), "hello");
192 assert_eq!(strip_control_codes("he\x07llo"), "hello");
193 assert_eq!(strip_control_codes("he\x08llo"), "hello");
194 assert_eq!(strip_control_codes("he\x0Bllo"), "hello");
195 assert_eq!(strip_control_codes("he\x0Cllo"), "hello");
196 assert_eq!(strip_control_codes("he\rllo"), "hello");
197 assert_eq!(strip_control_codes("\x07\x08\x0B\x0C\rhello"), "hello");
198 }
199
200 #[test]
201 fn test_escape_control_codes() {
202 assert_eq!(escape_control_codes("hello"), "hello");
203 assert_eq!(escape_control_codes("he\x07llo"), "he\\allo");
204 assert_eq!(escape_control_codes("he\x08llo"), "he\\bllo");
205 assert_eq!(escape_control_codes("he\x0Bllo"), "he\\vllo");
206 assert_eq!(escape_control_codes("he\x0Cllo"), "he\\fllo");
207 assert_eq!(escape_control_codes("he\rllo"), "he\\rllo");
208 }
209}