1#[derive(Debug, Default)]
3pub struct RustFileBuilder {
4 doc_header: Option<String>,
5 imports: Vec<String>,
6 items: Vec<String>,
7}
8
9impl RustFileBuilder {
10 pub fn new() -> Self {
11 Self::default()
12 }
13
14 pub fn with_generated_header(mut self) -> Self {
16 self.doc_header = Some(
17 "// This file is auto-generated by alef. DO NOT EDIT.\n\
18 // Re-generate with: alef generate\n"
19 .to_string(),
20 );
21 self
22 }
23
24 pub fn add_inner_attribute(&mut self, attr: &str) {
27 let rendered = crate::template_env::render(
29 "builders/inner_attribute.jinja",
30 minijinja::context! {
31 attr => attr,
32 },
33 );
34 if let Some(ref mut header) = self.doc_header {
35 header.push_str(&rendered);
36 } else {
37 self.doc_header = Some(rendered);
38 }
39 }
40
41 pub fn add_import(&mut self, import: &str) {
45 if !import.contains("::") && !import.contains('*') && !import.contains('{') {
48 return;
49 }
50 if !self.imports.iter().any(|i| i == import) {
51 self.imports.push(import.to_string());
52 }
53 }
54
55 pub fn add_item(&mut self, item: &str) {
57 self.items.push(item.to_string());
58 }
59
60 pub fn build(&self) -> String {
62 let mut capacity = 256; if let Some(header) = &self.doc_header {
64 capacity += header.len() + 1;
65 }
66 capacity += self.imports.iter().map(|i| i.len() + 6).sum::<usize>(); capacity += self.items.iter().map(|i| i.len() + 2).sum::<usize>(); let mut out = String::with_capacity(capacity);
70
71 if let Some(header) = &self.doc_header {
72 out.push_str(header);
73 out.push('\n');
74 }
75
76 if !self.imports.is_empty() {
77 for import in &self.imports {
78 out.push_str(&crate::template_env::render(
79 "builders/use_import.jinja",
80 minijinja::context! {
81 import => import,
82 },
83 ));
84 }
85 out.push('\n');
86 }
87
88 for (i, item) in self.items.iter().enumerate() {
89 out.push_str(item);
90 if i < self.items.len() - 1 {
91 out.push_str("\n\n");
92 }
93 }
94
95 if !out.ends_with('\n') {
96 out.push('\n');
97 }
98
99 out
100 }
101}
102
103pub struct StructBuilder {
105 attrs: Vec<String>,
106 visibility: String,
107 name: String,
108 derives: Vec<String>,
109 fields: Vec<(String, String, Vec<String>, String)>, }
111
112impl StructBuilder {
113 pub fn new(name: &str) -> Self {
114 Self {
115 attrs: vec![],
116 visibility: String::from("pub"),
117 name: name.to_string(),
118 derives: vec![],
119 fields: vec![],
120 }
121 }
122
123 pub fn add_attr(&mut self, attr: &str) -> &mut Self {
124 self.attrs.push(attr.to_string());
125 self
126 }
127
128 pub fn add_derive(&mut self, derive: &str) -> &mut Self {
129 if !self.derives.iter().any(|d| d == derive) {
130 self.derives.push(derive.to_string());
131 }
132 self
133 }
134
135 pub fn add_field(&mut self, name: &str, ty: &str, attrs: Vec<String>) -> &mut Self {
136 self.add_field_with_doc(name, ty, attrs, "")
137 }
138
139 pub fn add_field_with_doc(&mut self, name: &str, ty: &str, attrs: Vec<String>, doc: &str) -> &mut Self {
140 self.fields
141 .push((name.to_string(), ty.to_string(), attrs, doc.to_string()));
142 self
143 }
144
145 pub fn build(&self) -> String {
146 let mut capacity = 128; capacity += self.derives.iter().map(|d| d.len() + 2).sum::<usize>(); capacity += self.attrs.iter().map(|a| a.len() + 5).sum::<usize>(); capacity += self.name.len() + self.visibility.len() + 16; capacity += self
151 .fields
152 .iter()
153 .map(|(n, t, attrs, doc)| {
154 n.len() + t.len() + 12 + doc.len() + 8 + attrs.iter().map(|a| a.len() + 5).sum::<usize>()
155 })
156 .sum::<usize>();
157
158 let mut out = String::with_capacity(capacity);
159
160 if !self.derives.is_empty() {
161 let rendered = crate::template_env::render(
162 "builders/derive_attr.jinja",
163 minijinja::context! {
164 derives => self.derives.join(", "),
165 },
166 );
167 out.push_str(&rendered);
168 if !rendered.ends_with('\n') {
169 out.push('\n');
170 }
171 }
172
173 for attr in &self.attrs {
174 let rendered = crate::template_env::render(
175 "builders/generic_attr.jinja",
176 minijinja::context! {
177 attr => attr,
178 },
179 );
180 out.push_str(&rendered);
181 if !rendered.ends_with('\n') {
182 out.push('\n');
183 }
184 }
185
186 let rendered_header = crate::template_env::render(
187 "builders/struct_header.jinja",
188 minijinja::context! {
189 visibility => &self.visibility,
190 name => &self.name,
191 },
192 );
193 out.push_str(&rendered_header);
194 if !rendered_header.ends_with('\n') {
195 out.push('\n');
196 }
197
198 for (name, ty, attrs, doc) in &self.fields {
199 if !doc.is_empty() {
200 for line in doc.lines() {
201 let rendered = crate::template_env::render(
202 "builders/doc_line.jinja",
203 minijinja::context! {
204 line => line,
205 },
206 );
207 out.push_str(&rendered);
208 if !rendered.ends_with('\n') {
209 out.push('\n');
210 }
211 }
212 }
213 for attr in attrs {
214 let rendered = crate::template_env::render(
215 "builders/generic_attr.jinja",
216 minijinja::context! {
217 attr => attr,
218 },
219 );
220 out.push_str(&rendered);
221 if !rendered.ends_with('\n') {
222 out.push('\n');
223 }
224 }
225 let rendered_field = crate::template_env::render(
226 "builders/struct_field.jinja",
227 minijinja::context! {
228 name => name,
229 ty => ty,
230 },
231 );
232 out.push_str(&rendered_field);
233 if !rendered_field.ends_with('\n') {
234 out.push('\n');
235 }
236 }
237
238 out.push('}');
239 out
240 }
241}
242
243pub struct ImplBuilder {
245 target: String,
246 attrs: Vec<String>,
247 methods: Vec<String>,
248}
249
250impl ImplBuilder {
251 pub fn new(target: &str) -> Self {
252 Self {
253 target: target.to_string(),
254 attrs: vec![],
255 methods: vec![],
256 }
257 }
258
259 pub fn add_attr(&mut self, attr: &str) -> &mut Self {
260 self.attrs.push(attr.to_string());
261 self
262 }
263
264 pub fn add_method(&mut self, method: &str) -> &mut Self {
265 self.methods.push(method.to_string());
266 self
267 }
268
269 pub fn build(&self) -> String {
270 let mut capacity = 128; capacity += self.target.len() + 10; capacity += self.attrs.iter().map(|a| a.len() + 5).sum::<usize>(); capacity += self.methods.iter().map(|m| m.len() + 4).sum::<usize>(); let mut out = String::with_capacity(capacity);
276
277 for attr in &self.attrs {
278 let rendered = crate::template_env::render(
279 "builders/generic_attr.jinja",
280 minijinja::context! {
281 attr => attr,
282 },
283 );
284 out.push_str(&rendered);
285 if !rendered.ends_with('\n') {
286 out.push('\n');
287 }
288 }
289
290 let rendered_header = crate::template_env::render(
291 "builders/impl_header.jinja",
292 minijinja::context! {
293 target => &self.target,
294 },
295 );
296 out.push_str(&rendered_header);
297 if !rendered_header.ends_with('\n') {
298 out.push('\n');
299 }
300
301 for (i, method) in self.methods.iter().enumerate() {
302 for line in method.lines() {
304 if line.is_empty() {
305 out.push('\n');
306 } else {
307 let rendered = crate::template_env::render(
308 "builders/indented_line.jinja",
309 minijinja::context! {
310 line => line,
311 },
312 );
313 out.push_str(&rendered);
314 if !rendered.ends_with('\n') {
315 out.push('\n');
316 }
317 }
318 }
319 if i < self.methods.len() - 1 {
320 out.push('\n');
321 }
322 }
323
324 out.push('}');
325 out
326 }
327}