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 serde_tag: None,
217 serde_rename_all: None,
218 }
219 }
220
221 #[test]
222 fn test_from_binding_to_core() {
223 let typ = simple_type();
224 let result = gen_from_binding_to_core(&typ, "my_crate");
225 assert!(result.contains("impl From<Config> for my_crate::Config"));
226 assert!(result.contains("name: val.name"));
227 assert!(result.contains("timeout: val.timeout"));
228 assert!(result.contains("backend: val.backend.map(Into::into)"));
229 }
230
231 #[test]
232 fn test_from_core_to_binding() {
233 let typ = simple_type();
234 let result = gen_from_core_to_binding(&typ, "my_crate", &AHashSet::new());
235 assert!(result.contains("impl From<my_crate::Config> for Config"));
236 }
237
238 #[test]
239 fn test_enum_from_binding_to_core() {
240 let enum_def = simple_enum();
241 let result = gen_enum_from_binding_to_core(&enum_def, "my_crate");
242 assert!(result.contains("impl From<Backend> for my_crate::Backend"));
243 assert!(result.contains("Backend::Cpu => Self::Cpu"));
244 assert!(result.contains("Backend::Gpu => Self::Gpu"));
245 }
246
247 #[test]
248 fn test_enum_from_core_to_binding() {
249 let enum_def = simple_enum();
250 let result = gen_enum_from_core_to_binding(&enum_def, "my_crate");
251 assert!(result.contains("impl From<my_crate::Backend> for Backend"));
252 assert!(result.contains("my_crate::Backend::Cpu => Self::Cpu"));
253 assert!(result.contains("my_crate::Backend::Gpu => Self::Gpu"));
254 }
255
256 #[test]
257 fn test_from_binding_to_core_with_cfg_gated_field() {
258 let mut typ = simple_type();
260 typ.has_stripped_cfg_fields = true;
261 typ.fields.push(FieldDef {
262 name: "layout".into(),
263 ty: TypeRef::String,
264 optional: false,
265 default: None,
266 doc: String::new(),
267 sanitized: false,
268 is_boxed: false,
269 type_rust_path: None,
270 cfg: Some("feature = \"layout-detection\"".into()),
271 typed_default: None,
272 core_wrapper: CoreWrapper::None,
273 vec_inner_core_wrapper: CoreWrapper::None,
274 newtype_wrapper: None,
275 });
276
277 let result = gen_from_binding_to_core(&typ, "my_crate");
278
279 assert!(result.contains("impl From<Config> for my_crate::Config"));
281 assert!(result.contains("name: val.name"));
283 assert!(result.contains("timeout: val.timeout"));
284 assert!(!result.contains("layout: val.layout"));
286 assert!(result.contains("..Default::default()"));
288 }
289
290 #[test]
291 fn test_from_core_to_binding_with_cfg_gated_field() {
292 let mut typ = simple_type();
294 typ.fields.push(FieldDef {
295 name: "layout".into(),
296 ty: TypeRef::String,
297 optional: false,
298 default: None,
299 doc: String::new(),
300 sanitized: false,
301 is_boxed: false,
302 type_rust_path: None,
303 cfg: Some("feature = \"layout-detection\"".into()),
304 typed_default: None,
305 core_wrapper: CoreWrapper::None,
306 vec_inner_core_wrapper: CoreWrapper::None,
307 newtype_wrapper: None,
308 });
309
310 let result = gen_from_core_to_binding(&typ, "my_crate", &AHashSet::new());
311
312 assert!(result.contains("impl From<my_crate::Config> for Config"));
314 assert!(result.contains("name: val.name"));
316 assert!(!result.contains("layout:"));
318 }
319}