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 typ.has_default {
45 sb.add_derive("Default");
46 }
47 if cfg.has_serde {
48 sb.add_derive("serde::Serialize");
49 }
50 for field in &typ.fields {
51 if field.cfg.is_some() {
55 continue;
56 }
57 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 attrs: Vec<String> = cfg.field_attrs.iter().map(|a| a.to_string()).collect();
70 sb.add_field_with_doc(&field.name, &ty, attrs, &field.doc);
71 }
72 sb.build()
73}
74
75pub fn gen_struct_default_impl(typ: &TypeDef, name_prefix: &str) -> String {
80 let full_name = format!("{}{}", name_prefix, typ.name);
81 let mut out = String::with_capacity(256);
82 writeln!(out, "impl Default for {} {{", full_name).ok();
83 writeln!(out, " fn default() -> Self {{").ok();
84 writeln!(out, " Self {{").ok();
85 for field in &typ.fields {
86 if field.cfg.is_some() {
87 continue;
88 }
89 let default_val = match &field.ty {
90 TypeRef::Optional(_) => "None".to_string(),
91 _ => "Default::default()".to_string(),
92 };
93 writeln!(out, " {}: {},", field.name, default_val).ok();
94 }
95 writeln!(out, " }}").ok();
96 writeln!(out, " }}").ok();
97 write!(out, "}}").ok();
98 out
99}
100
101pub fn gen_opaque_struct(typ: &TypeDef, cfg: &RustBindingConfig) -> String {
104 let mut out = String::with_capacity(512);
105 if !cfg.struct_derives.is_empty() {
106 writeln!(out, "#[derive(Clone)]").ok();
107 }
108 for attr in cfg.struct_attrs {
109 writeln!(out, "#[{attr}]").ok();
110 }
111 writeln!(out, "pub struct {} {{", typ.name).ok();
112 let core_path = typ.rust_path.replace('-', "_");
113 if typ.is_trait {
114 writeln!(out, " inner: Arc<dyn {core_path} + Send + Sync>,").ok();
115 } else {
116 writeln!(out, " inner: Arc<{core_path}>,").ok();
117 }
118 write!(out, "}}").ok();
119 out
120}
121
122pub fn gen_opaque_struct_prefixed(typ: &TypeDef, cfg: &RustBindingConfig, prefix: &str) -> String {
124 let mut out = String::with_capacity(512);
125 if !cfg.struct_derives.is_empty() {
126 writeln!(out, "#[derive(Clone)]").ok();
127 }
128 for attr in cfg.struct_attrs {
129 writeln!(out, "#[{attr}]").ok();
130 }
131 let core_path = typ.rust_path.replace('-', "_");
132 writeln!(out, "pub struct {}{} {{", prefix, typ.name).ok();
133 if typ.is_trait {
134 writeln!(out, " inner: Arc<dyn {core_path} + Send + Sync>,").ok();
135 } else {
136 writeln!(out, " inner: Arc<{core_path}>,").ok();
137 }
138 write!(out, "}}").ok();
139 out
140}