shell_tunnel/output/
screen.rs1use vt100::Parser;
4
5pub struct VirtualScreen {
11 parser: Parser,
12}
13
14impl VirtualScreen {
15 pub fn new() -> Self {
17 Self::with_size(80, 24)
18 }
19
20 pub fn with_size(cols: u16, rows: u16) -> Self {
22 Self {
23 parser: Parser::new(rows, cols, 0),
24 }
25 }
26
27 pub fn process(&mut self, input: &[u8]) {
29 self.parser.process(input);
30 }
31
32 pub fn contents(&self) -> String {
36 self.parser.screen().contents()
37 }
38
39 pub fn lines(&self) -> Vec<String> {
41 let screen = self.parser.screen();
42 let mut lines = Vec::new();
43
44 for row in 0..screen.size().0 {
45 let mut line = String::new();
46 for col in 0..screen.size().1 {
47 if let Some(cell) = screen.cell(row, col) {
48 line.push(cell.contents().chars().next().unwrap_or(' '));
49 }
50 }
51 lines.push(line.trim_end().to_string());
52 }
53
54 lines
55 }
56
57 pub fn non_empty_lines(&self) -> Vec<String> {
59 self.lines().into_iter().filter(|l| !l.is_empty()).collect()
60 }
61
62 pub fn cursor_position(&self) -> (u16, u16) {
64 self.parser.screen().cursor_position()
65 }
66
67 pub fn size(&self) -> (u16, u16) {
69 self.parser.screen().size()
70 }
71
72 pub fn is_empty(&self) -> bool {
74 self.contents().trim().is_empty()
75 }
76
77 pub fn clear(&mut self) {
79 let (rows, cols) = self.size();
80 self.parser = Parser::new(rows, cols, 0);
81 }
82}
83
84impl Default for VirtualScreen {
85 fn default() -> Self {
86 Self::new()
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn test_new_screen() {
96 let screen = VirtualScreen::new();
97 assert_eq!(screen.size(), (24, 80));
98 assert!(screen.is_empty());
99 }
100
101 #[test]
102 fn test_custom_size() {
103 let screen = VirtualScreen::with_size(120, 40);
104 assert_eq!(screen.size(), (40, 120));
105 }
106
107 #[test]
108 fn test_process_text() {
109 let mut screen = VirtualScreen::new();
110 screen.process(b"Hello, World!");
111
112 let contents = screen.contents();
113 assert!(contents.contains("Hello, World!"));
114 }
115
116 #[test]
117 fn test_process_with_newlines() {
118 let mut screen = VirtualScreen::new();
119 screen.process(b"Line 1\r\nLine 2\r\nLine 3");
120
121 let lines = screen.non_empty_lines();
122 assert!(lines.len() >= 3);
123 assert!(lines[0].contains("Line 1"));
124 }
125
126 #[test]
127 fn test_cursor_position() {
128 let mut screen = VirtualScreen::new();
129 screen.process(b"test");
130
131 let (row, col) = screen.cursor_position();
132 assert_eq!(row, 0);
133 assert_eq!(col, 4); }
135
136 #[test]
137 fn test_process_ansi_colors() {
138 let mut screen = VirtualScreen::new();
139 screen.process(b"\x1b[31mRed Text\x1b[0m");
140
141 let contents = screen.contents();
142 assert!(contents.contains("Red Text"));
143 }
144
145 #[test]
146 fn test_clear_screen_sequence() {
147 let mut screen = VirtualScreen::new();
148 screen.process(b"Initial content");
149 screen.process(b"\x1b[2J\x1b[HCleared");
150
151 let contents = screen.contents();
152 assert!(contents.contains("Cleared"));
153 }
154
155 #[test]
156 fn test_clear_method() {
157 let mut screen = VirtualScreen::new();
158 screen.process(b"Some content");
159 screen.clear();
160
161 assert!(screen.is_empty());
162 }
163
164 #[test]
165 fn test_lines_trimmed() {
166 let mut screen = VirtualScreen::new();
167 screen.process(b" text with spaces ");
168
169 let lines = screen.lines();
170 assert!(!lines.is_empty());
172 }
173}