• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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