1use std::cell::Cell;
75use std::fmt;
76use std::rc::Rc;
77
78pub struct CodeWriter<W> {
81 writer: W,
82 indent_level: Rc<Cell<usize>>,
83 indent_string: String,
84 at_line_start: Cell<bool>,
85}
86
87impl<W: fmt::Write> CodeWriter<W> {
88 pub fn new(writer: W, indent_string: String) -> Self {
90 Self {
91 writer,
92 indent_level: Rc::new(Cell::new(0)),
93 indent_string,
94 at_line_start: Cell::new(true),
95 }
96 }
97
98 pub fn with_indent_spaces(writer: W, spaces: usize) -> Self {
100 Self::new(writer, " ".repeat(spaces))
101 }
102
103 pub fn write(&mut self, text: &str) -> fmt::Result {
105 if text.is_empty() {
106 return Ok(());
107 }
108
109 if self.at_line_start.get() && !text.trim().is_empty() {
110 for _ in 0..self.indent_level.get() {
111 self.writer.write_str(&self.indent_string)?;
112 }
113 self.at_line_start.set(false);
114 }
115
116 self.writer.write_str(text)
117 }
118
119 pub fn writeln(&mut self, text: &str) -> fmt::Result {
121 self.write(text)?;
122 self.writer.write_char('\n')?;
123 self.at_line_start.set(true);
124 Ok(())
125 }
126
127 pub fn blank_line(&mut self) -> fmt::Result {
129 self.writer.write_char('\n')?;
130 self.at_line_start.set(true);
131 Ok(())
132 }
133
134 pub fn indent(&mut self) -> IndentGuard {
136 self.indent_level.set(self.indent_level.get() + 1);
137 IndentGuard {
138 indent_level: Rc::clone(&self.indent_level),
139 }
140 }
141
142 pub fn comment(&mut self, comment_prefix: &str, text: &str) -> fmt::Result {
144 self.writeln(&format!("{} {}", comment_prefix, text))
145 }
146
147 pub fn doc_comment(&mut self, comment_prefix: &str, text: &str) -> fmt::Result {
149 for line in text.lines() {
150 self.writeln(&format!("{} {}", comment_prefix, line))?;
151 }
152 Ok(())
153 }
154
155 pub fn begin_block(&mut self, header: &str) -> Result<IndentGuard, fmt::Error> {
157 self.writeln(&format!("{} {{", header))?;
158 Ok(self.indent())
159 }
160
161 pub fn end_block(&mut self) -> fmt::Result {
163 self.writeln("}")
164 }
165
166 pub fn block<F>(&mut self, header: &str, body: F) -> fmt::Result
168 where
169 F: FnOnce(&mut Self) -> fmt::Result,
170 {
171 self.writeln(&format!("{} {{", header))?;
172 {
173 let _indent = self.indent();
174 body(self)?;
175 }
176 self.writeln("}")
177 }
178
179 pub fn indent_level(&self) -> usize {
181 self.indent_level.get()
182 }
183
184 pub fn into_inner(self) -> W {
186 self.writer
187 }
188
189 pub fn inner(&self) -> &W {
191 &self.writer
192 }
193
194 pub fn inner_mut(&mut self) -> &mut W {
196 &mut self.writer
197 }
198
199 #[doc(hidden)]
203 pub fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
204 let formatted = format!("{}", args);
205 self.write(&formatted)
206 }
207
208 #[doc(hidden)]
212 pub fn writeln_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
213 let formatted = format!("{}", args);
214 self.writeln(&formatted)
215 }
216
217 pub fn write_separated<I, F>(
219 &mut self,
220 items: I,
221 separator: &str,
222 mut write_item: F,
223 ) -> fmt::Result
224 where
225 I: IntoIterator,
226 F: FnMut(&mut Self, I::Item) -> fmt::Result,
227 {
228 let mut first = true;
229 for item in items {
230 if !first {
231 self.write(separator)?;
232 }
233 write_item(self, item)?;
234 first = false;
235 }
236 Ok(())
237 }
238
239 pub fn write_separated_lines<I, F>(
241 &mut self,
242 items: I,
243 separator: &str,
244 mut write_item: F,
245 ) -> fmt::Result
246 where
247 I: IntoIterator,
248 F: FnMut(&mut Self, I::Item) -> fmt::Result,
249 {
250 let mut first = true;
251 for item in items {
252 if !first {
253 self.writeln(separator)?;
254 }
255 write_item(self, item)?;
256 first = false;
257 }
258 Ok(())
259 }
260
261 pub fn write_if<F>(&mut self, condition: bool, f: F) -> fmt::Result
263 where
264 F: FnOnce(&mut Self) -> fmt::Result,
265 {
266 if condition { f(self) } else { Ok(()) }
267 }
268
269 pub fn write_parens<F>(&mut self, f: F) -> fmt::Result
271 where
272 F: FnOnce(&mut Self) -> fmt::Result,
273 {
274 self.write("(")?;
275 f(self)?;
276 self.write(")")
277 }
278
279 pub fn write_brackets<F>(&mut self, f: F) -> fmt::Result
281 where
282 F: FnOnce(&mut Self) -> fmt::Result,
283 {
284 self.write("[")?;
285 f(self)?;
286 self.write("]")
287 }
288
289 pub fn write_angles<F>(&mut self, f: F) -> fmt::Result
291 where
292 F: FnOnce(&mut Self) -> fmt::Result,
293 {
294 self.write("<")?;
295 f(self)?;
296 self.write(">")
297 }
298}
299
300pub struct IndentGuard {
304 indent_level: Rc<Cell<usize>>,
305}
306
307impl Drop for IndentGuard {
308 fn drop(&mut self) {
309 let current = self.indent_level.get();
310 self.indent_level.set(current.saturating_sub(1));
311 }
312}
313
314#[macro_export]
321macro_rules! cw_write {
322 ($writer:expr, $($arg:tt)*) => {
323 $writer.write_fmt(format_args!($($arg)*))
324 };
325}
326
327#[macro_export]
334macro_rules! cw_writeln {
335 ($writer:expr, $($arg:tt)*) => {
336 $writer.writeln_fmt(format_args!($($arg)*))
337 };
338}
339
340#[cfg(test)]
341mod tests {
342 use super::*;
343
344 #[test]
345 fn test_basic_writing() {
346 let mut output = String::new();
347 let mut w = CodeWriter::with_indent_spaces(&mut output, 2);
348
349 w.writeln("hello").unwrap();
350 w.writeln("world").unwrap();
351
352 assert_eq!(output, "hello\nworld\n");
353 }
354
355 #[test]
356 fn test_indentation() {
357 let mut output = String::new();
358 let mut w = CodeWriter::with_indent_spaces(&mut output, 2);
359
360 w.writeln("level 0").unwrap();
361 {
362 let _indent = w.indent();
363 w.writeln("level 1").unwrap();
364 {
365 let _indent = w.indent();
366 w.writeln("level 2").unwrap();
367 }
368 w.writeln("level 1 again").unwrap();
369 }
370 w.writeln("level 0 again").unwrap();
371
372 assert_eq!(
373 output,
374 "level 0\n level 1\n level 2\n level 1 again\nlevel 0 again\n"
375 );
376 }
377
378 #[test]
379 fn test_block_helper() {
380 let mut output = String::new();
381 let mut w = CodeWriter::with_indent_spaces(&mut output, 2);
382
383 w.block("class Foo", |w| {
384 w.writeln("let x = 42")?;
385 w.block("func bar()", |w| w.writeln("return x"))
386 })
387 .unwrap();
388
389 assert_eq!(
390 output,
391 "class Foo {\n let x = 42\n func bar() {\n return x\n }\n}\n"
392 );
393 }
394
395 #[test]
396 fn test_begin_end_block() {
397 let mut output = String::new();
398 let mut w = CodeWriter::with_indent_spaces(&mut output, 2);
399
400 w.writeln("before").unwrap();
401 {
402 let _guard = w.begin_block("if true").unwrap();
403 w.writeln("inside").unwrap();
404 }
405 w.end_block().unwrap();
406 w.writeln("after").unwrap();
407
408 assert_eq!(output, "before\nif true {\n inside\n}\nafter\n");
409 }
410
411 #[test]
412 fn test_comments() {
413 let mut output = String::new();
414 let mut w = CodeWriter::with_indent_spaces(&mut output, 2);
415
416 w.comment("//", "Single line comment").unwrap();
417 w.doc_comment("///", "Doc comment\nwith multiple lines")
418 .unwrap();
419
420 assert_eq!(
421 output,
422 "// Single line comment\n/// Doc comment\n/// with multiple lines\n"
423 );
424 }
425
426 #[test]
427 fn test_blank_lines() {
428 let mut output = String::new();
429 let mut w = CodeWriter::with_indent_spaces(&mut output, 2);
430
431 w.writeln("line 1").unwrap();
432 w.blank_line().unwrap();
433 w.writeln("line 2").unwrap();
434
435 assert_eq!(output, "line 1\n\nline 2\n");
436 }
437
438 #[test]
439 fn test_write_separated() {
440 let mut output = String::new();
441 let mut w = CodeWriter::with_indent_spaces(&mut output, 2);
442
443 let items = vec!["a", "b", "c"];
444 w.write_separated(items, ", ", |w, item| w.write(item))
445 .unwrap();
446
447 assert_eq!(output, "a, b, c");
448 }
449
450 #[test]
451 fn test_write_separated_lines() {
452 let mut output = String::new();
453 let mut w = CodeWriter::with_indent_spaces(&mut output, 2);
454
455 w.writeln("items:").unwrap();
456 {
457 let _indent = w.indent();
458 let items = vec!["first", "second", "third"];
459 w.write_separated_lines(items, ",", |w, item| w.write(item))
460 .unwrap();
461 }
462
463 assert_eq!(output, "items:\n first,\n second,\n third");
464 }
465
466 #[test]
467 fn test_write_parens() {
468 let mut output = String::new();
469 let mut w = CodeWriter::with_indent_spaces(&mut output, 2);
470
471 w.write("func").unwrap();
472 w.write_parens(|w| {
473 w.write_separated(vec!["a: Int", "b: String"], ", ", |w, item| w.write(item))
474 })
475 .unwrap();
476
477 assert_eq!(output, "func(a: Int, b: String)");
478 }
479
480 #[test]
481 fn test_write_if() {
482 let mut output = String::new();
483 let mut w = CodeWriter::with_indent_spaces(&mut output, 2);
484
485 w.write_if(true, |w| w.writeln("shown")).unwrap();
486 w.write_if(false, |w| w.writeln("hidden")).unwrap();
487 w.write_if(true, |w| w.writeln("also shown")).unwrap();
488
489 assert_eq!(output, "shown\nalso shown\n");
490 }
491
492 #[test]
493 fn test_write_fmt() {
494 let mut output = String::new();
495 let mut w = CodeWriter::with_indent_spaces(&mut output, 2);
496
497 let name = "test";
498 let value = 42;
499 w.write_fmt(format_args!("let {} = {}", name, value))
500 .unwrap();
501
502 assert_eq!(output, "let test = 42");
503 }
504
505 #[test]
506 fn test_complex_code_generation() {
507 let mut output = String::new();
508 let mut w = CodeWriter::with_indent_spaces(&mut output, 4);
509
510 w.doc_comment("///", "A sample class").unwrap();
511 w.block("class Example", |w| {
512 w.writeln("private let value: Int").unwrap();
513 w.blank_line().unwrap();
514 w.write("init")?;
515 w.write_parens(|w| w.write("value: Int"))?;
516 w.writeln(" {")?;
517 {
518 let _indent = w.indent();
519 w.writeln("self.value = value")?;
520 }
521 w.writeln("}")?;
522 w.blank_line()?;
523 w.block("func compute() -> Int", |w| w.writeln("return value * 2"))
524 })
525 .unwrap();
526
527 let expected = "\
528/// A sample class
529class Example {
530 private let value: Int
531
532 init(value: Int) {
533 self.value = value
534 }
535
536 func compute() -> Int {
537 return value * 2
538 }
539}
540";
541 assert_eq!(output, expected);
542 }
543
544 #[test]
545 fn test_macros() {
546 let mut output = String::new();
547 let mut w = CodeWriter::with_indent_spaces(&mut output, 2);
548
549 let name = "counter";
550 let value = 42;
551
552 cw_writeln!(w, "let {} = {}", name, value).unwrap();
553 cw_write!(w, "println!(\"value: {{}}\"").unwrap();
554 cw_writeln!(w, ", {})", name).unwrap();
555
556 assert_eq!(
557 output,
558 "let counter = 42\nprintln!(\"value: {}\", counter)\n"
559 );
560 }
561}