1mod 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_copy: false,
178 is_trait: false,
179 has_default: false,
180 has_stripped_cfg_fields: false,
181 is_return_type: false,
182 serde_rename_all: None,
183 has_serde: false,
184 super_traits: vec![],
185 doc: String::new(),
186 cfg: None,
187 }
188 }
189
190 fn simple_enum() -> EnumDef {
191 EnumDef {
192 name: "Backend".to_string(),
193 rust_path: "my_crate::Backend".to_string(),
194 original_rust_path: String::new(),
195 variants: vec![
196 EnumVariant {
197 name: "Cpu".into(),
198 fields: vec![],
199 is_tuple: false,
200 doc: String::new(),
201 is_default: false,
202 serde_rename: None,
203 },
204 EnumVariant {
205 name: "Gpu".into(),
206 fields: vec![],
207 is_tuple: false,
208 doc: String::new(),
209 is_default: false,
210 serde_rename: None,
211 },
212 ],
213 doc: String::new(),
214 cfg: None,
215 is_copy: false,
216 has_serde: false,
217 serde_tag: None,
218 serde_rename_all: None,
219 }
220 }
221
222 #[test]
223 fn test_from_binding_to_core() {
224 let typ = simple_type();
225 let result = gen_from_binding_to_core(&typ, "my_crate");
226 assert!(result.contains("impl From<Config> for my_crate::Config"));
227 assert!(result.contains("name: val.name"));
228 assert!(result.contains("timeout: val.timeout"));
229 assert!(result.contains("backend: val.backend.map(Into::into)"));
230 }
231
232 #[test]
233 fn test_from_core_to_binding() {
234 let typ = simple_type();
235 let result = gen_from_core_to_binding(&typ, "my_crate", &AHashSet::new());
236 assert!(result.contains("impl From<my_crate::Config> for Config"));
237 }
238
239 #[test]
240 fn test_enum_from_binding_to_core() {
241 let enum_def = simple_enum();
242 let result = gen_enum_from_binding_to_core(&enum_def, "my_crate");
243 assert!(result.contains("impl From<Backend> for my_crate::Backend"));
244 assert!(result.contains("Backend::Cpu => Self::Cpu"));
245 assert!(result.contains("Backend::Gpu => Self::Gpu"));
246 }
247
248 #[test]
249 fn test_enum_from_core_to_binding() {
250 let enum_def = simple_enum();
251 let result = gen_enum_from_core_to_binding(&enum_def, "my_crate");
252 assert!(result.contains("impl From<my_crate::Backend> for Backend"));
253 assert!(result.contains("my_crate::Backend::Cpu => Self::Cpu"));
254 assert!(result.contains("my_crate::Backend::Gpu => Self::Gpu"));
255 }
256
257 #[test]
258 fn test_from_binding_to_core_with_cfg_gated_field() {
259 let mut typ = simple_type();
261 typ.has_stripped_cfg_fields = true;
262 typ.fields.push(FieldDef {
263 name: "layout".into(),
264 ty: TypeRef::String,
265 optional: false,
266 default: None,
267 doc: String::new(),
268 sanitized: false,
269 is_boxed: false,
270 type_rust_path: None,
271 cfg: Some("feature = \"layout-detection\"".into()),
272 typed_default: None,
273 core_wrapper: CoreWrapper::None,
274 vec_inner_core_wrapper: CoreWrapper::None,
275 newtype_wrapper: None,
276 });
277
278 let result = gen_from_binding_to_core(&typ, "my_crate");
279
280 assert!(result.contains("impl From<Config> for my_crate::Config"));
282 assert!(result.contains("name: val.name"));
284 assert!(result.contains("timeout: val.timeout"));
285 assert!(!result.contains("layout: val.layout"));
287 assert!(result.contains("..Default::default()"));
289 }
290
291 #[test]
292 fn test_from_core_to_binding_with_cfg_gated_field() {
293 let mut typ = simple_type();
295 typ.fields.push(FieldDef {
296 name: "layout".into(),
297 ty: TypeRef::String,
298 optional: false,
299 default: None,
300 doc: String::new(),
301 sanitized: false,
302 is_boxed: false,
303 type_rust_path: None,
304 cfg: Some("feature = \"layout-detection\"".into()),
305 typed_default: None,
306 core_wrapper: CoreWrapper::None,
307 vec_inner_core_wrapper: CoreWrapper::None,
308 newtype_wrapper: None,
309 });
310
311 let result = gen_from_core_to_binding(&typ, "my_crate", &AHashSet::new());
312
313 assert!(result.contains("impl From<my_crate::Config> for Config"));
315 assert!(result.contains("name: val.name"));
317 assert!(!result.contains("layout:"));
319 }
320}