1use crate::builder::StructBuilder;
2use crate::generators::RustBindingConfig;
3use crate::shared::binding_fields;
4use crate::type_mapper::TypeMapper;
5use alef_core::ir::{CoreWrapper, TypeDef, TypeRef};
6
7pub fn can_generate_default_impl(typ: &TypeDef, known_default_types: &std::collections::HashSet<&str>) -> bool {
13 for field in binding_fields(&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<_> = binding_fields(&typ.fields)
98 .filter(|f| f.cfg.is_none())
99 .map(|f| &f.name)
100 .collect();
101 if has_similar_names(&field_names) {
102 sb.add_attr("allow(clippy::similar_names)");
103 }
104
105 for d in cfg.struct_derives {
106 sb.add_derive(d);
107 }
108 let opaque_fields: Vec<&str> = typ
110 .fields
111 .iter()
112 .filter(|f| !f.binding_excluded)
113 .filter(|f| {
114 f.cfg.is_none()
115 && field_references_opaque_type(&f.ty, cfg.opaque_type_names)
116 && !field_references_opaque_type(&f.ty, cfg.serializable_opaque_type_names)
117 })
118 .map(|f| f.name.as_str())
119 .collect();
120 sb.add_derive("Default");
124 sb.add_derive("serde::Serialize");
125 sb.add_derive("serde::Deserialize");
126 let has_serde = true;
127 for field in binding_fields(&typ.fields) {
128 let force_optional = cfg.option_duration_on_defaults
129 && typ.has_default
130 && !field.optional
131 && matches!(field.ty, TypeRef::Duration);
132 let ty = if (field.optional || force_optional) && !matches!(field.ty, TypeRef::Optional(_)) {
133 mapper.optional(&mapper.map_type(&field.ty))
134 } else {
135 mapper.map_type(&field.ty)
137 };
138 let mut attrs: Vec<String> = cfg.field_attrs.iter().map(|a| a.to_string()).collect();
139 attrs.extend(extra_field_attrs(field));
140 let skip_sanitized_field = field.sanitized && field.core_wrapper != CoreWrapper::Cow;
147 let skip_cfg_bridge_field = field.cfg.is_some()
148 && cfg.never_skip_cfg_field_names.contains(&field.name)
149 && field_references_opaque_type(&field.ty, cfg.opaque_type_names);
150 if has_serde && (opaque_fields.contains(&field.name.as_str()) || skip_sanitized_field || skip_cfg_bridge_field)
151 {
152 attrs.push("serde(skip)".to_string());
153 }
154 sb.add_field_with_doc(&field.name, &ty, attrs, &field.doc);
155 }
156 sb.build()
157}
158
159pub fn gen_struct_with_rename(
176 typ: &TypeDef,
177 mapper: &dyn TypeMapper,
178 cfg: &RustBindingConfig,
179 extra_field_attrs: impl Fn(&alef_core::ir::FieldDef) -> Vec<String>,
180 field_name_override: impl Fn(&alef_core::ir::FieldDef) -> Option<String>,
181) -> String {
182 let mut sb = StructBuilder::new(&typ.name);
183 for attr in cfg.struct_attrs {
184 sb.add_attr(attr);
185 }
186
187 let field_names: Vec<_> = binding_fields(&typ.fields)
188 .filter(|f| f.cfg.is_none())
189 .map(|f| &f.name)
190 .collect();
191 if has_similar_names(&field_names) {
192 sb.add_attr("allow(clippy::similar_names)");
193 }
194
195 for d in cfg.struct_derives {
196 sb.add_derive(d);
197 }
198 let opaque_fields: Vec<&str> = typ
199 .fields
200 .iter()
201 .filter(|f| !f.binding_excluded)
202 .filter(|f| {
203 f.cfg.is_none()
204 && field_references_opaque_type(&f.ty, cfg.opaque_type_names)
205 && !field_references_opaque_type(&f.ty, cfg.serializable_opaque_type_names)
206 })
207 .map(|f| f.name.as_str())
208 .collect();
209 sb.add_derive("Default");
210 sb.add_derive("serde::Serialize");
211 sb.add_derive("serde::Deserialize");
212 let has_serde = true;
213 for field in binding_fields(&typ.fields) {
214 let force_optional = cfg.option_duration_on_defaults
215 && typ.has_default
216 && !field.optional
217 && matches!(field.ty, TypeRef::Duration);
218 let ty = if (field.optional || force_optional) && !matches!(field.ty, TypeRef::Optional(_)) {
219 mapper.optional(&mapper.map_type(&field.ty))
220 } else {
221 mapper.map_type(&field.ty)
222 };
223 let name_override = field_name_override(field);
224 let extra_attrs = extra_field_attrs(field);
225 let mut attrs: Vec<String> = if name_override.is_some() && !extra_attrs.is_empty() {
229 extra_attrs
230 } else {
231 let mut a: Vec<String> = cfg.field_attrs.iter().map(|a| a.to_string()).collect();
232 a.extend(extra_attrs);
233 a
234 };
235 let skip_sanitized_field = field.sanitized && field.core_wrapper != CoreWrapper::Cow;
240 let skip_cfg_bridge_field = field.cfg.is_some()
241 && cfg.never_skip_cfg_field_names.contains(&field.name)
242 && field_references_opaque_type(&field.ty, cfg.opaque_type_names);
243 if has_serde && (opaque_fields.contains(&field.name.as_str()) || skip_sanitized_field || skip_cfg_bridge_field)
244 {
245 attrs.push("serde(skip)".to_string());
246 }
247 if has_serde
251 && !attrs.iter().any(|a| a.starts_with("serde(rename"))
252 && !attrs.iter().any(|a| a == "serde(skip)")
253 {
254 if let Some(rename) = &field.serde_rename {
255 attrs.push(format!("serde(rename = \"{rename}\")"));
256 }
257 }
258 let emit_name = name_override.unwrap_or_else(|| field.name.clone());
259 sb.add_field_with_doc(&emit_name, &ty, attrs, &field.doc);
260 }
261 sb.build()
262}
263
264pub fn gen_struct(typ: &TypeDef, mapper: &dyn TypeMapper, cfg: &RustBindingConfig) -> String {
266 let mut sb = StructBuilder::new(&typ.name);
267 for attr in cfg.struct_attrs {
268 sb.add_attr(attr);
269 }
270
271 let field_names: Vec<_> = binding_fields(&typ.fields)
273 .filter(|f| f.cfg.is_none())
274 .map(|f| &f.name)
275 .collect();
276 if has_similar_names(&field_names) {
277 sb.add_attr("allow(clippy::similar_names)");
278 }
279
280 for d in cfg.struct_derives {
281 sb.add_derive(d);
282 }
283 let _opaque_fields: Vec<&str> = typ
284 .fields
285 .iter()
286 .filter(|f| !f.binding_excluded)
287 .filter(|f| {
288 f.cfg.is_none()
289 && field_references_opaque_type(&f.ty, cfg.opaque_type_names)
290 && !field_references_opaque_type(&f.ty, cfg.serializable_opaque_type_names)
291 })
292 .map(|f| f.name.as_str())
293 .collect();
294 sb.add_derive("Default");
295 sb.add_derive("serde::Serialize");
296 sb.add_derive("serde::Deserialize");
297 let _has_serde = true;
298 for field in binding_fields(&typ.fields) {
299 if field.cfg.is_some() && !cfg.never_skip_cfg_field_names.contains(&field.name) {
303 continue;
304 }
305 let force_optional = cfg.option_duration_on_defaults
309 && typ.has_default
310 && !field.optional
311 && matches!(field.ty, TypeRef::Duration);
312 let ty = if (field.optional || force_optional) && !matches!(field.ty, TypeRef::Optional(_)) {
313 mapper.optional(&mapper.map_type(&field.ty))
314 } else {
315 mapper.map_type(&field.ty)
317 };
318 let mut attrs: Vec<String> = cfg.field_attrs.iter().map(|a| a.to_string()).collect();
319 if let Some(rename) = &field.serde_rename {
322 if !attrs.iter().any(|a| a.starts_with("serde(rename")) {
323 attrs.push(format!("serde(rename = \"{rename}\")"));
324 }
325 }
326 sb.add_field_with_doc(&field.name, &ty, attrs, &field.doc);
327 }
328 sb.build()
329}
330
331pub fn gen_struct_default_impl(typ: &TypeDef, name_prefix: &str) -> String {
340 let full_name = format!("{}{}", name_prefix, typ.name);
341 let fields: Vec<_> = typ
342 .fields
343 .iter()
344 .filter(|field| !field.binding_excluded)
345 .filter_map(|field| {
346 if field.cfg.is_some() {
347 return None;
348 }
349 let default_val = match &field.ty {
350 TypeRef::Optional(_) => "None".to_string(),
351 _ => "Default::default()".to_string(),
352 };
353 Some(minijinja::context! {
354 name => field.name.clone(),
355 default_val => default_val
356 })
357 })
358 .collect();
359
360 crate::template_env::render(
361 "structs/default_impl.jinja",
362 minijinja::context! {
363 full_name => full_name,
364 fields => fields
365 },
366 )
367}
368
369pub fn type_needs_mutex(typ: &TypeDef) -> bool {
372 typ.methods
373 .iter()
374 .any(|m| m.receiver == Some(alef_core::ir::ReceiverKind::RefMut))
375}
376
377pub fn type_needs_tokio_mutex(typ: &TypeDef) -> bool {
390 use alef_core::ir::ReceiverKind;
391 if !type_needs_mutex(typ) {
392 return false;
393 }
394 let refmut_methods = typ.methods.iter().filter(|m| m.receiver == Some(ReceiverKind::RefMut));
395 let mut any = false;
396 for m in refmut_methods {
397 any = true;
398 if !m.is_async {
399 return false;
400 }
401 }
402 any
403}
404
405pub fn gen_opaque_struct(typ: &TypeDef, cfg: &RustBindingConfig) -> String {
415 let needs_mutex = type_needs_mutex(typ);
416 let core_path = typ.rust_path.replace('-', "_");
424 let has_unresolvable_generics = core_path.contains('<');
425 let all_methods_sanitized = !typ.methods.is_empty() && typ.methods.iter().all(|m| m.sanitized);
426 let omit_inner = all_methods_sanitized && has_unresolvable_generics;
427
428 let struct_attrs: Vec<_> = cfg.struct_attrs.iter().map(|s| s.to_string()).collect();
429 let has_derives = !cfg.struct_derives.is_empty();
430 let inner_type = if typ.is_trait {
431 format!("Arc<dyn {core_path} + Send + Sync>")
432 } else if needs_mutex {
433 format!("Arc<std::sync::Mutex<{core_path}>>")
434 } else {
435 format!("Arc<{core_path}>")
436 };
437
438 crate::template_env::render(
439 "structs/opaque_struct.jinja",
440 minijinja::context! {
441 struct_name => typ.name.clone(),
442 has_derives => has_derives,
443 struct_attrs => struct_attrs,
444 omit_inner => omit_inner,
445 inner_type => inner_type,
446 },
447 )
448}
449
450pub fn gen_opaque_struct_prefixed(typ: &TypeDef, cfg: &RustBindingConfig, prefix: &str) -> String {
456 let needs_mutex = type_needs_mutex(typ);
457 let core_path = typ.rust_path.replace('-', "_");
458 let has_unresolvable_generics = core_path.contains('<');
459 let all_methods_sanitized = !typ.methods.is_empty() && typ.methods.iter().all(|m| m.sanitized);
460 let omit_inner = all_methods_sanitized && has_unresolvable_generics;
461
462 let struct_attrs: Vec<_> = cfg.struct_attrs.iter().map(|s| s.to_string()).collect();
463 let has_derives = !cfg.struct_derives.is_empty();
464 let struct_name = format!("{prefix}{}", typ.name);
465 let inner_type = if typ.is_trait {
466 format!("Arc<dyn {core_path} + Send + Sync>")
467 } else if needs_mutex {
468 format!("Arc<std::sync::Mutex<{core_path}>>")
469 } else {
470 format!("Arc<{core_path}>")
471 };
472
473 crate::template_env::render(
474 "structs/opaque_struct.jinja",
475 minijinja::context! {
476 struct_name => struct_name,
477 has_derives => has_derives,
478 struct_attrs => struct_attrs,
479 omit_inner => omit_inner,
480 inner_type => inner_type,
481 },
482 )
483}
484
485#[cfg(test)]
486mod tests {
487 use super::{type_needs_mutex, type_needs_tokio_mutex};
488 use alef_core::ir::{MethodDef, ReceiverKind, TypeDef, TypeRef};
489
490 fn method(name: &str, receiver: Option<ReceiverKind>, is_async: bool) -> MethodDef {
491 MethodDef {
492 name: name.into(),
493 params: vec![],
494 return_type: TypeRef::Unit,
495 is_async,
496 is_static: false,
497 error_type: None,
498 doc: String::new(),
499 receiver,
500 sanitized: false,
501 trait_source: None,
502 returns_ref: false,
503 returns_cow: false,
504 return_newtype_wrapper: None,
505 has_default_impl: false,
506 binding_excluded: false,
507 binding_exclusion_reason: None,
508 }
509 }
510
511 fn type_with_methods(name: &str, methods: Vec<MethodDef>) -> TypeDef {
512 TypeDef {
513 name: name.into(),
514 rust_path: format!("my_crate::{name}"),
515 original_rust_path: String::new(),
516 fields: vec![],
517 methods,
518 is_opaque: true,
519 is_clone: false,
520 is_copy: false,
521 is_trait: false,
522 has_default: false,
523 has_stripped_cfg_fields: false,
524 is_return_type: false,
525 serde_rename_all: None,
526 has_serde: false,
527 super_traits: vec![],
528 doc: String::new(),
529 cfg: None,
530 binding_excluded: false,
531 binding_exclusion_reason: None,
532 }
533 }
534
535 #[test]
536 fn tokio_mutex_when_all_refmut_methods_async() {
537 let typ = type_with_methods(
538 "WebSocketConnection",
539 vec![
540 method("send_text", Some(ReceiverKind::RefMut), true),
541 method("receive_text", Some(ReceiverKind::RefMut), true),
542 method("close", None, true),
543 ],
544 );
545 assert!(type_needs_mutex(&typ));
546 assert!(type_needs_tokio_mutex(&typ));
547 }
548
549 #[test]
550 fn no_tokio_mutex_when_any_refmut_is_sync() {
551 let typ = type_with_methods(
552 "Mixed",
553 vec![
554 method("async_op", Some(ReceiverKind::RefMut), true),
555 method("sync_op", Some(ReceiverKind::RefMut), false),
556 ],
557 );
558 assert!(type_needs_mutex(&typ));
559 assert!(!type_needs_tokio_mutex(&typ));
560 }
561
562 #[test]
563 fn no_tokio_mutex_when_no_refmut() {
564 let typ = type_with_methods("ReadOnly", vec![method("get", Some(ReceiverKind::Ref), true)]);
565 assert!(!type_needs_mutex(&typ));
566 assert!(!type_needs_tokio_mutex(&typ));
567 }
568
569 #[test]
570 fn no_tokio_mutex_when_empty_methods() {
571 let typ = type_with_methods("Empty", vec![]);
572 assert!(!type_needs_mutex(&typ));
573 assert!(!type_needs_tokio_mutex(&typ));
574 }
575}