spacetimedb_codegen/
code_indenter.rs1use std::fmt;
2use std::ops::{Deref, DerefMut};
3
4pub(super) type Indenter = CodeIndenter<String>;
5
6#[macro_export]
7macro_rules! indent_scope {
8 ($x:ident) => {
9 let mut $x = $x.indented(1);
10 };
11}
12
13pub struct CodeIndenter<W: fmt::Write> {
14 writer: W,
15 level: u32,
16 needs_indenting: bool,
17 indent: &'static str,
18}
19impl<W: fmt::Write> CodeIndenter<W> {
20 pub fn new(writer: W, indent: &'static str) -> Self {
21 CodeIndenter {
22 writer,
23 level: 0,
24 needs_indenting: true,
25 indent,
26 }
27 }
28 pub fn into_inner(self) -> W {
35 self.writer
36 }
37 pub fn indent(&mut self, n: u32) {
38 self.level = self.level.saturating_add(n);
39 }
40 pub fn dedent(&mut self, n: u32) {
41 self.level = self.level.saturating_sub(n);
42 }
43 pub fn indented(&mut self, n: u32) -> IndentScope<'_, W> {
44 self.indent(n);
45 IndentScope { fmt: self }
46 }
47 fn write_indent(&mut self) -> fmt::Result {
48 for _ in 0..self.level {
49 self.writer.write_str(self.indent)?;
50 }
51 Ok(())
52 }
53
54 pub fn with_indent<Res>(&mut self, f: impl FnOnce(&mut Self) -> Res) -> Res {
56 let mut indenter = self.indented(1);
57 f(&mut indenter)
58 }
59
60 pub fn newline(&mut self) {
64 self.writer.write_char('\n').unwrap();
65 }
66
67 pub fn delimited_block<Res>(&mut self, before: &str, f: impl FnOnce(&mut Self) -> Res, after: &str) -> Res {
69 self.writer.write_str(before).unwrap();
70 let res = self.with_indent(|out| {
71 out.newline();
72 out.write_indent().unwrap();
76 f(out)
77 });
78 self.writer.write_str(after).unwrap();
79 res
80 }
81}
82impl<W: fmt::Write> fmt::Write for CodeIndenter<W> {
83 fn write_str(&mut self, s: &str) -> fmt::Result {
84 for (i, line) in s.split_inclusive('\n').enumerate() {
85 let write_indent = i != 0 || std::mem::take(&mut self.needs_indenting);
86 if write_indent && line != "\n" {
88 self.write_indent()?;
89 }
90 self.writer.write_str(line)?;
91 }
92 self.needs_indenting = s.ends_with('\n');
93 Ok(())
94 }
95}
96impl CodeIndenter<String> {
97 pub fn write_fmt(&mut self, args: fmt::Arguments) {
101 fmt::Write::write_fmt(self, args).unwrap()
102 }
103}
104pub struct IndentScope<'a, W: fmt::Write> {
105 fmt: &'a mut CodeIndenter<W>,
106}
107impl<W: fmt::Write> Drop for IndentScope<'_, W> {
108 fn drop(&mut self) {
109 self.fmt.dedent(1);
110 }
111}
112impl<T: fmt::Write> Deref for IndentScope<'_, T> {
113 type Target = CodeIndenter<T>;
114 fn deref(&self) -> &Self::Target {
115 self.fmt
116 }
117}
118impl<T: fmt::Write> DerefMut for IndentScope<'_, T> {
119 fn deref_mut(&mut self) -> &mut Self::Target {
120 self.fmt
121 }
122}