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
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 gen_struct_with_per_field_attrs(
72 typ: &TypeDef,
73 mapper: &dyn TypeMapper,
74 cfg: &RustBindingConfig,
75 extra_field_attrs: impl Fn(&alef_core::ir::FieldDef) -> Vec<String>,
76) -> String {
77 let mut sb = StructBuilder::new(&typ.name);
78 for attr in cfg.struct_attrs {
79 sb.add_attr(attr);
80 }
81
82 let field_names: Vec<_> = typ.fields.iter().filter(|f| f.cfg.is_none()).map(|f| &f.name).collect();
84 if has_similar_names(&field_names) {
85 sb.add_attr("allow(clippy::similar_names)");
86 }
87
88 for d in cfg.struct_derives {
89 sb.add_derive(d);
90 }
91 sb.add_derive("Default");
95 sb.add_derive("serde::Serialize");
96 sb.add_derive("serde::Deserialize");
97 for field in &typ.fields {
98 if field.cfg.is_some() {
99 continue;
100 }
101 let force_optional = cfg.option_duration_on_defaults
102 && typ.has_default
103 && !field.optional
104 && matches!(field.ty, TypeRef::Duration);
105 let ty = if (field.optional || force_optional) && !matches!(field.ty, TypeRef::Optional(_)) {
106 mapper.optional(&mapper.map_type(&field.ty))
107 } else {
108 mapper.map_type(&field.ty)
110 };
111 let mut attrs: Vec<String> = cfg.field_attrs.iter().map(|a| a.to_string()).collect();
112 attrs.extend(extra_field_attrs(field));
113 sb.add_field_with_doc(&field.name, &ty, attrs, &field.doc);
114 }
115 sb.build()
116}
117
118pub fn gen_struct(typ: &TypeDef, mapper: &dyn TypeMapper, cfg: &RustBindingConfig) -> String {
120 let mut sb = StructBuilder::new(&typ.name);
121 for attr in cfg.struct_attrs {
122 sb.add_attr(attr);
123 }
124
125 let field_names: Vec<_> = typ.fields.iter().filter(|f| f.cfg.is_none()).map(|f| &f.name).collect();
127 if has_similar_names(&field_names) {
128 sb.add_attr("allow(clippy::similar_names)");
129 }
130
131 for d in cfg.struct_derives {
132 sb.add_derive(d);
133 }
134 sb.add_derive("Default");
138 sb.add_derive("serde::Serialize");
139 sb.add_derive("serde::Deserialize");
140 for field in &typ.fields {
141 if field.cfg.is_some() {
145 continue;
146 }
147 let force_optional = cfg.option_duration_on_defaults
151 && typ.has_default
152 && !field.optional
153 && matches!(field.ty, TypeRef::Duration);
154 let ty = if (field.optional || force_optional) && !matches!(field.ty, TypeRef::Optional(_)) {
155 mapper.optional(&mapper.map_type(&field.ty))
156 } else {
157 mapper.map_type(&field.ty)
159 };
160 let attrs: Vec<String> = cfg.field_attrs.iter().map(|a| a.to_string()).collect();
161 sb.add_field_with_doc(&field.name, &ty, attrs, &field.doc);
162 }
163 sb.build()
164}
165
166pub fn gen_struct_default_impl(typ: &TypeDef, name_prefix: &str) -> String {
175 let full_name = format!("{}{}", name_prefix, typ.name);
176 let mut out = String::with_capacity(256);
177 writeln!(out, "impl Default for {} {{", full_name).ok();
178 writeln!(out, " fn default() -> Self {{").ok();
179 writeln!(out, " Self {{").ok();
180 for field in &typ.fields {
181 if field.cfg.is_some() {
182 continue;
183 }
184 let default_val = match &field.ty {
185 TypeRef::Optional(_) => "None".to_string(),
186 _ => "Default::default()".to_string(),
187 };
188 writeln!(out, " {}: {},", field.name, default_val).ok();
189 }
190 writeln!(out, " }}").ok();
191 writeln!(out, " }}").ok();
192 write!(out, "}}").ok();
193 out
194}
195
196pub fn gen_opaque_struct(typ: &TypeDef, cfg: &RustBindingConfig) -> String {
199 let mut out = String::with_capacity(512);
200 if !cfg.struct_derives.is_empty() {
201 writeln!(out, "#[derive(Clone)]").ok();
202 }
203 for attr in cfg.struct_attrs {
204 writeln!(out, "#[{attr}]").ok();
205 }
206 writeln!(out, "pub struct {} {{", typ.name).ok();
207 let core_path = typ.rust_path.replace('-', "_");
208 if typ.is_trait {
209 writeln!(out, " inner: Arc<dyn {core_path} + Send + Sync>,").ok();
210 } else {
211 writeln!(out, " inner: Arc<{core_path}>,").ok();
212 }
213 write!(out, "}}").ok();
214 out
215}
216
217pub fn gen_opaque_struct_prefixed(typ: &TypeDef, cfg: &RustBindingConfig, prefix: &str) -> String {
219 let mut out = String::with_capacity(512);
220 if !cfg.struct_derives.is_empty() {
221 writeln!(out, "#[derive(Clone)]").ok();
222 }
223 for attr in cfg.struct_attrs {
224 writeln!(out, "#[{attr}]").ok();
225 }
226 let core_path = typ.rust_path.replace('-', "_");
227 writeln!(out, "pub struct {}{} {{", prefix, typ.name).ok();
228 if typ.is_trait {
229 writeln!(out, " inner: Arc<dyn {core_path} + Send + Sync>,").ok();
230 } else {
231 writeln!(out, " inner: Arc<{core_path}>,").ok();
232 }
233 write!(out, "}}").ok();
234 out
235}