1use crate::builder::StructBuilder;
2use crate::generators::RustBindingConfig;
3use crate::type_mapper::TypeMapper;
4use alef_core::ir::{CoreWrapper, DefaultValue, 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 let skip_sanitized_field = field.sanitized && field.core_wrapper != CoreWrapper::Cow;
138 if has_serde && (opaque_fields.contains(&field.name.as_str()) || skip_sanitized_field) {
139 attrs.push("serde(skip)".to_string());
140 }
141 sb.add_field_with_doc(&field.name, &ty, attrs, &field.doc);
142 }
143 sb.build()
144}
145
146pub fn gen_struct_with_rename(
163 typ: &TypeDef,
164 mapper: &dyn TypeMapper,
165 cfg: &RustBindingConfig,
166 extra_field_attrs: impl Fn(&alef_core::ir::FieldDef) -> Vec<String>,
167 field_name_override: impl Fn(&alef_core::ir::FieldDef) -> Option<String>,
168) -> String {
169 let mut sb = StructBuilder::new(&typ.name);
170 for attr in cfg.struct_attrs {
171 sb.add_attr(attr);
172 }
173
174 let field_names: Vec<_> = typ.fields.iter().filter(|f| f.cfg.is_none()).map(|f| &f.name).collect();
175 if has_similar_names(&field_names) {
176 sb.add_attr("allow(clippy::similar_names)");
177 }
178
179 for d in cfg.struct_derives {
180 sb.add_derive(d);
181 }
182 let opaque_fields: Vec<&str> = typ
183 .fields
184 .iter()
185 .filter(|f| f.cfg.is_none() && field_references_opaque_type(&f.ty, cfg.opaque_type_names))
186 .map(|f| f.name.as_str())
187 .collect();
188 sb.add_derive("Default");
189 sb.add_derive("serde::Serialize");
190 sb.add_derive("serde::Deserialize");
191 let has_serde = true;
192 for field in &typ.fields {
193 if field.cfg.is_some() {
194 continue;
195 }
196 let force_optional = cfg.option_duration_on_defaults
197 && typ.has_default
198 && !field.optional
199 && matches!(field.ty, TypeRef::Duration);
200 let ty = if (field.optional || force_optional) && !matches!(field.ty, TypeRef::Optional(_)) {
201 mapper.optional(&mapper.map_type(&field.ty))
202 } else {
203 mapper.map_type(&field.ty)
204 };
205 let name_override = field_name_override(field);
206 let extra_attrs = extra_field_attrs(field);
207 let mut attrs: Vec<String> = if name_override.is_some() && !extra_attrs.is_empty() {
211 extra_attrs
212 } else {
213 let mut a: Vec<String> = cfg.field_attrs.iter().map(|a| a.to_string()).collect();
214 a.extend(extra_attrs);
215 a
216 };
217 let skip_sanitized_field = field.sanitized && field.core_wrapper != CoreWrapper::Cow;
222 if has_serde && (opaque_fields.contains(&field.name.as_str()) || skip_sanitized_field) {
223 attrs.push("serde(skip)".to_string());
224 }
225 let emit_name = name_override.unwrap_or_else(|| field.name.clone());
226 sb.add_field_with_doc(&emit_name, &ty, attrs, &field.doc);
227 }
228 sb.build()
229}
230
231pub fn gen_struct(typ: &TypeDef, mapper: &dyn TypeMapper, cfg: &RustBindingConfig) -> String {
233 let mut sb = StructBuilder::new(&typ.name);
234 for attr in cfg.struct_attrs {
235 sb.add_attr(attr);
236 }
237
238 let field_names: Vec<_> = typ.fields.iter().filter(|f| f.cfg.is_none()).map(|f| &f.name).collect();
240 if has_similar_names(&field_names) {
241 sb.add_attr("allow(clippy::similar_names)");
242 }
243
244 for d in cfg.struct_derives {
245 sb.add_derive(d);
246 }
247 let _opaque_fields: Vec<&str> = typ
248 .fields
249 .iter()
250 .filter(|f| f.cfg.is_none() && field_references_opaque_type(&f.ty, cfg.opaque_type_names))
251 .map(|f| f.name.as_str())
252 .collect();
253 sb.add_derive("Default");
254 sb.add_derive("serde::Serialize");
255 sb.add_derive("serde::Deserialize");
256 let _has_serde = true;
257 for field in &typ.fields {
258 if field.cfg.is_some() {
262 continue;
263 }
264 let force_optional = cfg.option_duration_on_defaults
268 && typ.has_default
269 && !field.optional
270 && matches!(field.ty, TypeRef::Duration);
271 let ty = if (field.optional || force_optional) && !matches!(field.ty, TypeRef::Optional(_)) {
272 mapper.optional(&mapper.map_type(&field.ty))
273 } else {
274 mapper.map_type(&field.ty)
276 };
277 let attrs: Vec<String> = cfg.field_attrs.iter().map(|a| a.to_string()).collect();
278 sb.add_field_with_doc(&field.name, &ty, attrs, &field.doc);
284 }
285 sb.build()
286}
287
288pub fn gen_struct_default_impl(typ: &TypeDef, name_prefix: &str) -> String {
297 let full_name = format!("{}{}", name_prefix, typ.name);
298 let mut out = String::with_capacity(256);
299 writeln!(out, "impl Default for {} {{", full_name).ok();
300 writeln!(out, " fn default() -> Self {{").ok();
301 writeln!(out, " Self {{").ok();
302 for field in &typ.fields {
303 if field.cfg.is_some() {
304 continue;
305 }
306 let default_val = match (&field.ty, &field.typed_default) {
307 (_, Some(DefaultValue::BoolLiteral(b))) => b.to_string(),
308 (_, Some(DefaultValue::IntLiteral(n))) => n.to_string(),
309 (_, Some(DefaultValue::StringLiteral(s))) => format!("String::from(\"{}\")", s),
310 (_, Some(DefaultValue::None)) | (TypeRef::Optional(_), _) => "None".to_string(),
311 _ => "Default::default()".to_string(),
312 };
313 writeln!(out, " {}: {},", field.name, default_val).ok();
314 }
315 writeln!(out, " }}").ok();
316 writeln!(out, " }}").ok();
317 write!(out, "}}").ok();
318 out
319}
320
321pub fn type_needs_mutex(typ: &TypeDef) -> bool {
324 typ.methods
325 .iter()
326 .any(|m| m.receiver == Some(alef_core::ir::ReceiverKind::RefMut))
327}
328
329pub fn type_needs_tokio_mutex(typ: &TypeDef) -> bool {
342 use alef_core::ir::ReceiverKind;
343 if !type_needs_mutex(typ) {
344 return false;
345 }
346 let refmut_methods = typ.methods.iter().filter(|m| m.receiver == Some(ReceiverKind::RefMut));
347 let mut any = false;
348 for m in refmut_methods {
349 any = true;
350 if !m.is_async {
351 return false;
352 }
353 }
354 any
355}
356
357pub fn gen_opaque_struct(typ: &TypeDef, cfg: &RustBindingConfig) -> String {
367 let needs_mutex = type_needs_mutex(typ);
368 let core_path = typ.rust_path.replace('-', "_");
376 let has_unresolvable_generics = core_path.contains('<');
377 let all_methods_sanitized = !typ.methods.is_empty() && typ.methods.iter().all(|m| m.sanitized);
378 let omit_inner = all_methods_sanitized && has_unresolvable_generics;
379 let mut out = String::with_capacity(512);
380 if !cfg.struct_derives.is_empty() {
381 writeln!(out, "#[derive(Clone)]").ok();
382 }
383 for attr in cfg.struct_attrs {
384 writeln!(out, "#[{attr}]").ok();
385 }
386 writeln!(out, "pub struct {} {{", typ.name).ok();
387 if !omit_inner {
388 if typ.is_trait {
389 writeln!(out, " inner: Arc<dyn {core_path} + Send + Sync>,").ok();
390 } else if needs_mutex {
391 writeln!(out, " inner: Arc<std::sync::Mutex<{core_path}>>,").ok();
392 } else {
393 writeln!(out, " inner: Arc<{core_path}>,").ok();
394 }
395 }
396 write!(out, "}}").ok();
397 out
398}
399
400pub fn gen_opaque_struct_prefixed(typ: &TypeDef, cfg: &RustBindingConfig, prefix: &str) -> String {
406 let needs_mutex = type_needs_mutex(typ);
407 let core_path = typ.rust_path.replace('-', "_");
408 let has_unresolvable_generics = core_path.contains('<');
409 let all_methods_sanitized = !typ.methods.is_empty() && typ.methods.iter().all(|m| m.sanitized);
410 let omit_inner = all_methods_sanitized && has_unresolvable_generics;
411 let mut out = String::with_capacity(512);
412 if !cfg.struct_derives.is_empty() {
413 writeln!(out, "#[derive(Clone)]").ok();
414 }
415 for attr in cfg.struct_attrs {
416 writeln!(out, "#[{attr}]").ok();
417 }
418 writeln!(out, "pub struct {}{} {{", prefix, typ.name).ok();
419 if !omit_inner {
420 if typ.is_trait {
421 writeln!(out, " inner: Arc<dyn {core_path} + Send + Sync>,").ok();
422 } else if needs_mutex {
423 writeln!(out, " inner: Arc<std::sync::Mutex<{core_path}>>,").ok();
424 } else {
425 writeln!(out, " inner: Arc<{core_path}>,").ok();
426 }
427 }
428 write!(out, "}}").ok();
429 out
430}
431
432#[cfg(test)]
433mod tests {
434 use super::{type_needs_mutex, type_needs_tokio_mutex};
435 use alef_core::ir::{MethodDef, ReceiverKind, TypeDef, TypeRef};
436
437 fn method(name: &str, receiver: Option<ReceiverKind>, is_async: bool) -> MethodDef {
438 MethodDef {
439 name: name.into(),
440 params: vec![],
441 return_type: TypeRef::Unit,
442 is_async,
443 is_static: false,
444 error_type: None,
445 doc: String::new(),
446 receiver,
447 sanitized: false,
448 trait_source: None,
449 returns_ref: false,
450 returns_cow: false,
451 return_newtype_wrapper: None,
452 has_default_impl: false,
453 }
454 }
455
456 fn type_with_methods(name: &str, methods: Vec<MethodDef>) -> TypeDef {
457 TypeDef {
458 name: name.into(),
459 rust_path: format!("my_crate::{name}"),
460 original_rust_path: String::new(),
461 fields: vec![],
462 methods,
463 is_opaque: true,
464 is_clone: false,
465 is_copy: false,
466 is_trait: false,
467 has_default: false,
468 has_stripped_cfg_fields: false,
469 is_return_type: false,
470 serde_rename_all: None,
471 has_serde: false,
472 super_traits: vec![],
473 doc: String::new(),
474 cfg: None,
475 }
476 }
477
478 #[test]
479 fn tokio_mutex_when_all_refmut_methods_async() {
480 let typ = type_with_methods(
481 "WebSocketConnection",
482 vec![
483 method("send_text", Some(ReceiverKind::RefMut), true),
484 method("receive_text", Some(ReceiverKind::RefMut), true),
485 method("close", None, true),
486 ],
487 );
488 assert!(type_needs_mutex(&typ));
489 assert!(type_needs_tokio_mutex(&typ));
490 }
491
492 #[test]
493 fn no_tokio_mutex_when_any_refmut_is_sync() {
494 let typ = type_with_methods(
495 "Mixed",
496 vec![
497 method("async_op", Some(ReceiverKind::RefMut), true),
498 method("sync_op", Some(ReceiverKind::RefMut), false),
499 ],
500 );
501 assert!(type_needs_mutex(&typ));
502 assert!(!type_needs_tokio_mutex(&typ));
503 }
504
505 #[test]
506 fn no_tokio_mutex_when_no_refmut() {
507 let typ = type_with_methods("ReadOnly", vec![method("get", Some(ReceiverKind::Ref), true)]);
508 assert!(!type_needs_mutex(&typ));
509 assert!(!type_needs_tokio_mutex(&typ));
510 }
511
512 #[test]
513 fn no_tokio_mutex_when_empty_methods() {
514 let typ = type_with_methods("Empty", vec![]);
515 assert!(!type_needs_mutex(&typ));
516 assert!(!type_needs_tokio_mutex(&typ));
517 }
518}