1use core::fmt;
9
10use crate::IdentDisplay;
11use crate::dom::Event;
12
13#[derive(Clone, Debug, PartialEq, Eq, Hash)]
14pub struct Writer<W> {
16 inner: W,
17 state: State,
18 indent: usize,
19 indent_text: &'static str,
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
25enum State {
26 First,
27 BlockStart,
28 Block,
29 Node,
30}
31
32impl<W: fmt::Write> Writer<W> {
33 pub const fn new(writer: W) -> Self {
35 Self {
36 inner: writer,
37 state: State::First,
38 indent: 0,
39 indent_text: " ",
40 }
41 }
42 pub fn set_indent(&mut self, indent: &'static str) { self.indent_text = indent; }
45 fn line(&mut self) -> fmt::Result {
46 writeln!(self.inner)?;
47 for _ in 0..self.indent {
48 write!(self.inner, "{}", self.indent_text)?;
49 }
50 Ok(())
51 }
52 pub fn push(&mut self, event: &Event) -> fmt::Result {
56 match event {
57 Event::Node { r#type, name } => {
58 if self.state != State::First {
59 self.line()?;
60 }
61 self.state = State::Node;
62 if let Some(r#type) = r#type {
63 write!(self.inner, "({})", IdentDisplay(r#type))?;
64 }
65 write!(self.inner, "{}", IdentDisplay(name))
66 }
67 Event::Entry(entry) => write!(self.inner, " {entry}"),
68 Event::Children => {
69 self.indent += 1;
70 self.state = State::BlockStart;
71 write!(self.inner, " {{")
72 }
73 Event::End => {
74 match self.state {
75 State::BlockStart => {
76 self.indent -= 1;
77 write!(self.inner, "}}")?;
78 }
79 State::Block => {
80 self.indent -= 1;
81 self.line()?;
82 write!(self.inner, "}}")?;
83 }
84 _ => {}
85 }
86 self.state = State::Block;
87 Ok(())
88 }
89 }
90 }
91}
92
93#[cfg(feature = "std")]
95#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
96#[derive(Clone, Debug, PartialEq, Eq, Hash)]
97pub struct WriteOutput<T>(pub T);
98
99#[cfg(feature = "std")]
100#[expect(clippy::absolute_paths, reason = "feature-gated")]
101impl<T: std::io::Write> fmt::Write for WriteOutput<T> {
102 fn write_str(&mut self, s: &str) -> fmt::Result {
103 self.0.write_all(s.as_bytes()).map_err(|_| fmt::Error)
104 }
105}