writer.rs
1 use core::fmt; 2 use embedded_io_async::Write as AsyncWrite; 3 4 /// Terminal writer for formatted output with ANSI support 5 pub struct TerminalWriter<'a, W: AsyncWrite> { 6 writer: &'a mut W, 7 ansi_enabled: bool, 8 } 9 10 impl<'a, W: AsyncWrite> TerminalWriter<'a, W> { 11 /// Create a new terminal writer 12 pub fn new(writer: &'a mut W, ansi_enabled: bool) -> Self { 13 Self { 14 writer, 15 ansi_enabled, 16 } 17 } 18 19 /// Write a string 20 pub async fn write_str(&mut self, s: &str) -> Result<(), W::Error> { 21 self.writer.write_all(s.as_bytes()).await?; 22 self.writer.flush().await 23 } 24 25 /// Write a formatted string 26 pub async fn write_fmt( 27 &mut self, 28 args: fmt::Arguments<'_>, 29 ) -> Result<(), W::Error> { 30 // For no_std, we need to format to a temporary buffer 31 use heapless::String; 32 let mut buffer = String::<256>::new(); 33 let _ = fmt::write(&mut buffer, args); 34 self.write_str(&buffer).await 35 } 36 37 /// Write a line (adds \r\n) 38 pub async fn writeln(&mut self, s: &str) -> Result<(), W::Error> { 39 self.write_str(s).await?; 40 self.write_str("\r\n").await 41 } 42 43 /// Write the prompt 44 pub async fn write_prompt(&mut self, prompt: &str) -> Result<(), W::Error> { 45 self.write_str(prompt).await 46 } 47 48 /// Clear the current line 49 pub async fn clear_line(&mut self) -> Result<(), W::Error> { 50 if self.ansi_enabled { 51 // Move to start of line and clear 52 self.write_str("\r\x1b[K").await 53 } else { 54 // Just carriage return for simple terminals 55 self.write_str("\r").await 56 } 57 } 58 59 /// Clear the screen 60 pub async fn clear_screen(&mut self) -> Result<(), W::Error> { 61 if self.ansi_enabled { 62 self.write_str("\x1b[2J\x1b[H").await 63 } else { 64 // Send multiple newlines as fallback 65 self.write_str("\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n").await 66 } 67 } 68 69 /// Move cursor up by n lines 70 pub async fn cursor_up(&mut self, n: usize) -> Result<(), W::Error> { 71 if self.ansi_enabled && n > 0 { 72 use heapless::String; 73 let mut cmd = String::<16>::new(); 74 use core::fmt::Write; 75 write!(&mut cmd, "\x1b[{}A", n).ok(); 76 self.write_str(&cmd).await 77 } else { 78 Ok(()) 79 } 80 } 81 82 /// Move cursor down by n lines 83 pub async fn cursor_down(&mut self, n: usize) -> Result<(), W::Error> { 84 if self.ansi_enabled && n > 0 { 85 use heapless::String; 86 let mut cmd = String::<16>::new(); 87 use core::fmt::Write; 88 write!(&mut cmd, "\x1b[{}B", n).ok(); 89 self.write_str(&cmd).await 90 } else { 91 Ok(()) 92 } 93 } 94 95 /// Set text color (ANSI colors: 0-7 for basic colors, 8-15 for bright colors) 96 pub async fn set_color(&mut self, color: u8) -> Result<(), W::Error> { 97 if self.ansi_enabled { 98 use heapless::String; 99 let mut cmd = String::<16>::new(); 100 use core::fmt::Write; 101 if color < 8 { 102 write!(&mut cmd, "\x1b[3{}m", color).ok(); 103 } else { 104 write!(&mut cmd, "\x1b[9{}m", color - 8).ok(); 105 } 106 self.write_str(&cmd).await 107 } else { 108 Ok(()) 109 } 110 } 111 112 /// Reset text formatting 113 pub async fn reset_format(&mut self) -> Result<(), W::Error> { 114 if self.ansi_enabled { 115 self.write_str("\x1b[0m").await 116 } else { 117 Ok(()) 118 } 119 } 120 121 /// Set bold text 122 pub async fn set_bold(&mut self, enable: bool) -> Result<(), W::Error> { 123 if self.ansi_enabled { 124 if enable { 125 self.write_str("\x1b[1m").await 126 } else { 127 self.write_str("\x1b[22m").await 128 } 129 } else { 130 Ok(()) 131 } 132 } 133 134 /// Write colored text 135 pub async fn write_colored( 136 &mut self, 137 text: &str, 138 color: u8, 139 ) -> Result<(), W::Error> { 140 self.set_color(color).await?; 141 self.write_str(text).await?; 142 self.reset_format().await 143 } 144 145 /// Write an error message 146 pub async fn write_error(&mut self, msg: &str) -> Result<(), W::Error> { 147 if self.ansi_enabled { 148 self.write_colored(msg, 1).await // Red 149 } else { 150 self.writeln(msg).await 151 } 152 } 153 154 /// Write a success message 155 pub async fn write_success(&mut self, msg: &str) -> Result<(), W::Error> { 156 if self.ansi_enabled { 157 self.write_colored(msg, 2).await // Green 158 } else { 159 self.writeln(msg).await 160 } 161 } 162 163 /// Write a warning message 164 pub async fn write_warning(&mut self, msg: &str) -> Result<(), W::Error> { 165 if self.ansi_enabled { 166 self.write_colored(msg, 3).await // Yellow 167 } else { 168 self.writeln(msg).await 169 } 170 } 171 172 /// Write an info message 173 pub async fn write_info(&mut self, msg: &str) -> Result<(), W::Error> { 174 if self.ansi_enabled { 175 self.write_colored(msg, 6).await // Cyan 176 } else { 177 self.writeln(msg).await 178 } 179 } 180 181 /// Flush the writer 182 pub async fn flush(&mut self) -> Result<(), W::Error> { 183 self.writer.flush().await 184 } 185 } 186 187 /// ANSI color codes for convenience 188 pub mod colors { 189 pub const BLACK: u8 = 0; 190 pub const RED: u8 = 1; 191 pub const GREEN: u8 = 2; 192 pub const YELLOW: u8 = 3; 193 pub const BLUE: u8 = 4; 194 pub const MAGENTA: u8 = 5; 195 pub const CYAN: u8 = 6; 196 pub const WHITE: u8 = 7; 197 198 pub const BRIGHT_BLACK: u8 = 8; 199 pub const BRIGHT_RED: u8 = 9; 200 pub const BRIGHT_GREEN: u8 = 10; 201 pub const BRIGHT_YELLOW: u8 = 11; 202 pub const BRIGHT_BLUE: u8 = 12; 203 pub const BRIGHT_MAGENTA: u8 = 13; 204 pub const BRIGHT_CYAN: u8 = 14; 205 pub const BRIGHT_WHITE: u8 = 15; 206 }