code_gen/common/
code_buffer.rs1use std::fmt::{Display, Formatter};
2
3#[derive(Clone, Debug)]
5pub struct CodeBuffer {
6 indent: String,
7 line_ending: String,
8 code: String,
9}
10
11impl CodeBuffer {
12 pub const DEFAULT_INDENT: &str = " ";
16
17 pub const DEFAULT_LINE_ENDING: &str = "\n";
19
20 pub const DEFAULT_CAPACITY: usize = 4 * 1024;
22}
23
24impl CodeBuffer {
25 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 #[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 #[must_use]
68 pub fn len(&self) -> usize {
69 self.code.len()
70 }
71
72 #[must_use]
74 pub fn is_empty(&self) -> bool {
75 self.code.is_empty()
76 }
77
78 #[must_use]
80 pub fn peek(&self) -> &str {
81 self.code.as_str()
82 }
83
84 pub fn clear(&mut self) {
86 self.code.clear();
87 }
88}
89
90impl CodeBuffer {
91 pub fn write(&mut self, code: &str) {
95 self.code.push_str(code);
96 }
97
98 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 pub fn end_line(&mut self) {
107 self.code.push_str(self.line_ending.as_str());
108 }
109
110 pub fn line(&mut self, level: usize, code: &str) {
112 self.indent(level);
113 self.write(code);
114 self.end_line();
115 }
116
117 pub fn push(&mut self, c: char) {
119 self.code.push(c);
120 }
121
122 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}