1use std::borrow::Cow;
2use std::cell::RefCell;
3use std::io;
4use std::io::Write;
5use std::rc::Rc;
6
7pub struct CodeBuffer {
13 indent: Cow<'static, str>,
14 content: RefCell<Vec<CodeBufferContent>>,
15}
16
17impl CodeBuffer {
18 pub const fn new() -> Self {
20 Self::with_indent(Cow::Borrowed(""))
21 }
22
23 #[inline]
25 pub fn newline(&self) {
26 self.line(Cow::Borrowed(""))
27 }
28
29 pub fn line(&self, text: impl Into<Cow<'static, str>>) {
31 let mut content = self.content.borrow_mut();
32 content.push(CodeBufferContent::String(text.into(), true));
33 }
34
35 pub fn text(&self, text: impl Into<Cow<'static, str>>) {
37 let mut content = self.content.borrow_mut();
38 content.push(CodeBufferContent::String(text.into(), false));
39 }
40
41 #[inline]
43 pub fn indent(&self, indent: Cow<'static, str>) -> Rc<CodeBuffer> {
44 self.indent_with_options(IndentOptions {
45 indent,
46 leading: None,
47 trailing: None,
48 trailing_newline: false,
49 })
50 }
51
52 pub fn indent_with_options(&self, options: IndentOptions) -> Rc<CodeBuffer> {
54 let mut content = self.content.borrow_mut();
55 if let Some(leading) = options.leading {
56 content.push(CodeBufferContent::String(leading, true));
57 }
58
59 let idx = content.len();
60 content.push(CodeBufferContent::Buffer(Rc::new(CodeBuffer::with_indent(
61 Cow::Owned(format!("{}{}", self.indent, options.indent)),
62 ))));
63
64 if let Some(trailing) = options.trailing {
65 content.push(CodeBufferContent::String(
66 trailing,
67 options.trailing_newline,
68 ));
69 } else if options.trailing_newline {
70 content.push(CodeBufferContent::String(Cow::Borrowed(""), true));
71 }
72
73 content[idx].as_buffer()
74 }
75
76 #[inline]
78 pub fn section(&self, trailing_newline: bool) -> Rc<CodeBuffer> {
79 self.indent_with_options(IndentOptions {
80 indent: Cow::Borrowed(""),
81 leading: None,
82 trailing: None,
83 trailing_newline,
84 })
85 }
86
87 pub fn write(self, writer: &mut dyn io::Write) -> io::Result<()> {
89 self.inner_write(&mut IndentedWriter::new(writer))
90 }
91
92 fn inner_write(&self, writer: &mut IndentedWriter) -> io::Result<()> {
93 writer.with_indent(&self.indent, move |writer| {
94 for item in self.content.borrow().iter() {
95 item.write(writer)?;
96 }
97 Ok(())
98 })
99 }
100}
101
102impl CodeBuffer {
103 const fn with_indent(indent: Cow<'static, str>) -> Self {
104 Self {
105 indent,
106 content: RefCell::new(Vec::new()),
107 }
108 }
109}
110
111impl Default for CodeBuffer {
112 #[inline(always)]
113 fn default() -> Self {
114 Self::new()
115 }
116}
117
118pub struct IndentOptions {
120 pub indent: Cow<'static, str>,
122 pub leading: Option<Cow<'static, str>>,
125 pub trailing: Option<Cow<'static, str>>,
127 pub trailing_newline: bool,
130}
131
132enum CodeBufferContent {
133 String(Cow<'static, str>, bool),
134 Buffer(Rc<CodeBuffer>),
135}
136
137impl CodeBufferContent {
138 fn as_buffer(&self) -> Rc<CodeBuffer> {
139 match self {
140 Self::Buffer(buffer) => buffer.clone(),
141 _ => unreachable!("expected a buffer"),
142 }
143 }
144
145 fn write(&self, writer: &mut IndentedWriter) -> io::Result<()> {
146 match self {
147 Self::String(string, newline) => {
148 if *newline {
149 writeln!(writer, "{string}")
150 } else {
151 write!(writer, "{string}")
152 }
153 }
154 Self::Buffer(buffer) => buffer.inner_write(writer),
155 }
156 }
157}
158
159struct IndentedWriter<'a> {
160 indent: &'a [u8],
161 writer: &'a mut dyn io::Write,
162
163 after_newline: bool,
164}
165
166impl<'a> IndentedWriter<'a> {
167 fn new(writer: &'a mut dyn io::Write) -> Self {
168 Self {
169 indent: &[],
170 writer,
171 after_newline: true,
172 }
173 }
174
175 fn with_indent(
176 &mut self,
177 indent: &str,
178 cb: impl FnOnce(&mut IndentedWriter<'_>) -> io::Result<()>,
179 ) -> io::Result<()> {
180 let mut delegate = IndentedWriter {
181 indent: indent.as_bytes(),
182 writer: self.writer,
183 after_newline: self.after_newline,
184 };
185
186 let result = cb(&mut delegate);
187 self.after_newline = delegate.after_newline;
188 result
189 }
190}
191
192impl io::Write for IndentedWriter<'_> {
193 fn write(&mut self, mut buf: &[u8]) -> io::Result<usize> {
194 if self.indent.is_empty() {
196 return self.writer.write(buf);
197 }
198
199 let mut written = 0;
200 while !buf.is_empty() {
201 let (slice, nl) = match buf.iter().position(|&b| b == b'\n') {
202 None => (buf, false),
203 Some(idx) => (&buf[..=idx], true),
204 };
205 debug_assert!(!slice.is_empty(), "the slice cannot be empty");
206
207 if self.after_newline && (slice.len() > 1 || !nl) {
208 self.writer.write_all(self.indent)?;
210 self.after_newline = false;
211 }
212
213 let out = self.writer.write(slice)?;
214 written += out;
215
216 if out < slice.len() {
217 break;
219 }
220
221 self.after_newline = nl;
222 buf = &buf[slice.len()..];
223 }
224
225 Ok(written)
226 }
227
228 #[inline]
229 fn flush(&mut self) -> io::Result<()> {
230 self.writer.flush()
231 }
232}