1use crate::builder::StructBuilder;
2use crate::generators::RustBindingConfig;
3use crate::type_mapper::TypeMapper;
4use alef_core::ir::{CoreWrapper, TypeDef, TypeRef};
5
6pub fn can_generate_default_impl(typ: &TypeDef, known_default_types: &std::collections::HashSet<&str>) -> bool {
12 for field in &typ.fields {
13 if field.cfg.is_some() {
14 continue; }
16 if !field_type_has_default(&field.ty, known_default_types) {
17 return false;
18 }
19 }
20 true
21}
22
23fn field_type_has_default(ty: &TypeRef, known_default_types: &std::collections::HashSet<&str>) -> bool {
25 match ty {
26 TypeRef::Primitive(_)
27 | TypeRef::String
28 | TypeRef::Char
29 | TypeRef::Bytes
30 | TypeRef::Path
31 | TypeRef::Unit
32 | TypeRef::Duration
33 | TypeRef::Json => true,
34 TypeRef::Optional(inner) => field_type_has_default(inner, known_default_types),
36 TypeRef::Vec(inner) => field_type_has_default(inner, known_default_types),
38 TypeRef::Map(k, v) => {
40 field_type_has_default(k, known_default_types) && field_type_has_default(v, known_default_types)
41 }
42 TypeRef::Named(name) => known_default_types.contains(name.as_str()),
44 }
45}
46
47fn has_similar_names(names: &[&String]) -> bool {
50 for (i, &name1) in names.iter().enumerate() {
51 for &name2 in &names[i + 1..] {
52 if name1.len() == name2.len() && diff_count(name1, name2) <= 2 {
54 return true;
55 }
56 }
57 }
58 false
59}
60
61fn diff_count(s1: &str, s2: &str) -> usize {
63 s1.chars().zip(s2.chars()).filter(|(c1, c2)| c1 != c2).count()
64}
65
66pub fn field_references_opaque_type(ty: &TypeRef, opaque_names: &[String]) -> bool {
70 match ty {
71 TypeRef::Named(name) => opaque_names.contains(name),
72 TypeRef::Optional(inner) | TypeRef::Vec(inner) => field_references_opaque_type(inner, opaque_names),
73 TypeRef::Map(k, v) => {
74 field_references_opaque_type(k, opaque_names) || field_references_opaque_type(v, opaque_names)
75 }
76 _ => false,
77 }
78}
79
80pub fn gen_struct_with_per_field_attrs(
85 typ: &TypeDef,
86 mapper: &dyn TypeMapper,
87 cfg: &RustBindingConfig,
88 extra_field_attrs: impl Fn(&alef_core::ir::FieldDef) -> Vec<String>,
89) -> String {
90 let mut sb = StructBuilder::new(&typ.name);
91 for attr in cfg.struct_attrs {
92 sb.add_attr(attr);
93 }
94
95 let field_names: Vec<_> = typ.fields.iter().filter(|f| f.cfg.is_none()).map(|f| &f.name).collect();
97 if has_similar_names(&field_names) {
98 sb.add_attr("allow(clippy::similar_names)");
99 }
100
101 for d in cfg.struct_derives {
102 sb.add_derive(d);
103 }
104 let opaque_fields: Vec<&str> = typ
106 .fields
107 .iter()
108 .filter(|f| {
109 f.cfg.is_none()
110 && field_references_opaque_type(&f.ty, cfg.opaque_type_names)
111 && !field_references_opaque_type(&f.ty, cfg.serializable_opaque_type_names)
112 })
113 .map(|f| f.name.as_str())
114 .collect();
115 sb.add_derive("Default");
119 sb.add_derive("serde::Serialize");
120 sb.add_derive("serde::Deserialize");
121 let has_serde = true;
122 for field in &typ.fields {
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;
142 let skip_cfg_bridge_field = field.cfg.is_some()
143 && cfg.never_skip_cfg_field_names.contains(&field.name)
144 && field_references_opaque_type(&field.ty, cfg.opaque_type_names);
145 if has_serde && (opaque_fields.contains(&field.name.as_str()) || skip_sanitized_field || skip_cfg_bridge_field)
146 {
147 attrs.push("serde(skip)".to_string());
148 }
149 sb.add_field_with_doc(&field.name, &ty, attrs, &field.doc);
150 }
151 sb.build()
152}
153
154pub fn gen_struct_with_rename(
171 typ: &TypeDef,
172 mapper: &dyn TypeMapper,
173 cfg: &RustBindingConfig,
174 extra_field_attrs: impl Fn(&alef_core::ir::FieldDef) -> Vec<String>,
175 field_name_override: impl Fn(&alef_core::ir::FieldDef) -> Option<String>,
176) -> String {
177 let mut sb = StructBuilder::new(&typ.name);
178 for attr in cfg.struct_attrs {
179 sb.add_attr(attr);
180 }
181
182 let field_names: Vec<_> = typ.fields.iter().filter(|f| f.cfg.is_none()).map(|f| &f.name).collect();
183 if has_similar_names(&field_names) {
184 sb.add_attr("allow(clippy::similar_names)");
185 }
186
187 for d in cfg.struct_derives {
188 sb.add_derive(d);
189 }
190 let opaque_fields: Vec<&str> = typ
191 .fields
192 .iter()
193 .filter(|f| {
194 f.cfg.is_none()
195 && field_references_opaque_type(&f.ty, cfg.opaque_type_names)
196 && !field_references_opaque_type(&f.ty, cfg.serializable_opaque_type_names)
197 })
198 .map(|f| f.name.as_str())
199 .collect();
200 sb.add_derive("Default");
201 sb.add_derive("serde::Serialize");
202 sb.add_derive("serde::Deserialize");
203 let has_serde = true;
204 for field in &typ.fields {
205 let force_optional = cfg.option_duration_on_defaults
206 && typ.has_default
207 && !field.optional
208 && matches!(field.ty, TypeRef::Duration);
209 let ty = if (field.optional || force_optional) && !matches!(field.ty, TypeRef::Optional(_)) {
210 mapper.optional(&mapper.map_type(&field.ty))
211 } else {
212 mapper.map_type(&field.ty)
213 };
214 let name_override = field_name_override(field);
215 let extra_attrs = extra_field_attrs(field);
216 let mut attrs: Vec<String> = if name_override.is_some() && !extra_attrs.is_empty() {
220 extra_attrs
221 } else {
222 let mut a: Vec<String> = cfg.field_attrs.iter().map(|a| a.to_string()).collect();
223 a.extend(extra_attrs);
224 a
225 };
226 let skip_sanitized_field = field.sanitized && field.core_wrapper != CoreWrapper::Cow;
231 let skip_cfg_bridge_field = field.cfg.is_some()
232 && cfg.never_skip_cfg_field_names.contains(&field.name)
233 && field_references_opaque_type(&field.ty, cfg.opaque_type_names);
234 if has_serde && (opaque_fields.contains(&field.name.as_str()) || skip_sanitized_field || skip_cfg_bridge_field)
235 {
236 attrs.push("serde(skip)".to_string());
237 }
238 if has_serde
242 && !attrs.iter().any(|a| a.starts_with("serde(rename"))
243 && !attrs.iter().any(|a| a == "serde(skip)")
244 {
245 if let Some(rename) = &field.serde_rename {
246 attrs.push(format!("serde(rename = \"{rename}\")"));
247 }
248 }
249 let emit_name = name_override.unwrap_or_else(|| field.name.clone());
250 sb.add_field_with_doc(&emit_name, &ty, attrs, &field.doc);
251 }
252 sb.build()
253}
254
255pub fn gen_struct(typ: &TypeDef, mapper: &dyn TypeMapper, cfg: &RustBindingConfig) -> String {
257 let mut sb = StructBuilder::new(&typ.name);
258 for attr in cfg.struct_attrs {
259 sb.add_attr(attr);
260 }
261
262 let field_names: Vec<_> = typ.fields.iter().filter(|f| f.cfg.is_none()).map(|f| &f.name).collect();
264 if has_similar_names(&field_names) {
265 sb.add_attr("allow(clippy::similar_names)");
266 }
267
268 for d in cfg.struct_derives {
269 sb.add_derive(d);
270 }
271 let _opaque_fields: Vec<&str> = typ
272 .fields
273 .iter()
274 .filter(|f| {
275 f.cfg.is_none()
276 && field_references_opaque_type(&f.ty, cfg.opaque_type_names)
277 && !field_references_opaque_type(&f.ty, cfg.serializable_opaque_type_names)
278 })
279 .map(|f| f.name.as_str())
280 .collect();
281 sb.add_derive("Default");
282 sb.add_derive("serde::Serialize");
283 sb.add_derive("serde::Deserialize");
284 let _has_serde = true;
285 for field in &typ.fields {
286 if field.cfg.is_some() && !cfg.never_skip_cfg_field_names.contains(&field.name) {
290 continue;
291 }
292 let force_optional = cfg.option_duration_on_defaults
296 && typ.has_default
297 && !field.optional
298 && matches!(field.ty, TypeRef::Duration);
299 let ty = if (field.optional || force_optional) && !matches!(field.ty, TypeRef::Optional(_)) {
300 mapper.optional(&mapper.map_type(&field.ty))
301 } else {
302 mapper.map_type(&field.ty)
304 };
305 let mut attrs: Vec<String> = cfg.field_attrs.iter().map(|a| a.to_string()).collect();
306 if let Some(rename) = &field.serde_rename {
309 if !attrs.iter().any(|a| a.starts_with("serde(rename")) {
310 attrs.push(format!("serde(rename = \"{rename}\")"));
311 }
312 }
313 sb.add_field_with_doc(&field.name, &ty, attrs, &field.doc);
314 }
315 sb.build()
316}
317
318pub fn gen_struct_default_impl(typ: &TypeDef, name_prefix: &str) -> String {
327 let full_name = format!("{}{}", name_prefix, typ.name);
328 let fields: Vec<_> = typ
329 .fields
330 .iter()
331 .filter_map(|field| {
332 if field.cfg.is_some() {
333 return None;
334 }
335 let default_val = match &field.ty {
336 TypeRef::Optional(_) => "None".to_string(),
337 _ => "Default::default()".to_string(),
338 };
339 Some(minijinja::context! {
340 name => field.name.clone(),
341 default_val => default_val
342 })
343 })
344 .collect();
345
346 crate::template_env::render(
347 "structs/default_impl.jinja",
348 minijinja::context! {
349 full_name => full_name,
350 fields => fields
351 },
352 )
353}
354
355pub fn type_needs_mutex(typ: &TypeDef) -> bool {
358 typ.methods
359 .iter()
360 .any(|m| m.receiver == Some(alef_core::ir::ReceiverKind::RefMut))
361}
362
363pub fn type_needs_tokio_mutex(typ: &TypeDef) -> bool {
376 use alef_core::ir::ReceiverKind;
377 if !type_needs_mutex(typ) {
378 return false;
379 }
380 let refmut_methods = typ.methods.iter().filter(|m| m.receiver == Some(ReceiverKind::RefMut));
381 let mut any = false;
382 for m in refmut_methods {
383 any = true;
384 if !m.is_async {
385 return false;
386 }
387 }
388 any
389}
390
391pub fn gen_opaque_struct(typ: &TypeDef, cfg: &RustBindingConfig) -> String {
401 let needs_mutex = type_needs_mutex(typ);
402 let core_path = typ.rust_path.replace('-', "_");
410 let has_unresolvable_generics = core_path.contains('<');
411 let all_methods_sanitized = !typ.methods.is_empty() && typ.methods.iter().all(|m| m.sanitized);
412 let omit_inner = all_methods_sanitized && has_unresolvable_generics;
413
414 let struct_attrs: Vec<_> = cfg.struct_attrs.iter().map(|s| s.to_string()).collect();
415 let has_derives = !cfg.struct_derives.is_empty();
416 let inner_type = if typ.is_trait {
417 format!("Arc<dyn {core_path} + Send + Sync>")
418 } else if needs_mutex {
419 format!("Arc<std::sync::Mutex<{core_path}>>")
420 } else {
421 format!("Arc<{core_path}>")
422 };
423
424 crate::template_env::render(
425 "structs/opaque_struct.jinja",
426 minijinja::context! {
427 struct_name => typ.name.clone(),
428 has_derives => has_derives,
429 struct_attrs => struct_attrs,
430 omit_inner => omit_inner,
431 inner_type => inner_type,
432 },
433 )
434}
435
436pub fn gen_opaque_struct_prefixed(typ: &TypeDef, cfg: &RustBindingConfig, prefix: &str) -> String {
442 let needs_mutex = type_needs_mutex(typ);
443 let core_path = typ.rust_path.replace('-', "_");
444 let has_unresolvable_generics = core_path.contains('<');
445 let all_methods_sanitized = !typ.methods.is_empty() && typ.methods.iter().all(|m| m.sanitized);
446 let omit_inner = all_methods_sanitized && has_unresolvable_generics;
447
448 let struct_attrs: Vec<_> = cfg.struct_attrs.iter().map(|s| s.to_string()).collect();
449 let has_derives = !cfg.struct_derives.is_empty();
450 let struct_name = format!("{prefix}{}", typ.name);
451 let inner_type = if typ.is_trait {
452 format!("Arc<dyn {core_path} + Send + Sync>")
453 } else if needs_mutex {
454 format!("Arc<std::sync::Mutex<{core_path}>>")
455 } else {
456 format!("Arc<{core_path}>")
457 };
458
459 crate::template_env::render(
460 "structs/opaque_struct.jinja",
461 minijinja::context! {
462 struct_name => struct_name,
463 has_derives => has_derives,
464 struct_attrs => struct_attrs,
465 omit_inner => omit_inner,
466 inner_type => inner_type,
467 },
468 )
469}
470
471#[cfg(test)]
472mod tests {
473 use super::{type_needs_mutex, type_needs_tokio_mutex};
474 use alef_core::ir::{MethodDef, ReceiverKind, TypeDef, TypeRef};
475
476 fn method(name: &str, receiver: Option<ReceiverKind>, is_async: bool) -> MethodDef {
477 MethodDef {
478 name: name.into(),
479 params: vec![],
480 return_type: TypeRef::Unit,
481 is_async,
482 is_static: false,
483 error_type: None,
484 doc: String::new(),
485 receiver,
486 sanitized: false,
487 trait_source: None,
488 returns_ref: false,
489 returns_cow: false,
490 return_newtype_wrapper: None,
491 has_default_impl: false,
492 }
493 }
494
495 fn type_with_methods(name: &str, methods: Vec<MethodDef>) -> TypeDef {
496 TypeDef {
497 name: name.into(),
498 rust_path: format!("my_crate::{name}"),
499 original_rust_path: String::new(),
500 fields: vec![],
501 methods,
502 is_opaque: true,
503 is_clone: false,
504 is_copy: false,
505 is_trait: false,
506 has_default: false,
507 has_stripped_cfg_fields: false,
508 is_return_type: false,
509 serde_rename_all: None,
510 has_serde: false,
511 super_traits: vec![],
512 doc: String::new(),
513 cfg: None,
514 }
515 }
516
517 #[test]
518 fn tokio_mutex_when_all_refmut_methods_async() {
519 let typ = type_with_methods(
520 "WebSocketConnection",
521 vec![
522 method("send_text", Some(ReceiverKind::RefMut), true),
523 method("receive_text", Some(ReceiverKind::RefMut), true),
524 method("close", None, true),
525 ],
526 );
527 assert!(type_needs_mutex(&typ));
528 assert!(type_needs_tokio_mutex(&typ));
529 }
530
531 #[test]
532 fn no_tokio_mutex_when_any_refmut_is_sync() {
533 let typ = type_with_methods(
534 "Mixed",
535 vec![
536 method("async_op", Some(ReceiverKind::RefMut), true),
537 method("sync_op", Some(ReceiverKind::RefMut), false),
538 ],
539 );
540 assert!(type_needs_mutex(&typ));
541 assert!(!type_needs_tokio_mutex(&typ));
542 }
543
544 #[test]
545 fn no_tokio_mutex_when_no_refmut() {
546 let typ = type_with_methods("ReadOnly", vec![method("get", Some(ReceiverKind::Ref), true)]);
547 assert!(!type_needs_mutex(&typ));
548 assert!(!type_needs_tokio_mutex(&typ));
549 }
550
551 #[test]
552 fn no_tokio_mutex_when_empty_methods() {
553 let typ = type_with_methods("Empty", vec![]);
554 assert!(!type_needs_mutex(&typ));
555 assert!(!type_needs_tokio_mutex(&typ));
556 }
557}