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_with_per_field_attrs(
31 typ: &TypeDef,
32 mapper: &dyn TypeMapper,
33 cfg: &RustBindingConfig,
34 extra_field_attrs: impl Fn(&alef_core::ir::FieldDef) -> Vec<String>,
35) -> String {
36 let mut sb = StructBuilder::new(&typ.name);
37 for attr in cfg.struct_attrs {
38 sb.add_attr(attr);
39 }
40
41 let field_names: Vec<_> = typ.fields.iter().filter(|f| f.cfg.is_none()).map(|f| &f.name).collect();
43 if has_similar_names(&field_names) {
44 sb.add_attr("allow(clippy::similar_names)");
45 }
46
47 for d in cfg.struct_derives {
48 sb.add_derive(d);
49 }
50 if typ.has_default {
51 sb.add_derive("Default");
52 }
53 if cfg.has_serde {
54 sb.add_derive("serde::Serialize");
55 }
56 for field in &typ.fields {
57 if field.cfg.is_some() {
58 continue;
59 }
60 let force_optional = cfg.option_duration_on_defaults
61 && typ.has_default
62 && !field.optional
63 && matches!(field.ty, TypeRef::Duration);
64 let ty = if field.optional || force_optional {
65 mapper.optional(&mapper.map_type(&field.ty))
66 } else {
67 mapper.map_type(&field.ty)
68 };
69 let mut attrs: Vec<String> = cfg.field_attrs.iter().map(|a| a.to_string()).collect();
70 attrs.extend(extra_field_attrs(field));
71 sb.add_field_with_doc(&field.name, &ty, attrs, &field.doc);
72 }
73 sb.build()
74}
75
76pub fn gen_struct(typ: &TypeDef, mapper: &dyn TypeMapper, cfg: &RustBindingConfig) -> String {
78 let mut sb = StructBuilder::new(&typ.name);
79 for attr in cfg.struct_attrs {
80 sb.add_attr(attr);
81 }
82
83 let field_names: Vec<_> = typ.fields.iter().filter(|f| f.cfg.is_none()).map(|f| &f.name).collect();
85 if has_similar_names(&field_names) {
86 sb.add_attr("allow(clippy::similar_names)");
87 }
88
89 for d in cfg.struct_derives {
90 sb.add_derive(d);
91 }
92 if typ.has_default {
95 sb.add_derive("Default");
96 }
97 if cfg.has_serde {
98 sb.add_derive("serde::Serialize");
99 }
100 for field in &typ.fields {
101 if field.cfg.is_some() {
105 continue;
106 }
107 let force_optional = cfg.option_duration_on_defaults
111 && typ.has_default
112 && !field.optional
113 && matches!(field.ty, TypeRef::Duration);
114 let ty = if field.optional || force_optional {
115 mapper.optional(&mapper.map_type(&field.ty))
116 } else {
117 mapper.map_type(&field.ty)
118 };
119 let attrs: Vec<String> = cfg.field_attrs.iter().map(|a| a.to_string()).collect();
120 sb.add_field_with_doc(&field.name, &ty, attrs, &field.doc);
121 }
122 sb.build()
123}
124
125pub fn gen_struct_default_impl(typ: &TypeDef, name_prefix: &str) -> String {
130 let full_name = format!("{}{}", name_prefix, typ.name);
131 let mut out = String::with_capacity(256);
132 writeln!(out, "impl Default for {} {{", full_name).ok();
133 writeln!(out, " fn default() -> Self {{").ok();
134 writeln!(out, " Self {{").ok();
135 for field in &typ.fields {
136 if field.cfg.is_some() {
137 continue;
138 }
139 let default_val = match &field.ty {
140 TypeRef::Optional(_) => "None".to_string(),
141 _ => "Default::default()".to_string(),
142 };
143 writeln!(out, " {}: {},", field.name, default_val).ok();
144 }
145 writeln!(out, " }}").ok();
146 writeln!(out, " }}").ok();
147 write!(out, "}}").ok();
148 out
149}
150
151pub fn gen_opaque_struct(typ: &TypeDef, cfg: &RustBindingConfig) -> String {
154 let mut out = String::with_capacity(512);
155 if !cfg.struct_derives.is_empty() {
156 writeln!(out, "#[derive(Clone)]").ok();
157 }
158 for attr in cfg.struct_attrs {
159 writeln!(out, "#[{attr}]").ok();
160 }
161 writeln!(out, "pub struct {} {{", typ.name).ok();
162 let core_path = typ.rust_path.replace('-', "_");
163 if typ.is_trait {
164 writeln!(out, " inner: Arc<dyn {core_path} + Send + Sync>,").ok();
165 } else {
166 writeln!(out, " inner: Arc<{core_path}>,").ok();
167 }
168 write!(out, "}}").ok();
169 out
170}
171
172pub fn gen_opaque_struct_prefixed(typ: &TypeDef, cfg: &RustBindingConfig, prefix: &str) -> String {
174 let mut out = String::with_capacity(512);
175 if !cfg.struct_derives.is_empty() {
176 writeln!(out, "#[derive(Clone)]").ok();
177 }
178 for attr in cfg.struct_attrs {
179 writeln!(out, "#[{attr}]").ok();
180 }
181 let core_path = typ.rust_path.replace('-', "_");
182 writeln!(out, "pub struct {}{} {{", prefix, typ.name).ok();
183 if typ.is_trait {
184 writeln!(out, " inner: Arc<dyn {core_path} + Send + Sync>,").ok();
185 } else {
186 writeln!(out, " inner: Arc<{core_path}>,").ok();
187 }
188 write!(out, "}}").ok();
189 out
190}