1use core::fmt::{self, Write};
10
11#[derive(Clone, Copy, Debug, Default)]
13pub struct ConsoleSink;
14
15impl Write for ConsoleSink {
16 fn write_str(&mut self, s: &str) -> fmt::Result {
17 kprint::kprint!("{:s}", s);
18 Ok(())
19 }
20}
21
22#[macro_export]
31macro_rules! emit {
32 ($printer:expr, $($args:tt)*) => {
33 $printer.emit(core::format_args!($($args)*))
34 };
35}
36
37pub struct DepthPrinter<W: Write> {
41 writer: W,
42 depth: usize,
43 list_max: usize,
44 list_emitted: usize,
45 in_list: bool,
46}
47
48impl<W: Write> DepthPrinter<W> {
49 pub fn new(writer: W, depth: usize) -> Self {
51 Self { writer, depth, list_max: 0, list_emitted: 0, in_list: false }
52 }
53
54 pub fn emit(&mut self, args: fmt::Arguments<'_>) {
60 if self.in_list {
61 self.list_emitted += 1;
62 if self.list_emitted > self.list_max {
63 return;
64 }
65 }
66 self.print_depth();
67 let _ = self.writer.write_fmt(args);
68 let _ = self.writer.write_str("\n");
69 }
70
71 pub fn begin_list(&mut self, max: usize) {
75 debug_assert!(!self.in_list);
76 self.list_max = max;
77 self.list_emitted = 0;
78 self.in_list = true;
79 }
80
81 pub fn end_list(&mut self) {
84 debug_assert!(self.in_list);
85 self.in_list = false;
86 if self.list_emitted > self.list_max {
87 let not_emitted = self.list_emitted - self.list_max;
88 crate::emit!(self, "[{} items not emitted]", not_emitted);
89 }
90 }
91
92 fn print_depth(&mut self) {
93 for _ in 0..self.depth {
94 let _ = self.writer.write_str(" ");
95 }
96 }
97}
98
99impl DepthPrinter<ConsoleSink> {
100 pub fn console(depth: usize) -> Self {
102 Self::new(ConsoleSink, depth)
103 }
104}
105
106impl<W: Write> Drop for DepthPrinter<W> {
107 fn drop(&mut self) {
108 debug_assert!(!self.in_list);
109 }
110}
111
112#[cfg(ktest)]
114#[unittest::suite(name = "depth_printer_rust")]
115mod tests {
116 use crate::DepthPrinter;
117 use core::cell::RefCell;
118 use core::fmt;
119 use fbl::StringBuffer;
120 use unittest::{expect_eq, expect_true};
121
122 struct StringSink {
125 buffer: RefCell<StringBuffer<2048>>,
126 }
127
128 impl StringSink {
129 fn new() -> Self {
130 Self { buffer: RefCell::new(StringBuffer::new()) }
131 }
132 }
133
134 impl fmt::Write for &StringSink {
135 fn write_str(&mut self, s: &str) -> fmt::Result {
136 self.buffer.borrow_mut().write_str(s)
137 }
138 }
139
140 #[test]
142 fn depth_printer_smoke_test() {
143 const EXPECTED: &[u8] = concat!(
144 "Hello\n",
145 " World\n",
146 "One\n",
147 "Two\n",
148 "[2 items not emitted]\n",
149 " One\n",
150 " Two\n",
151 " Goodbye\n",
152 "foo bar baz\n",
153 )
154 .as_bytes();
155
156 let buffer = StringSink::new();
157
158 let mut no_depth = DepthPrinter::new(&buffer, 0);
159 let mut depth = DepthPrinter::new(&buffer, 4);
160
161 emit!(no_depth, "Hello");
162 emit!(depth, "World");
163 no_depth.begin_list(2);
164 emit!(no_depth, "One");
165 emit!(no_depth, "Two");
166 emit!(no_depth, "Three");
167 emit!(no_depth, "Four");
168 no_depth.end_list();
169 depth.begin_list(4);
170 emit!(depth, "One");
171 emit!(depth, "Two");
172 depth.end_list();
173 emit!(depth, "Goodbye");
174 emit!(no_depth, "foo {} baz", "bar");
175
176 let output = buffer.buffer.borrow();
177 expect_eq!(EXPECTED.len(), output.len());
178 expect_true!(EXPECTED == &output[..]);
179 }
180}