alef_codegen/generators/
structs.rs1use crate::builder::StructBuilder;
2use crate::generators::RustBindingConfig;
3use crate::type_mapper::TypeMapper;
4use alef_core::ir::{TypeDef, TypeRef};
5use std::fmt::Write;
6
7fn has_similar_names(names: &[&String]) -> bool {
10 for (i, &name1) in names.iter().enumerate() {
11 for &name2 in &names[i + 1..] {
12 if name1.len() == name2.len() && diff_count(name1, name2) <= 2 {
14 return true;
15 }
16 }
17 }
18 false
19}
20
21fn diff_count(s1: &str, s2: &str) -> usize {
23 s1.chars().zip(s2.chars()).filter(|(c1, c2)| c1 != c2).count()
24}
25
26pub fn gen_struct(typ: &TypeDef, mapper: &dyn TypeMapper, cfg: &RustBindingConfig) -> String {
28 let mut sb = StructBuilder::new(&typ.name);
29 for attr in cfg.struct_attrs {
30 sb.add_attr(attr);
31 }
32
33 let field_names: Vec<_> = typ.fields.iter().filter(|f| f.cfg.is_none()).map(|f| &f.name).collect();
35 if has_similar_names(&field_names) {
36 sb.add_attr("allow(clippy::similar_names)");
37 }
38
39 for d in cfg.struct_derives {
40 sb.add_derive(d);
41 }
42 if cfg.has_serde {
43 sb.add_derive("serde::Serialize");
44 }
45 for field in &typ.fields {
46 if field.cfg.is_some() {
50 continue;
51 }
52 let ty = if field.optional {
53 mapper.optional(&mapper.map_type(&field.ty))
54 } else {
55 mapper.map_type(&field.ty)
56 };
57 let attrs: Vec<String> = cfg.field_attrs.iter().map(|a| a.to_string()).collect();
58 sb.add_field_with_doc(&field.name, &ty, attrs, &field.doc);
59 }
60 sb.build()
61}
62
63pub fn gen_struct_default_impl(typ: &TypeDef, name_prefix: &str) -> String {
68 let full_name = format!("{}{}", name_prefix, typ.name);
69 let mut out = String::with_capacity(256);
70 writeln!(out, "impl Default for {} {{", full_name).ok();
71 writeln!(out, " fn default() -> Self {{").ok();
72 writeln!(out, " Self {{").ok();
73 for field in &typ.fields {
74 if field.cfg.is_some() {
75 continue;
76 }
77 let default_val = match &field.ty {
78 TypeRef::Optional(_) => "None".to_string(),
79 _ => "Default::default()".to_string(),
80 };
81 writeln!(out, " {}: {},", field.name, default_val).ok();
82 }
83 if name_prefix == "Js" && typ.name == "ConversionResult" {
86 writeln!(out, " metadata: None,").ok();
89 }
90 writeln!(out, " }}").ok();
91 writeln!(out, " }}").ok();
92 write!(out, "}}").ok();
93 out
94}
95
96pub fn gen_opaque_struct(typ: &TypeDef, cfg: &RustBindingConfig) -> String {
99 let mut out = String::with_capacity(512);
100 if !cfg.struct_derives.is_empty() {
101 writeln!(out, "#[derive(Clone)]").ok();
102 }
103 for attr in cfg.struct_attrs {
104 writeln!(out, "#[{attr}]").ok();
105 }
106 writeln!(out, "pub struct {} {{", typ.name).ok();
107 let core_path = typ.rust_path.replace('-', "_");
108 if typ.is_trait {
109 writeln!(out, " inner: Arc<dyn {core_path} + Send + Sync>,").ok();
110 } else {
111 writeln!(out, " inner: Arc<{core_path}>,").ok();
112 }
113 write!(out, "}}").ok();
114 out
115}
116
117pub fn gen_opaque_struct_prefixed(typ: &TypeDef, cfg: &RustBindingConfig, prefix: &str) -> String {
119 let mut out = String::with_capacity(512);
120 if !cfg.struct_derives.is_empty() {
121 writeln!(out, "#[derive(Clone)]").ok();
122 }
123 for attr in cfg.struct_attrs {
124 writeln!(out, "#[{attr}]").ok();
125 }
126 let core_path = typ.rust_path.replace('-', "_");
127 writeln!(out, "pub struct {}{} {{", prefix, typ.name).ok();
128 if typ.is_trait {
129 writeln!(out, " inner: Arc<dyn {core_path} + Send + Sync>,").ok();
130 } else {
131 writeln!(out, " inner: Arc<{core_path}>,").ok();
132 }
133 write!(out, "}}").ok();
134 out
135}