Skip to main content

alef_codegen/
builder.rs

1use std::fmt::Write;
2
3/// Builder for constructing Rust source files.
4#[derive(Debug, Default)]
5pub struct RustFileBuilder {
6    doc_header: Option<String>,
7    imports: Vec<String>,
8    items: Vec<String>,
9}
10
11impl RustFileBuilder {
12    pub fn new() -> Self {
13        Self::default()
14    }
15
16    /// Add a "DO NOT EDIT" header.
17    pub fn with_generated_header(mut self) -> Self {
18        self.doc_header = Some(
19            "// This file is auto-generated by alef. DO NOT EDIT.\n\
20             // Re-generate with: alef generate\n"
21                .to_string(),
22        );
23        self
24    }
25
26    /// Add a crate-level inner attribute (e.g., `#![allow(clippy::...)]`).
27    /// These are placed at the very top of the file, before imports.
28    pub fn add_inner_attribute(&mut self, attr: &str) {
29        // Append to doc_header since it comes first
30        if let Some(ref mut header) = self.doc_header {
31            header.push_str(&format!("#![{attr}]\n"));
32        } else {
33            self.doc_header = Some(format!("#![{attr}]\n"));
34        }
35    }
36
37    /// Add a use import line.
38    pub fn add_import(&mut self, import: &str) {
39        if !self.imports.iter().any(|i| i == import) {
40            self.imports.push(import.to_string());
41        }
42    }
43
44    /// Add a raw code item (struct, impl, function, etc.).
45    pub fn add_item(&mut self, item: &str) {
46        self.items.push(item.to_string());
47    }
48
49    /// Build the final source string.
50    pub fn build(&self) -> String {
51        let mut capacity = 256; // base header + imports + newlines
52        if let Some(header) = &self.doc_header {
53            capacity += header.len() + 1;
54        }
55        capacity += self.imports.iter().map(|i| i.len() + 6).sum::<usize>(); // "use ...;\n"
56        capacity += self.items.iter().map(|i| i.len() + 2).sum::<usize>(); // item + "\n\n"
57
58        let mut out = String::with_capacity(capacity);
59
60        if let Some(header) = &self.doc_header {
61            out.push_str(header);
62            out.push('\n');
63        }
64
65        if !self.imports.is_empty() {
66            for import in &self.imports {
67                writeln!(out, "use {import};").ok();
68            }
69            out.push('\n');
70        }
71
72        for (i, item) in self.items.iter().enumerate() {
73            out.push_str(item);
74            if i < self.items.len() - 1 {
75                out.push_str("\n\n");
76            }
77        }
78
79        if !out.ends_with('\n') {
80            out.push('\n');
81        }
82
83        out
84    }
85}
86
87/// Helper to build a struct with attributes.
88pub struct StructBuilder {
89    attrs: Vec<String>,
90    visibility: String,
91    name: String,
92    derives: Vec<String>,
93    fields: Vec<(String, String, Vec<String>, String)>, // (name, type, field_attrs, doc)
94}
95
96impl StructBuilder {
97    pub fn new(name: &str) -> Self {
98        Self {
99            attrs: vec![],
100            visibility: String::from("pub"),
101            name: name.to_string(),
102            derives: vec![],
103            fields: vec![],
104        }
105    }
106
107    pub fn add_attr(&mut self, attr: &str) -> &mut Self {
108        self.attrs.push(attr.to_string());
109        self
110    }
111
112    pub fn add_derive(&mut self, derive: &str) -> &mut Self {
113        self.derives.push(derive.to_string());
114        self
115    }
116
117    pub fn add_field(&mut self, name: &str, ty: &str, attrs: Vec<String>) -> &mut Self {
118        self.add_field_with_doc(name, ty, attrs, "")
119    }
120
121    pub fn add_field_with_doc(&mut self, name: &str, ty: &str, attrs: Vec<String>, doc: &str) -> &mut Self {
122        self.fields
123            .push((name.to_string(), ty.to_string(), attrs, doc.to_string()));
124        self
125    }
126
127    pub fn build(&self) -> String {
128        let mut capacity = 128; // structural overhead
129        capacity += self.derives.iter().map(|d| d.len() + 2).sum::<usize>(); // derive + comma
130        capacity += self.attrs.iter().map(|a| a.len() + 5).sum::<usize>(); // #[...]\n
131        capacity += self.name.len() + self.visibility.len() + 16; // struct declaration
132        capacity += self
133            .fields
134            .iter()
135            .map(|(n, t, attrs, doc)| {
136                n.len() + t.len() + 12 + doc.len() + 8 + attrs.iter().map(|a| a.len() + 5).sum::<usize>()
137            })
138            .sum::<usize>();
139
140        let mut out = String::with_capacity(capacity);
141
142        if !self.derives.is_empty() {
143            writeln!(out, "#[derive({})]", self.derives.join(", ")).ok();
144        }
145
146        for attr in &self.attrs {
147            writeln!(out, "#[{attr}]").ok();
148        }
149
150        writeln!(out, "{} struct {} {{", self.visibility, self.name).ok();
151
152        for (name, ty, attrs, doc) in &self.fields {
153            if !doc.is_empty() {
154                for line in doc.lines() {
155                    writeln!(out, "    /// {line}").ok();
156                }
157            }
158            for attr in attrs {
159                writeln!(out, "    #[{attr}]").ok();
160            }
161            writeln!(out, "    pub {name}: {ty},").ok();
162        }
163
164        write!(out, "}}").ok();
165        out
166    }
167}
168
169/// Helper to build an impl block.
170pub struct ImplBuilder {
171    target: String,
172    attrs: Vec<String>,
173    methods: Vec<String>,
174}
175
176impl ImplBuilder {
177    pub fn new(target: &str) -> Self {
178        Self {
179            target: target.to_string(),
180            attrs: vec![],
181            methods: vec![],
182        }
183    }
184
185    pub fn add_attr(&mut self, attr: &str) -> &mut Self {
186        self.attrs.push(attr.to_string());
187        self
188    }
189
190    pub fn add_method(&mut self, method: &str) -> &mut Self {
191        self.methods.push(method.to_string());
192        self
193    }
194
195    pub fn build(&self) -> String {
196        let mut capacity = 128; // structural overhead
197        capacity += self.target.len() + 10; // impl ... {}
198        capacity += self.attrs.iter().map(|a| a.len() + 5).sum::<usize>(); // #[...]\n
199        capacity += self.methods.iter().map(|m| m.len() + 4).sum::<usize>(); // indented + newlines
200
201        let mut out = String::with_capacity(capacity);
202
203        for attr in &self.attrs {
204            writeln!(out, "#[{attr}]").ok();
205        }
206
207        writeln!(out, "impl {} {{", self.target).ok();
208
209        for (i, method) in self.methods.iter().enumerate() {
210            // Indent each line of the method
211            for line in method.lines() {
212                if line.is_empty() {
213                    out.push('\n');
214                } else {
215                    writeln!(out, "    {line}").ok();
216                }
217            }
218            if i < self.methods.len() - 1 {
219                out.push('\n');
220            }
221        }
222
223        write!(out, "}}").ok();
224        out
225    }
226}