file_reader/
file_reader.rs1use rustui::*;
2use std::{
3 fs::File,
4 io::{self, BufRead, BufReader},
5 thread, time,
6};
7
8const RENDERING_RATE: time::Duration = time::Duration::from_millis(31); const INPUT_CAPTURING_RATE: time::Duration = time::Duration::from_millis(10); fn main() -> Result<(), Box<dyn std::error::Error>> {
12 let args: Vec<String> = std::env::args().collect();
13 let file_path = if args.len() > 1 {
14 &args[1]
15 } else {
16 eprintln!("Usage: file_reader <file_path>");
17 return Ok(());
18 };
19
20 let mut file_reader = FileReader::new(file_path);
21
22 let mut win = Window::new(false)?;
23 win.initialize(RENDERING_RATE)?; let input_rx = InputListener::new(INPUT_CAPTURING_RATE); loop {
27 if let Ok(event) = input_rx.try_recv() {
29 match event {
30 InputEvent::Key(Key::Char('q')) => break,
31 InputEvent::Key(Key::ArrowUp) => {
32 file_reader.scroll_up();
33 }
34 InputEvent::Key(Key::ArrowDown) => {
35 let visible_lines = win.height.saturating_sub(4);
36 file_reader.scroll_down(visible_lines);
37 }
38 InputEvent::Key(Key::PageUp) => {
39 let visible_lines = win.height.saturating_sub(4);
40 file_reader.page_up(visible_lines);
41 }
42 InputEvent::Key(Key::PageDown) => {
43 let visible_lines = win.height.saturating_sub(4);
44 file_reader.page_down(visible_lines);
45 }
46 InputEvent::Key(Key::Home) => {
47 file_reader.current_line = 0;
48 file_reader.scroll_offset = 0;
49 }
50 InputEvent::Key(Key::End) => {
51 let visible_lines = win.height.saturating_sub(4);
52 file_reader.current_line = file_reader.lines.len().saturating_sub(1);
53 file_reader.scroll_offset =
54 file_reader.lines.len().saturating_sub(visible_lines);
55 }
56 _ => {}
57 }
58 }
59
60 win.draw(|canvas| {
62 file_reader.render(canvas);
63 })?;
64
65 thread::sleep(time::Duration::from_millis(31)); }
67 Ok(())
68}
69
70struct FileReader {
71 file_path: String,
72 lines: Vec<String>,
73 current_line: usize,
74 scroll_offset: usize,
75}
76
77impl FileReader {
78 fn new(file_path: &str) -> Self {
79 let file = File::open(file_path).unwrap();
80 let reader = BufReader::new(file);
81 let lines: Result<Vec<String>, io::Error> = reader.lines().collect();
82 Self {
83 file_path: file_path.to_string(),
84 lines: lines.unwrap(),
85 current_line: 0,
86 scroll_offset: 0,
87 }
88 }
89
90 fn scroll_up(&mut self) {
91 if self.current_line > 0 {
92 self.current_line -= 1;
93 if self.current_line < self.scroll_offset {
94 self.scroll_offset = self.current_line;
95 }
96 }
97 }
98
99 fn scroll_down(&mut self, visible_lines: usize) {
100 if self.current_line + 1 < self.lines.len() {
101 self.current_line += 1;
102 if self.current_line >= self.scroll_offset + visible_lines {
103 self.scroll_offset = self.current_line - visible_lines + 1;
104 }
105 }
106 }
107
108 fn page_up(&mut self, visible_lines: usize) {
109 let page_size = visible_lines.saturating_sub(1);
110 self.current_line = self.current_line.saturating_sub(page_size);
111 self.scroll_offset = self.scroll_offset.saturating_sub(page_size);
112 }
113
114 fn page_down(&mut self, visible_lines: usize) {
115 let page_size = visible_lines.saturating_sub(1);
116 self.current_line = (self.current_line + page_size).min(self.lines.len().saturating_sub(1));
117
118 if self.current_line >= self.scroll_offset + visible_lines {
119 self.scroll_offset = self.current_line - visible_lines + 1;
120 }
121 }
122
123 fn render(&self, canvas: &mut Framebuffer) {
124 canvas.set_named_border(
126 &format!("File Reader - {}", self.file_path),
127 Align::Center,
128 Attr::NORMAL,
129 Color::White,
130 Color::default(),
131 );
132
133 let content_width = canvas.width.saturating_sub(4);
135 let content_height = canvas.height.saturating_sub(4);
136 let start_x = 2;
137 let start_y = 2;
138
139 for (i, line_idx) in (self.scroll_offset..self.scroll_offset + content_height).enumerate() {
141 if line_idx >= self.lines.len() {
142 break;
143 }
144
145 let line = &self.lines[line_idx];
146 let is_current = line_idx == self.current_line;
147
148 let (attrs, fg, bg) = if is_current {
150 (Attr::NORMAL, Color::Black, Color::White)
151 } else {
152 (Attr::NORMAL, Color::White, Color::default())
153 };
154
155 let line_number = format!("{:4} ", line_idx + 1);
157 canvas.set_str(
158 start_x,
159 start_y + i,
160 &line_number,
161 attrs,
162 Color::Cyan,
163 bg,
164 Align::Left,
165 );
166
167 let display_line = if line.len() > content_width - 5 {
169 format!("{}...", &line[..content_width - 8])
170 } else {
171 line.clone()
172 };
173
174 canvas.set_str(
175 start_x + 5,
176 start_y + i,
177 &display_line,
178 attrs,
179 fg,
180 bg,
181 Align::Left,
182 );
183 }
184
185 let status = format!(
187 "Line {}/{} | Press ↑↓ to scroll, PgUp/PgDn for pages, 'q' to quit",
188 self.current_line + 1,
189 self.lines.len()
190 );
191
192 canvas.set_str(
193 canvas.width / 2,
194 canvas.height - 1,
195 &status,
196 Attr::NORMAL,
197 Color::Yellow,
198 Color::default(),
199 Align::Center,
200 );
201 }
202}