kotlin_poet_rs/spec/
code_block.rs1use std::fmt::{Display, Formatter};
2use std::str::FromStr;
3use crate::io::{CodeBuffer, RenderKotlin};
4use crate::tokens;
5use crate::util::{SemanticConversionError, yolo_from_str};
6
7#[derive(Debug, Clone)]
10pub(crate) enum CodeBlockNode {
11 Atom(String),
12 StaticAtom(&'static str),
13 Space,
14 NewLine,
15 Indent(usize),
16 Unindent(usize),
17}
18
19#[derive(Debug, Clone)]
21pub struct CodeBlock {
22 pub(crate) nodes: Vec<CodeBlockNode>,
23}
24
25impl CodeBlock {
26 pub fn empty() -> CodeBlock {
28 CodeBlock {
29 nodes: vec![],
30 }
31 }
32
33 pub fn atom(text: &str) -> CodeBlock {
36 CodeBlock {
37 nodes: vec![
38 CodeBlockNode::Atom(
39 text.to_string()
40 )
41 ],
42 }
43 }
44
45 pub fn statement(text: &str) -> CodeBlock {
47 let mut cb = CodeBlock::atom(text);
48 cb.push_new_line();
49 cb
50 }
51
52 pub fn push_statement(&mut self, text: &str) {
54 self.push_atom(text);
55 self.push_new_line();
56 }
57
58 pub fn push_renderable<T: RenderKotlin>(&mut self, renderable: &T) {
60 renderable.render_into(self);
61 }
62
63 pub fn push_indent(&mut self) {
66 if let Some(CodeBlockNode::Indent(last_value)) = self.nodes.last_mut() {
67 *last_value += 1;
68 return;
69 }
70 self.nodes.push(CodeBlockNode::Indent(1));
71 }
72
73 pub fn push_unindent(&mut self) {
76 if let Some(CodeBlockNode::Unindent(last_value)) = self.nodes.last_mut() {
77 *last_value += 1;
78 return;
79 }
80 self.nodes.push(CodeBlockNode::Unindent(1));
81 }
82
83 pub fn push_new_line(&mut self) {
85 self.nodes.push(CodeBlockNode::NewLine);
86 }
87
88 pub fn push_atom(&mut self, text: &str) {
90 if let Some(CodeBlockNode::Atom(inner_buffer)) = self.nodes.last_mut() {
91 inner_buffer.push_str(text);
92 return;
93 }
94 self.nodes.push(CodeBlockNode::Atom(text.to_string()));
95 }
96
97 pub(crate) fn push_static_atom(&mut self, text: &'static str) {
99 self.nodes.push(CodeBlockNode::StaticAtom(text));
100 }
101
102 pub fn push_space(&mut self) {
104 if matches!(self.nodes.last(), Some(CodeBlockNode::Space)) {
105 return; }
107 self.nodes.push(CodeBlockNode::Space);
108 }
109
110 pub fn pop_space(&mut self) {
112 if matches!(self.nodes.last(), Some(CodeBlockNode::Space)) {
113 self.nodes.remove(self.nodes.len() - 1);
114 }
115 }
116
117 pub fn push_curly_brackets<F>(&mut self, block: F)
119 where
120 F: FnOnce(&mut CodeBlock),
121 {
122 let mut inner_code = CodeBlock::empty();
123
124 self.push_static_atom(tokens::CURLY_BRACKET_LEFT);
125 self.push_new_line();
126 self.push_indent();
127 block(&mut inner_code);
128 self.push_renderable(&inner_code);
129 self.push_unindent();
130 self.push_static_atom(tokens::CURLY_BRACKET_RIGHT);
131 }
132
133 pub fn push_round_brackets<F>(&mut self, block: F)
135 where
136 F: FnOnce(&mut CodeBlock),
137 {
138 let mut inner_code = CodeBlock::empty();
139
140 self.push_static_atom(tokens::ROUND_BRACKET_LEFT);
141 block(&mut inner_code);
142 self.push_renderable(&inner_code);
143 self.push_static_atom(tokens::ROUND_BRACKET_RIGHT);
144 }
145
146 pub fn push_angle_brackets<F>(&mut self, block: F)
148 where
149 F: FnOnce(&mut CodeBlock),
150 {
151 let mut inner_code = CodeBlock::empty();
152
153 self.push_static_atom(tokens::ANGLE_BRACKET_LEFT);
154 block(&mut inner_code);
155 self.push_renderable(&inner_code);
156 self.push_static_atom(tokens::ANGLE_BRACKET_RIGHT);
157 }
158
159 pub fn push_comma_separated<F>(&mut self, elements: &[F])
161 where
162 F: RenderKotlin,
163 {
164 let mut code = CodeBlock::empty();
165 let len = elements.len();
166 for (index, renderable) in elements.iter().enumerate() {
167 code.push_renderable(renderable);
168 if index != len - 1 {
169 code.push_static_atom(tokens::COMMA);
170 code.push_space();
171 }
172 }
173
174 self.push_renderable(&code);
175 }
176
177 fn push_indent_into(indent: usize, root_buffer: &mut CodeBuffer) {
178 if matches!(root_buffer.last_char(), Some(tokens::NEW_LINE_CH)) {
179 for _ in 0..indent {
180 root_buffer.push(tokens::INDENT)
181 }
182 }
183 }
184
185 fn render(&self) -> String {
186 let mut root_buffer = CodeBuffer::default();
187 let mut indent = 0;
188
189 for node in &self.nodes {
190 match node {
191 CodeBlockNode::Atom(buffer) => {
192 Self::push_indent_into(indent, &mut root_buffer);
193 root_buffer.push(buffer.as_str());
194 }
195 CodeBlockNode::StaticAtom(buffer) => {
196 Self::push_indent_into(indent, &mut root_buffer);
197 root_buffer.push(buffer);
198 }
199 CodeBlockNode::Indent(size) => {
200 indent += size;
201 }
202 CodeBlockNode::Unindent(size) => {
203 if *size > indent {
204 indent = 0;
205 continue;
206 }
207 indent -= size;
208 }
209 CodeBlockNode::Space => {
210 root_buffer.push(tokens::SPACE)
211 }
212 CodeBlockNode::NewLine => {
213 root_buffer.push(tokens::NEW_LINE);
214 }
215 }
216 }
217
218 root_buffer.trim();
219 root_buffer.into_string()
220 }
221}
222
223impl Display for CodeBlock {
224 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
225 f.write_str(self.render().as_str())
226 }
227}
228
229yolo_from_str!(CodeBlock);
230impl FromStr for CodeBlock {
231 type Err = SemanticConversionError;
232
233 fn from_str(s: &str) -> Result<Self, Self::Err> {
234 Ok(CodeBlock::atom(s))
235 }
236}
237
238impl RenderKotlin for CodeBlock {
239 fn render_into(&self, block: &mut CodeBlock) {
240 block.nodes.extend(self.nodes.iter().cloned());
241 }
242}