Skip to main content

code_gen/common/
code_buffer.rs

1use std::fmt::{Display, Formatter};
2
3/// Responsible for buffering code.
4#[derive(Clone, Debug)]
5pub struct CodeBuffer {
6    indent: String,
7    line_ending: String,
8    code: String,
9}
10
11impl CodeBuffer {
12    //! Constants
13
14    /// The default indent. (4 spaces)
15    pub const DEFAULT_INDENT: &str = "    ";
16
17    /// The default line-ending.
18    pub const DEFAULT_LINE_ENDING: &str = "\n";
19
20    /// The default buffer capacity. (4 KiB)
21    pub const DEFAULT_CAPACITY: usize = 4 * 1024;
22}
23
24impl CodeBuffer {
25    //! Construction
26
27    /// Creates a new code buffer.
28    pub fn new<S0, S1>(indent: S0, line_ending: S1, capacity: usize) -> Self
29    where
30        S0: Into<String>,
31        S1: Into<String>,
32    {
33        Self {
34            indent: indent.into(),
35            line_ending: line_ending.into(),
36            code: String::with_capacity(capacity),
37        }
38    }
39
40    /// Creates a new code buffer with the default indent and line-ending and the given `capacity`.
41    #[must_use]
42    pub fn with_capacity(capacity: usize) -> Self {
43        Self::new(Self::DEFAULT_INDENT, Self::DEFAULT_LINE_ENDING, capacity)
44    }
45}
46
47impl Default for CodeBuffer {
48    fn default() -> Self {
49        Self::new(
50            Self::DEFAULT_INDENT,
51            Self::DEFAULT_LINE_ENDING,
52            Self::DEFAULT_CAPACITY,
53        )
54    }
55}
56
57impl From<CodeBuffer> for String {
58    fn from(buffer: CodeBuffer) -> Self {
59        buffer.code
60    }
61}
62
63impl CodeBuffer {
64    //! Access
65
66    /// Gets the length of the buffered code in bytes.
67    #[must_use]
68    pub fn len(&self) -> usize {
69        self.code.len()
70    }
71
72    /// Checks if the buffered code is empty.
73    #[must_use]
74    pub fn is_empty(&self) -> bool {
75        self.code.is_empty()
76    }
77
78    /// Peeks at the buffered code.
79    #[must_use]
80    pub fn peek(&self) -> &str {
81        self.code.as_str()
82    }
83
84    /// Clears the buffered code.
85    pub fn clear(&mut self) {
86        self.code.clear();
87    }
88}
89
90impl CodeBuffer {
91    //! Writing
92
93    /// Writes the `code`.
94    pub fn write(&mut self, code: &str) {
95        self.code.push_str(code);
96    }
97
98    /// Writes the indent `level`.
99    pub fn indent(&mut self, level: usize) {
100        for _ in 0..level {
101            self.code.push_str(self.indent.as_str());
102        }
103    }
104
105    /// Writes a line-ending.
106    pub fn end_line(&mut self) {
107        self.code.push_str(self.line_ending.as_str());
108    }
109
110    /// Writes a line of `code` at the indent `level` with a line-ending.
111    pub fn line(&mut self, level: usize, code: &str) {
112        self.indent(level);
113        self.write(code);
114        self.end_line();
115    }
116
117    /// Writes a single character.
118    pub fn push(&mut self, c: char) {
119        self.code.push(c);
120    }
121
122    /// Writes a single space.
123    pub fn space(&mut self) {
124        self.code.push(' ');
125    }
126}
127
128impl Display for CodeBuffer {
129    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
130        f.write_str(self.peek())
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137
138    #[test]
139    fn default_is_empty() {
140        let b = CodeBuffer::default();
141        assert!(b.is_empty());
142        assert_eq!(b.len(), 0);
143        assert_eq!(b.peek(), "");
144    }
145
146    #[test]
147    fn write_and_push() {
148        let mut b = CodeBuffer::default();
149        b.write("hello");
150        b.push('!');
151        assert_eq!(b.peek(), "hello!");
152        assert_eq!(b.len(), 6);
153        assert!(!b.is_empty());
154    }
155
156    #[test]
157    fn indent_and_line() {
158        let mut b = CodeBuffer::default();
159        b.line(2, "code");
160        assert_eq!(b.peek(), "        code\n");
161    }
162
163    #[test]
164    fn space_and_end_line() {
165        let mut b = CodeBuffer::default();
166        b.write("a");
167        b.space();
168        b.write("b");
169        b.end_line();
170        assert_eq!(b.peek(), "a b\n");
171    }
172
173    #[test]
174    fn clear() {
175        let mut b = CodeBuffer::default();
176        b.write("stuff");
177        b.clear();
178        assert!(b.is_empty());
179        assert_eq!(b.peek(), "");
180    }
181
182    #[test]
183    fn into_string() {
184        let mut b = CodeBuffer::default();
185        b.write("code");
186        let s: String = b.into();
187        assert_eq!(s, "code");
188    }
189
190    #[test]
191    fn with_capacity() {
192        let b = CodeBuffer::with_capacity(128);
193        assert!(b.is_empty());
194    }
195
196    #[test]
197    fn custom_indent_and_line_ending() {
198        let mut b = CodeBuffer::new("\t", "\r\n", 64);
199        b.line(1, "code");
200        assert_eq!(b.peek(), "\tcode\r\n");
201    }
202
203    #[test]
204    fn display() {
205        let mut b = CodeBuffer::default();
206        b.write("hello");
207        assert_eq!(format!("{}", b), "hello");
208    }
209}