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