spacetimedb_codegen/
code_indenter.rs

1use 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 get_ref(&self) -> &W {
29    //     &self.writer
30    // }
31    // pub fn get_mut(&mut self) -> &mut W {
32    //     &mut self.writer
33    // }
34    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    /// Invoke `f` while indenting one level greater than `self` currently does.
55    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    // Writes a newline without setting the `needs_indenting` flag.
61    // TODO(cloutiertyler): I think it should set the flag, but I don't know
62    // if anyone is relying on the current behavior.
63    pub fn newline(&mut self) {
64        self.writer.write_char('\n').unwrap();
65    }
66
67    /// Print an indented block delimited by `before` and `after`, with body written by `f`.
68    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            // Need an explicit `write_indent` call here because calling `out.newline`
73            // will not cause the subsequent line to be indented, as `write_str` thinks
74            // it's an empty line.
75            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            // skip the indent if it's an empty line
87            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    // This method allows `write!` and `writeln!` to be used directly on `CodeIndenter`.
98    // It does the same thing as using it via the `fmt::Write` trait but in a non-fallible manner.
99    // This is only allowed on `String` because it's the only type that can't fail to write.
100    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}