alef_codegen/conversions/
mod.rs1mod binding_to_core;
2mod core_to_binding;
3mod enums;
4pub(crate) mod helpers;
5
6use ahash::AHashSet;
7
8#[derive(Default, Clone)]
11pub struct ConversionConfig<'a> {
12 pub type_name_prefix: &'a str,
14 pub cast_large_ints_to_i64: bool,
16 pub enum_string_names: Option<&'a AHashSet<String>>,
19 pub map_uses_jsvalue: bool,
23 pub cast_f32_to_f64: bool,
25 pub optionalize_defaults: bool,
29 pub json_to_string: bool,
33 pub include_cfg_metadata: bool,
36 pub option_duration_on_defaults: bool,
42 pub binding_enums_have_data: bool,
45 pub exclude_types: &'a [String],
49 pub vec_named_to_string: bool,
54 pub opaque_types: Option<&'a AHashSet<String>>,
59 pub binding_field_renames: Option<&'a std::collections::HashMap<String, String>>,
67}
68
69impl<'a> ConversionConfig<'a> {
70 pub fn binding_field_name<'b>(&self, type_name: &str, field_name: &'b str) -> &'b str
75 where
76 'a: 'b,
77 {
78 let _ = type_name;
83 field_name
84 }
85
86 pub fn binding_field_name_owned(&self, type_name: &str, field_name: &str) -> String {
89 if let Some(map) = self.binding_field_renames {
90 let key = format!("{type_name}.{field_name}");
91 if let Some(renamed) = map.get(&key) {
92 return renamed.clone();
93 }
94 }
95 field_name.to_string()
96 }
97}
98
99pub use binding_to_core::{
101 field_conversion_to_core, field_conversion_to_core_cfg, gen_from_binding_to_core, gen_from_binding_to_core_cfg,
102};
103pub use core_to_binding::{
104 field_conversion_from_core, field_conversion_from_core_cfg, gen_from_core_to_binding, gen_from_core_to_binding_cfg,
105};
106pub use enums::{
107 gen_enum_from_binding_to_core, gen_enum_from_binding_to_core_cfg, gen_enum_from_core_to_binding,
108 gen_enum_from_core_to_binding_cfg,
109};
110pub use helpers::{
111 binding_to_core_match_arm, build_type_path_map, can_generate_conversion, can_generate_enum_conversion,
112 can_generate_enum_conversion_from_core, convertible_types, core_enum_path, core_to_binding_convertible_types,
113 core_to_binding_match_arm, core_type_path, field_references_excluded_type, has_sanitized_fields, input_type_names,
114 is_tuple_variant, resolve_named_path,
115};
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120 use alef_core::ir::*;
121
122 fn simple_type() -> TypeDef {
123 TypeDef {
124 name: "Config".to_string(),
125 rust_path: "my_crate::Config".to_string(),
126 original_rust_path: String::new(),
127 fields: vec![
128 FieldDef {
129 name: "name".into(),
130 ty: TypeRef::String,
131 optional: false,
132 default: None,
133 doc: String::new(),
134 sanitized: false,
135 is_boxed: false,
136 type_rust_path: None,
137 cfg: None,
138 typed_default: None,
139 core_wrapper: CoreWrapper::None,
140 vec_inner_core_wrapper: CoreWrapper::None,
141 newtype_wrapper: None,
142 },
143 FieldDef {
144 name: "timeout".into(),
145 ty: TypeRef::Primitive(PrimitiveType::U64),
146 optional: true,
147 default: None,
148 doc: String::new(),
149 sanitized: false,
150 is_boxed: false,
151 type_rust_path: None,
152 cfg: None,
153 typed_default: None,
154 core_wrapper: CoreWrapper::None,
155 vec_inner_core_wrapper: CoreWrapper::None,
156 newtype_wrapper: None,
157 },
158 FieldDef {
159 name: "backend".into(),
160 ty: TypeRef::Named("Backend".into()),
161 optional: true,
162 default: None,
163 doc: String::new(),
164 sanitized: false,
165 is_boxed: false,
166 type_rust_path: None,
167 cfg: None,
168 typed_default: None,
169 core_wrapper: CoreWrapper::None,
170 vec_inner_core_wrapper: CoreWrapper::None,
171 newtype_wrapper: None,
172 },
173 ],
174 methods: vec![],
175 is_opaque: false,
176 is_clone: true,
177 is_trait: false,
178 has_default: false,
179 has_stripped_cfg_fields: false,
180 is_return_type: false,
181 serde_rename_all: None,
182 has_serde: false,
183 super_traits: vec![],
184 doc: String::new(),
185 cfg: None,
186 }
187 }
188
189 fn simple_enum() -> EnumDef {
190 EnumDef {
191 name: "Backend".to_string(),
192 rust_path: "my_crate::Backend".to_string(),
193 original_rust_path: String::new(),
194 variants: vec![
195 EnumVariant {
196 name: "Cpu".into(),
197 fields: vec![],
198 doc: String::new(),
199 is_default: false,
200 serde_rename: None,
201 },
202 EnumVariant {
203 name: "Gpu".into(),
204 fields: vec![],
205 doc: String::new(),
206 is_default: false,
207 serde_rename: None,
208 },
209 ],
210 doc: String::new(),
211 cfg: None,
212 serde_tag: None,
213 serde_rename_all: None,
214 }
215 }
216
217 #[test]
218 fn test_from_binding_to_core() {
219 let typ = simple_type();
220 let result = gen_from_binding_to_core(&typ, "my_crate");
221 assert!(result.contains("impl From<Config> for my_crate::Config"));
222 assert!(result.contains("name: val.name"));
223 assert!(result.contains("timeout: val.timeout"));
224 assert!(result.contains("backend: val.backend.map(Into::into)"));
225 }
226
227 #[test]
228 fn test_from_core_to_binding() {
229 let typ = simple_type();
230 let result = gen_from_core_to_binding(&typ, "my_crate", &AHashSet::new());
231 assert!(result.contains("impl From<my_crate::Config> for Config"));
232 }
233
234 #[test]
235 fn test_enum_from_binding_to_core() {
236 let enum_def = simple_enum();
237 let result = gen_enum_from_binding_to_core(&enum_def, "my_crate");
238 assert!(result.contains("impl From<Backend> for my_crate::Backend"));
239 assert!(result.contains("Backend::Cpu => Self::Cpu"));
240 assert!(result.contains("Backend::Gpu => Self::Gpu"));
241 }
242
243 #[test]
244 fn test_enum_from_core_to_binding() {
245 let enum_def = simple_enum();
246 let result = gen_enum_from_core_to_binding(&enum_def, "my_crate");
247 assert!(result.contains("impl From<my_crate::Backend> for Backend"));
248 assert!(result.contains("my_crate::Backend::Cpu => Self::Cpu"));
249 assert!(result.contains("my_crate::Backend::Gpu => Self::Gpu"));
250 }
251
252 #[test]
253 fn test_from_binding_to_core_with_cfg_gated_field() {
254 let mut typ = simple_type();
256 typ.has_stripped_cfg_fields = true;
257 typ.fields.push(FieldDef {
258 name: "layout".into(),
259 ty: TypeRef::String,
260 optional: false,
261 default: None,
262 doc: String::new(),
263 sanitized: false,
264 is_boxed: false,
265 type_rust_path: None,
266 cfg: Some("feature = \"layout-detection\"".into()),
267 typed_default: None,
268 core_wrapper: CoreWrapper::None,
269 vec_inner_core_wrapper: CoreWrapper::None,
270 newtype_wrapper: None,
271 });
272
273 let result = gen_from_binding_to_core(&typ, "my_crate");
274
275 assert!(result.contains("impl From<Config> for my_crate::Config"));
277 assert!(result.contains("name: val.name"));
279 assert!(result.contains("timeout: val.timeout"));
280 assert!(!result.contains("layout: val.layout"));
282 assert!(result.contains("..Default::default()"));
284 }
285
286 #[test]
287 fn test_from_core_to_binding_with_cfg_gated_field() {
288 let mut typ = simple_type();
290 typ.fields.push(FieldDef {
291 name: "layout".into(),
292 ty: TypeRef::String,
293 optional: false,
294 default: None,
295 doc: String::new(),
296 sanitized: false,
297 is_boxed: false,
298 type_rust_path: None,
299 cfg: Some("feature = \"layout-detection\"".into()),
300 typed_default: None,
301 core_wrapper: CoreWrapper::None,
302 vec_inner_core_wrapper: CoreWrapper::None,
303 newtype_wrapper: None,
304 });
305
306 let result = gen_from_core_to_binding(&typ, "my_crate", &AHashSet::new());
307
308 assert!(result.contains("impl From<my_crate::Config> for Config"));
310 assert!(result.contains("name: val.name"));
312 assert!(!result.contains("layout:"));
314 }
315}