1use crate::builder::StructBuilder;
2use crate::generators::RustBindingConfig;
3use crate::type_mapper::TypeMapper;
4use alef_core::ir::{TypeDef, TypeRef};
5use std::fmt::Write;
6
7pub fn can_generate_default_impl(typ: &TypeDef, known_default_types: &std::collections::HashSet<&str>) -> bool {
13 for field in &typ.fields {
14 if field.cfg.is_some() {
15 continue; }
17 if !field_type_has_default(&field.ty, known_default_types) {
18 return false;
19 }
20 }
21 true
22}
23
24fn field_type_has_default(ty: &TypeRef, known_default_types: &std::collections::HashSet<&str>) -> bool {
26 match ty {
27 TypeRef::Primitive(_)
28 | TypeRef::String
29 | TypeRef::Char
30 | TypeRef::Bytes
31 | TypeRef::Path
32 | TypeRef::Unit
33 | TypeRef::Duration
34 | TypeRef::Json => true,
35 TypeRef::Optional(inner) => field_type_has_default(inner, known_default_types),
37 TypeRef::Vec(inner) => field_type_has_default(inner, known_default_types),
39 TypeRef::Map(k, v) => {
41 field_type_has_default(k, known_default_types) && field_type_has_default(v, known_default_types)
42 }
43 TypeRef::Named(name) => known_default_types.contains(name.as_str()),
45 }
46}
47
48fn has_similar_names(names: &[&String]) -> bool {
51 for (i, &name1) in names.iter().enumerate() {
52 for &name2 in &names[i + 1..] {
53 if name1.len() == name2.len() && diff_count(name1, name2) <= 2 {
55 return true;
56 }
57 }
58 }
59 false
60}
61
62fn diff_count(s1: &str, s2: &str) -> usize {
64 s1.chars().zip(s2.chars()).filter(|(c1, c2)| c1 != c2).count()
65}
66
67pub fn field_references_opaque_type(ty: &TypeRef, opaque_names: &[String]) -> bool {
71 match ty {
72 TypeRef::Named(name) => opaque_names.contains(name),
73 TypeRef::Optional(inner) | TypeRef::Vec(inner) => field_references_opaque_type(inner, opaque_names),
74 TypeRef::Map(k, v) => {
75 field_references_opaque_type(k, opaque_names) || field_references_opaque_type(v, opaque_names)
76 }
77 _ => false,
78 }
79}
80
81pub fn gen_struct_with_per_field_attrs(
86 typ: &TypeDef,
87 mapper: &dyn TypeMapper,
88 cfg: &RustBindingConfig,
89 extra_field_attrs: impl Fn(&alef_core::ir::FieldDef) -> Vec<String>,
90) -> String {
91 let mut sb = StructBuilder::new(&typ.name);
92 for attr in cfg.struct_attrs {
93 sb.add_attr(attr);
94 }
95
96 let field_names: Vec<_> = typ.fields.iter().filter(|f| f.cfg.is_none()).map(|f| &f.name).collect();
98 if has_similar_names(&field_names) {
99 sb.add_attr("allow(clippy::similar_names)");
100 }
101
102 for d in cfg.struct_derives {
103 sb.add_derive(d);
104 }
105 let opaque_fields: Vec<&str> = typ
107 .fields
108 .iter()
109 .filter(|f| f.cfg.is_none() && field_references_opaque_type(&f.ty, cfg.opaque_type_names))
110 .map(|f| f.name.as_str())
111 .collect();
112 sb.add_derive("Default");
116 sb.add_derive("serde::Serialize");
117 sb.add_derive("serde::Deserialize");
118 let has_serde = true;
119 for field in &typ.fields {
120 if field.cfg.is_some() {
121 continue;
122 }
123 let force_optional = cfg.option_duration_on_defaults
124 && typ.has_default
125 && !field.optional
126 && matches!(field.ty, TypeRef::Duration);
127 let ty = if (field.optional || force_optional) && !matches!(field.ty, TypeRef::Optional(_)) {
128 mapper.optional(&mapper.map_type(&field.ty))
129 } else {
130 mapper.map_type(&field.ty)
132 };
133 let mut attrs: Vec<String> = cfg.field_attrs.iter().map(|a| a.to_string()).collect();
134 attrs.extend(extra_field_attrs(field));
135 if has_serde && opaque_fields.contains(&field.name.as_str()) {
137 attrs.push("serde(skip)".to_string());
138 }
139 sb.add_field_with_doc(&field.name, &ty, attrs, &field.doc);
140 }
141 sb.build()
142}
143
144pub fn gen_struct_with_rename(
161 typ: &TypeDef,
162 mapper: &dyn TypeMapper,
163 cfg: &RustBindingConfig,
164 extra_field_attrs: impl Fn(&alef_core::ir::FieldDef) -> Vec<String>,
165 field_name_override: impl Fn(&alef_core::ir::FieldDef) -> Option<String>,
166) -> String {
167 let mut sb = StructBuilder::new(&typ.name);
168 for attr in cfg.struct_attrs {
169 sb.add_attr(attr);
170 }
171
172 let field_names: Vec<_> = typ.fields.iter().filter(|f| f.cfg.is_none()).map(|f| &f.name).collect();
173 if has_similar_names(&field_names) {
174 sb.add_attr("allow(clippy::similar_names)");
175 }
176
177 for d in cfg.struct_derives {
178 sb.add_derive(d);
179 }
180 let opaque_fields: Vec<&str> = typ
181 .fields
182 .iter()
183 .filter(|f| f.cfg.is_none() && field_references_opaque_type(&f.ty, cfg.opaque_type_names))
184 .map(|f| f.name.as_str())
185 .collect();
186 sb.add_derive("Default");
187 sb.add_derive("serde::Serialize");
188 sb.add_derive("serde::Deserialize");
189 let has_serde = true;
190 for field in &typ.fields {
191 if field.cfg.is_some() {
192 continue;
193 }
194 let force_optional = cfg.option_duration_on_defaults
195 && typ.has_default
196 && !field.optional
197 && matches!(field.ty, TypeRef::Duration);
198 let ty = if (field.optional || force_optional) && !matches!(field.ty, TypeRef::Optional(_)) {
199 mapper.optional(&mapper.map_type(&field.ty))
200 } else {
201 mapper.map_type(&field.ty)
202 };
203 let name_override = field_name_override(field);
204 let extra_attrs = extra_field_attrs(field);
205 let mut attrs: Vec<String> = if name_override.is_some() && !extra_attrs.is_empty() {
209 extra_attrs
210 } else {
211 let mut a: Vec<String> = cfg.field_attrs.iter().map(|a| a.to_string()).collect();
212 a.extend(extra_attrs);
213 a
214 };
215 if has_serde && opaque_fields.contains(&field.name.as_str()) {
216 attrs.push("serde(skip)".to_string());
217 }
218 let emit_name = name_override.unwrap_or_else(|| field.name.clone());
219 sb.add_field_with_doc(&emit_name, &ty, attrs, &field.doc);
220 }
221 sb.build()
222}
223
224pub fn gen_struct(typ: &TypeDef, mapper: &dyn TypeMapper, cfg: &RustBindingConfig) -> String {
226 let mut sb = StructBuilder::new(&typ.name);
227 for attr in cfg.struct_attrs {
228 sb.add_attr(attr);
229 }
230
231 let field_names: Vec<_> = typ.fields.iter().filter(|f| f.cfg.is_none()).map(|f| &f.name).collect();
233 if has_similar_names(&field_names) {
234 sb.add_attr("allow(clippy::similar_names)");
235 }
236
237 for d in cfg.struct_derives {
238 sb.add_derive(d);
239 }
240 let _opaque_fields: Vec<&str> = typ
241 .fields
242 .iter()
243 .filter(|f| f.cfg.is_none() && field_references_opaque_type(&f.ty, cfg.opaque_type_names))
244 .map(|f| f.name.as_str())
245 .collect();
246 sb.add_derive("Default");
247 sb.add_derive("serde::Serialize");
248 sb.add_derive("serde::Deserialize");
249 let _has_serde = true;
250 for field in &typ.fields {
251 if field.cfg.is_some() {
255 continue;
256 }
257 let force_optional = cfg.option_duration_on_defaults
261 && typ.has_default
262 && !field.optional
263 && matches!(field.ty, TypeRef::Duration);
264 let ty = if (field.optional || force_optional) && !matches!(field.ty, TypeRef::Optional(_)) {
265 mapper.optional(&mapper.map_type(&field.ty))
266 } else {
267 mapper.map_type(&field.ty)
269 };
270 let attrs: Vec<String> = cfg.field_attrs.iter().map(|a| a.to_string()).collect();
271 sb.add_field_with_doc(&field.name, &ty, attrs, &field.doc);
277 }
278 sb.build()
279}
280
281pub fn gen_struct_default_impl(typ: &TypeDef, name_prefix: &str) -> String {
290 let full_name = format!("{}{}", name_prefix, typ.name);
291 let mut out = String::with_capacity(256);
292 writeln!(out, "impl Default for {} {{", full_name).ok();
293 writeln!(out, " fn default() -> Self {{").ok();
294 writeln!(out, " Self {{").ok();
295 for field in &typ.fields {
296 if field.cfg.is_some() {
297 continue;
298 }
299 let default_val = match &field.ty {
300 TypeRef::Optional(_) => "None".to_string(),
301 _ => "Default::default()".to_string(),
302 };
303 writeln!(out, " {}: {},", field.name, default_val).ok();
304 }
305 writeln!(out, " }}").ok();
306 writeln!(out, " }}").ok();
307 write!(out, "}}").ok();
308 out
309}
310
311pub fn type_needs_mutex(typ: &TypeDef) -> bool {
314 typ.methods
315 .iter()
316 .any(|m| m.receiver == Some(alef_core::ir::ReceiverKind::RefMut))
317}
318
319pub fn gen_opaque_struct(typ: &TypeDef, cfg: &RustBindingConfig) -> String {
329 let needs_mutex = type_needs_mutex(typ);
330 let core_path = typ.rust_path.replace('-', "_");
338 let has_unresolvable_generics = core_path.contains('<');
339 let all_methods_sanitized = !typ.methods.is_empty() && typ.methods.iter().all(|m| m.sanitized);
340 let omit_inner = all_methods_sanitized && has_unresolvable_generics;
341 let mut out = String::with_capacity(512);
342 if !cfg.struct_derives.is_empty() {
343 writeln!(out, "#[derive(Clone)]").ok();
344 }
345 for attr in cfg.struct_attrs {
346 writeln!(out, "#[{attr}]").ok();
347 }
348 writeln!(out, "pub struct {} {{", typ.name).ok();
349 if !omit_inner {
350 if typ.is_trait {
351 writeln!(out, " inner: Arc<dyn {core_path} + Send + Sync>,").ok();
352 } else if needs_mutex {
353 writeln!(out, " inner: Arc<std::sync::Mutex<{core_path}>>,").ok();
354 } else {
355 writeln!(out, " inner: Arc<{core_path}>,").ok();
356 }
357 }
358 write!(out, "}}").ok();
359 out
360}
361
362pub fn gen_opaque_struct_prefixed(typ: &TypeDef, cfg: &RustBindingConfig, prefix: &str) -> String {
368 let needs_mutex = type_needs_mutex(typ);
369 let core_path = typ.rust_path.replace('-', "_");
370 let has_unresolvable_generics = core_path.contains('<');
371 let all_methods_sanitized = !typ.methods.is_empty() && typ.methods.iter().all(|m| m.sanitized);
372 let omit_inner = all_methods_sanitized && has_unresolvable_generics;
373 let mut out = String::with_capacity(512);
374 if !cfg.struct_derives.is_empty() {
375 writeln!(out, "#[derive(Clone)]").ok();
376 }
377 for attr in cfg.struct_attrs {
378 writeln!(out, "#[{attr}]").ok();
379 }
380 writeln!(out, "pub struct {}{} {{", prefix, typ.name).ok();
381 if !omit_inner {
382 if typ.is_trait {
383 writeln!(out, " inner: Arc<dyn {core_path} + Send + Sync>,").ok();
384 } else if needs_mutex {
385 writeln!(out, " inner: Arc<std::sync::Mutex<{core_path}>>,").ok();
386 } else {
387 writeln!(out, " inner: Arc<{core_path}>,").ok();
388 }
389 }
390 write!(out, "}}").ok();
391 out
392}