1 #[cfg(feature = "alloc")] 2 use alloc::vec::Vec; 3 use core::fmt; 4 5 /// Helper struct to send console output to GDB. 6 /// 7 /// The recommended way to interact with `ConsoleOutput` is through the provided 8 /// [`output!`] and [`outputln!`] macros. 9 /// 10 /// On resource constrained systems which might want to avoid using Rust's 11 /// [fairly "heavy" formatting machinery](https://jamesmunns.com/blog/fmt-unreasonably-expensive/), 12 /// the `write_raw()` method can be used to write raw data directly to the GDB 13 /// console. 14 /// 15 /// When the `alloc` feature is disabled, all output buffering is disabled, and 16 /// each call to `output!` will automatically flush data over the Connection. 17 /// 18 /// [`output!`]: crate::output 19 /// [`outputln!`]: crate::outputln 20 // TODO: support user-provided output buffers for no-`alloc` environments. 21 pub struct ConsoleOutput<'a> { 22 #[cfg(feature = "alloc")] 23 buf: Vec<u8>, 24 callback: &'a mut dyn FnMut(&[u8]), 25 } 26 27 impl<'a> fmt::Write for ConsoleOutput<'a> { write_str(&mut self, s: &str) -> fmt::Result28 fn write_str(&mut self, s: &str) -> fmt::Result { 29 self.write_raw(s.as_bytes()); 30 Ok(()) 31 } 32 } 33 34 impl<'a> ConsoleOutput<'a> { new(callback: &'a mut dyn FnMut(&[u8])) -> ConsoleOutput<'a>35 pub(crate) fn new(callback: &'a mut dyn FnMut(&[u8])) -> ConsoleOutput<'a> { 36 ConsoleOutput { 37 #[cfg(feature = "alloc")] 38 buf: Vec::new(), 39 callback, 40 } 41 } 42 43 /// Write raw (non UTF-8) data to the GDB console. write_raw(&mut self, bytes: &[u8])44 pub fn write_raw(&mut self, bytes: &[u8]) { 45 cfg_if::cfg_if! { 46 if #[cfg(feature = "alloc")] { 47 self.buf.extend_from_slice(bytes); 48 } else { 49 (self.callback)(bytes); 50 } 51 } 52 } 53 54 /// Flush the internal output buffer. 55 /// 56 /// Only available when `alloc` is enabled. 57 #[cfg(feature = "alloc")] flush(&mut self)58 pub fn flush(&mut self) { 59 if !self.buf.is_empty() { 60 (self.callback)(&self.buf); 61 self.buf.clear() 62 } 63 } 64 } 65 66 impl Drop for ConsoleOutput<'_> { drop(&mut self)67 fn drop(&mut self) { 68 #[cfg(feature = "alloc")] 69 self.flush() 70 } 71 } 72 73 /// Send formatted data to the GDB client console. 74 /// 75 /// The first argument must be a [`ConsoleOutput`]. 76 #[macro_export] 77 macro_rules! output { 78 ($console_output:expr, $($args:tt)*) => {{ 79 use core::fmt::Write; 80 let _ = write!($console_output, $($args)*); 81 }}; 82 } 83 84 /// Send formatted data to the GDB client console, with a newline appended. 85 /// 86 /// The first argument must be a [`ConsoleOutput`]. 87 #[macro_export] 88 macro_rules! outputln { 89 ($console_output:expr) => {{ 90 use core::fmt::Write; 91 let _ = writeln!($console_output); 92 }}; 93 ($console_output:expr,) => { 94 outputln!($console_output) 95 }; 96 ($console_output:expr, $($args:tt)*) => {{ 97 use core::fmt::Write; 98 let _ = writeln!($console_output, $($args)*); 99 }}; 100 } 101