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