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}
55
56pub use binding_to_core::{
58 field_conversion_to_core, field_conversion_to_core_cfg, gen_from_binding_to_core, gen_from_binding_to_core_cfg,
59};
60pub use core_to_binding::{
61 field_conversion_from_core, field_conversion_from_core_cfg, gen_from_core_to_binding, gen_from_core_to_binding_cfg,
62};
63pub use enums::{
64 gen_enum_from_binding_to_core, gen_enum_from_binding_to_core_cfg, gen_enum_from_core_to_binding,
65 gen_enum_from_core_to_binding_cfg,
66};
67pub use helpers::{
68 binding_to_core_match_arm, build_type_path_map, can_generate_conversion, can_generate_enum_conversion,
69 can_generate_enum_conversion_from_core, convertible_types, core_enum_path, core_to_binding_convertible_types,
70 core_to_binding_match_arm, core_type_path, field_references_excluded_type, has_sanitized_fields, input_type_names,
71 is_tuple_variant, resolve_named_path,
72};
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77 use alef_core::ir::*;
78
79 fn make_field(name: &str, ty: TypeRef) -> FieldDef {
84 FieldDef {
85 name: name.into(),
86 ty,
87 optional: false,
88 default: None,
89 doc: String::new(),
90 sanitized: false,
91 is_boxed: false,
92 type_rust_path: None,
93 cfg: None,
94 typed_default: None,
95 core_wrapper: CoreWrapper::None,
96 vec_inner_core_wrapper: CoreWrapper::None,
97 newtype_wrapper: None,
98 }
99 }
100
101 fn make_opt_field(name: &str, ty: TypeRef) -> FieldDef {
102 FieldDef {
103 optional: true,
104 ..make_field(name, ty)
105 }
106 }
107
108 fn make_type(name: &str, rust_path: &str, fields: Vec<FieldDef>) -> TypeDef {
109 TypeDef {
110 name: name.into(),
111 rust_path: rust_path.into(),
112 original_rust_path: String::new(),
113 fields,
114 methods: vec![],
115 is_opaque: false,
116 is_clone: true,
117 is_trait: false,
118 has_default: false,
119 has_stripped_cfg_fields: false,
120 is_return_type: false,
121 serde_rename_all: None,
122 has_serde: false,
123 super_traits: vec![],
124 doc: String::new(),
125 cfg: None,
126 }
127 }
128
129 fn make_enum(name: &str, rust_path: &str, variants: &[&str]) -> EnumDef {
130 EnumDef {
131 name: name.into(),
132 rust_path: rust_path.into(),
133 original_rust_path: String::new(),
134 variants: variants
135 .iter()
136 .map(|v| EnumVariant {
137 name: (*v).into(),
138 fields: vec![],
139 doc: String::new(),
140 is_default: false,
141 serde_rename: None,
142 })
143 .collect(),
144 doc: String::new(),
145 cfg: None,
146 serde_tag: None,
147 serde_rename_all: None,
148 }
149 }
150
151 fn no_opaques() -> AHashSet<String> {
152 AHashSet::new()
153 }
154
155 fn simple_type() -> TypeDef {
156 TypeDef {
157 name: "Config".to_string(),
158 rust_path: "my_crate::Config".to_string(),
159 original_rust_path: String::new(),
160 fields: vec![
161 FieldDef {
162 name: "name".into(),
163 ty: TypeRef::String,
164 optional: false,
165 default: None,
166 doc: String::new(),
167 sanitized: false,
168 is_boxed: false,
169 type_rust_path: None,
170 cfg: None,
171 typed_default: None,
172 core_wrapper: CoreWrapper::None,
173 vec_inner_core_wrapper: CoreWrapper::None,
174 newtype_wrapper: None,
175 },
176 FieldDef {
177 name: "timeout".into(),
178 ty: TypeRef::Primitive(PrimitiveType::U64),
179 optional: true,
180 default: None,
181 doc: String::new(),
182 sanitized: false,
183 is_boxed: false,
184 type_rust_path: None,
185 cfg: None,
186 typed_default: None,
187 core_wrapper: CoreWrapper::None,
188 vec_inner_core_wrapper: CoreWrapper::None,
189 newtype_wrapper: None,
190 },
191 FieldDef {
192 name: "backend".into(),
193 ty: TypeRef::Named("Backend".into()),
194 optional: true,
195 default: None,
196 doc: String::new(),
197 sanitized: false,
198 is_boxed: false,
199 type_rust_path: None,
200 cfg: None,
201 typed_default: None,
202 core_wrapper: CoreWrapper::None,
203 vec_inner_core_wrapper: CoreWrapper::None,
204 newtype_wrapper: None,
205 },
206 ],
207 methods: vec![],
208 is_opaque: false,
209 is_clone: true,
210 is_trait: false,
211 has_default: false,
212 has_stripped_cfg_fields: false,
213 is_return_type: false,
214 serde_rename_all: None,
215 has_serde: false,
216 super_traits: vec![],
217 doc: String::new(),
218 cfg: None,
219 }
220 }
221
222 fn simple_enum() -> EnumDef {
223 EnumDef {
224 name: "Backend".to_string(),
225 rust_path: "my_crate::Backend".to_string(),
226 original_rust_path: String::new(),
227 variants: vec![
228 EnumVariant {
229 name: "Cpu".into(),
230 fields: vec![],
231 doc: String::new(),
232 is_default: false,
233 serde_rename: None,
234 },
235 EnumVariant {
236 name: "Gpu".into(),
237 fields: vec![],
238 doc: String::new(),
239 is_default: false,
240 serde_rename: None,
241 },
242 ],
243 doc: String::new(),
244 cfg: None,
245 serde_tag: None,
246 serde_rename_all: None,
247 }
248 }
249
250 #[test]
251 fn test_from_binding_to_core() {
252 let typ = simple_type();
253 let result = gen_from_binding_to_core(&typ, "my_crate");
254 assert!(result.contains("impl From<Config> for my_crate::Config"));
255 assert!(result.contains("name: val.name"));
256 assert!(result.contains("timeout: val.timeout"));
257 assert!(result.contains("backend: val.backend.map(Into::into)"));
258 }
259
260 #[test]
261 fn test_from_core_to_binding() {
262 let typ = simple_type();
263 let result = gen_from_core_to_binding(&typ, "my_crate", &AHashSet::new());
264 assert!(result.contains("impl From<my_crate::Config> for Config"));
265 }
266
267 #[test]
268 fn test_enum_from_binding_to_core() {
269 let enum_def = simple_enum();
270 let result = gen_enum_from_binding_to_core(&enum_def, "my_crate");
271 assert!(result.contains("impl From<Backend> for my_crate::Backend"));
272 assert!(result.contains("Backend::Cpu => Self::Cpu"));
273 assert!(result.contains("Backend::Gpu => Self::Gpu"));
274 }
275
276 #[test]
277 fn test_enum_from_core_to_binding() {
278 let enum_def = simple_enum();
279 let result = gen_enum_from_core_to_binding(&enum_def, "my_crate");
280 assert!(result.contains("impl From<my_crate::Backend> for Backend"));
281 assert!(result.contains("my_crate::Backend::Cpu => Self::Cpu"));
282 assert!(result.contains("my_crate::Backend::Gpu => Self::Gpu"));
283 }
284
285 #[test]
286 fn test_from_binding_to_core_with_cfg_gated_field() {
287 let mut typ = simple_type();
289 typ.has_stripped_cfg_fields = true;
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_binding_to_core(&typ, "my_crate");
307
308 assert!(result.contains("impl From<Config> for my_crate::Config"));
310 assert!(result.contains("name: val.name"));
312 assert!(result.contains("timeout: val.timeout"));
313 assert!(!result.contains("layout: val.layout"));
315 assert!(result.contains("..Default::default()"));
317 }
318
319 #[test]
320 fn test_from_core_to_binding_with_cfg_gated_field() {
321 let mut typ = simple_type();
323 typ.fields.push(FieldDef {
324 name: "layout".into(),
325 ty: TypeRef::String,
326 optional: false,
327 default: None,
328 doc: String::new(),
329 sanitized: false,
330 is_boxed: false,
331 type_rust_path: None,
332 cfg: Some("feature = \"layout-detection\"".into()),
333 typed_default: None,
334 core_wrapper: CoreWrapper::None,
335 vec_inner_core_wrapper: CoreWrapper::None,
336 newtype_wrapper: None,
337 });
338
339 let result = gen_from_core_to_binding(&typ, "my_crate", &AHashSet::new());
340
341 assert!(result.contains("impl From<my_crate::Config> for Config"));
343 assert!(result.contains("name: val.name"));
345 assert!(!result.contains("layout:"));
347 }
348
349 #[test]
354 fn test_field_conversion_to_core_string() {
355 let result = field_conversion_to_core("label", &TypeRef::String, false);
356 assert_eq!(result, "label: val.label");
357 }
358
359 #[test]
360 fn test_field_conversion_to_core_primitive() {
361 let result = field_conversion_to_core("count", &TypeRef::Primitive(PrimitiveType::I32), false);
362 assert_eq!(result, "count: val.count");
363 }
364
365 #[test]
366 fn test_field_conversion_to_core_bytes() {
367 let result = field_conversion_to_core("data", &TypeRef::Bytes, false);
368 assert_eq!(result, "data: val.data");
369 }
370
371 #[test]
372 fn test_field_conversion_to_core_unit() {
373 let result = field_conversion_to_core("nothing", &TypeRef::Unit, false);
374 assert_eq!(result, "nothing: val.nothing");
375 }
376
377 #[test]
378 fn test_field_conversion_to_core_duration_non_optional() {
379 let result = field_conversion_to_core("timeout", &TypeRef::Duration, false);
380 assert_eq!(result, "timeout: std::time::Duration::from_millis(val.timeout)");
381 }
382
383 #[test]
384 fn test_field_conversion_to_core_duration_optional() {
385 let result = field_conversion_to_core("timeout", &TypeRef::Duration, true);
386 assert_eq!(result, "timeout: val.timeout.map(std::time::Duration::from_millis)");
387 }
388
389 #[test]
390 fn test_field_conversion_to_core_path_non_optional() {
391 let result = field_conversion_to_core("file", &TypeRef::Path, false);
392 assert_eq!(result, "file: val.file.into()");
393 }
394
395 #[test]
396 fn test_field_conversion_to_core_path_optional() {
397 let result = field_conversion_to_core("file", &TypeRef::Path, true);
398 assert_eq!(result, "file: val.file.map(Into::into)");
399 }
400
401 #[test]
402 fn test_field_conversion_to_core_json_non_optional() {
403 let result = field_conversion_to_core("meta", &TypeRef::Json, false);
404 assert_eq!(result, "meta: serde_json::from_str(&val.meta).unwrap_or_default()");
405 }
406
407 #[test]
408 fn test_field_conversion_to_core_json_optional() {
409 let result = field_conversion_to_core("meta", &TypeRef::Json, true);
410 assert_eq!(
411 result,
412 "meta: val.meta.as_ref().and_then(|s| serde_json::from_str(s).ok())"
413 );
414 }
415
416 #[test]
417 fn test_field_conversion_to_core_char_non_optional() {
418 let result = field_conversion_to_core("sep", &TypeRef::Char, false);
419 assert_eq!(result, "sep: val.sep.chars().next().unwrap_or('*')");
420 }
421
422 #[test]
423 fn test_field_conversion_to_core_char_optional() {
424 let result = field_conversion_to_core("sep", &TypeRef::Char, true);
425 assert_eq!(result, "sep: val.sep.and_then(|s| s.chars().next())");
426 }
427
428 #[test]
429 fn test_field_conversion_to_core_named_non_optional() {
430 let result = field_conversion_to_core("backend", &TypeRef::Named("Backend".into()), false);
431 assert_eq!(result, "backend: val.backend.into()");
432 }
433
434 #[test]
435 fn test_field_conversion_to_core_named_optional() {
436 let result = field_conversion_to_core("backend", &TypeRef::Named("Backend".into()), true);
437 assert_eq!(result, "backend: val.backend.map(Into::into)");
438 }
439
440 #[test]
441 fn test_field_conversion_to_core_named_tuple_type_is_passthrough() {
442 let result = field_conversion_to_core("pair", &TypeRef::Named("(String, u32)".into()), false);
444 assert_eq!(result, "pair: val.pair");
445 }
446
447 #[test]
448 fn test_field_conversion_to_core_vec_named() {
449 let ty = TypeRef::Vec(Box::new(TypeRef::Named("Item".into())));
450 let result = field_conversion_to_core("items", &ty, false);
451 assert_eq!(result, "items: val.items.into_iter().map(Into::into).collect()");
452 }
453
454 #[test]
455 fn test_field_conversion_to_core_vec_named_optional() {
456 let ty = TypeRef::Vec(Box::new(TypeRef::Named("Item".into())));
457 let result = field_conversion_to_core("items", &ty, true);
458 assert_eq!(
459 result,
460 "items: val.items.map(|v| v.into_iter().map(Into::into).collect())"
461 );
462 }
463
464 #[test]
465 fn test_field_conversion_to_core_vec_tuple_passthrough() {
466 let ty = TypeRef::Vec(Box::new(TypeRef::Named("(u32, u32)".into())));
467 let result = field_conversion_to_core("pairs", &ty, false);
468 assert_eq!(result, "pairs: val.pairs");
469 }
470
471 #[test]
472 fn test_field_conversion_to_core_vec_json() {
473 let ty = TypeRef::Vec(Box::new(TypeRef::Json));
474 let result = field_conversion_to_core("items", &ty, false);
475 assert_eq!(
476 result,
477 "items: val.items.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect()"
478 );
479 }
480
481 #[test]
482 fn test_field_conversion_to_core_optional_named() {
483 let ty = TypeRef::Optional(Box::new(TypeRef::Named("Config".into())));
484 let result = field_conversion_to_core("config", &ty, false);
485 assert_eq!(result, "config: val.config.map(Into::into)");
486 }
487
488 #[test]
489 fn test_field_conversion_to_core_optional_vec_named() {
490 let ty = TypeRef::Optional(Box::new(TypeRef::Vec(Box::new(TypeRef::Named("Item".into())))));
491 let result = field_conversion_to_core("items", &ty, false);
492 assert_eq!(
493 result,
494 "items: val.items.map(|v| v.into_iter().map(Into::into).collect())"
495 );
496 }
497
498 #[test]
499 fn test_field_conversion_to_core_map_string_string() {
500 let ty = TypeRef::Map(Box::new(TypeRef::String), Box::new(TypeRef::String));
501 let result = field_conversion_to_core("map", &ty, false);
502 assert_eq!(result, "map: val.map.into_iter().collect()");
503 }
504
505 #[test]
506 fn test_field_conversion_to_core_map_string_json() {
507 let ty = TypeRef::Map(Box::new(TypeRef::String), Box::new(TypeRef::Json));
508 let result = field_conversion_to_core("map", &ty, false);
509 assert!(result.contains("serde_json::from_str(&v)"));
510 }
511
512 #[test]
513 fn test_field_conversion_to_core_map_named_values() {
514 let ty = TypeRef::Map(Box::new(TypeRef::String), Box::new(TypeRef::Named("Val".into())));
515 let result = field_conversion_to_core("map", &ty, false);
516 assert!(result.contains("v.into()"));
517 }
518
519 #[test]
524 fn test_field_conversion_from_core_string() {
525 let result = field_conversion_from_core("label", &TypeRef::String, false, false, &no_opaques());
526 assert_eq!(result, "label: val.label");
527 }
528
529 #[test]
530 fn test_field_conversion_from_core_duration_non_optional() {
531 let result = field_conversion_from_core("timeout", &TypeRef::Duration, false, false, &no_opaques());
532 assert_eq!(result, "timeout: val.timeout.as_millis() as u64");
533 }
534
535 #[test]
536 fn test_field_conversion_from_core_duration_optional() {
537 let result = field_conversion_from_core("timeout", &TypeRef::Duration, true, false, &no_opaques());
538 assert_eq!(result, "timeout: val.timeout.map(|d| d.as_millis() as u64)");
539 }
540
541 #[test]
542 fn test_field_conversion_from_core_path_non_optional() {
543 let result = field_conversion_from_core("file", &TypeRef::Path, false, false, &no_opaques());
544 assert_eq!(result, "file: val.file.to_string_lossy().to_string()");
545 }
546
547 #[test]
548 fn test_field_conversion_from_core_path_optional() {
549 let result = field_conversion_from_core("file", &TypeRef::Path, true, false, &no_opaques());
550 assert_eq!(result, "file: val.file.map(|p| p.to_string_lossy().to_string())");
551 }
552
553 #[test]
554 fn test_field_conversion_from_core_char_non_optional() {
555 let result = field_conversion_from_core("sep", &TypeRef::Char, false, false, &no_opaques());
556 assert_eq!(result, "sep: val.sep.to_string()");
557 }
558
559 #[test]
560 fn test_field_conversion_from_core_char_optional() {
561 let result = field_conversion_from_core("sep", &TypeRef::Char, true, false, &no_opaques());
562 assert_eq!(result, "sep: val.sep.map(|c| c.to_string())");
563 }
564
565 #[test]
566 fn test_field_conversion_from_core_bytes_non_optional() {
567 let result = field_conversion_from_core("data", &TypeRef::Bytes, false, false, &no_opaques());
568 assert_eq!(result, "data: val.data.to_vec()");
569 }
570
571 #[test]
572 fn test_field_conversion_from_core_bytes_optional() {
573 let result = field_conversion_from_core("data", &TypeRef::Bytes, true, false, &no_opaques());
574 assert_eq!(result, "data: val.data.map(|v| v.to_vec())");
575 }
576
577 #[test]
578 fn test_field_conversion_from_core_json_non_optional() {
579 let result = field_conversion_from_core("meta", &TypeRef::Json, false, false, &no_opaques());
580 assert_eq!(result, "meta: val.meta.to_string()");
581 }
582
583 #[test]
584 fn test_field_conversion_from_core_json_optional() {
585 let result = field_conversion_from_core("meta", &TypeRef::Json, true, false, &no_opaques());
586 assert_eq!(result, "meta: val.meta.as_ref().map(ToString::to_string)");
587 }
588
589 #[test]
590 fn test_field_conversion_from_core_named_non_opaque() {
591 let result = field_conversion_from_core(
593 "backend",
594 &TypeRef::Named("Backend".into()),
595 false,
596 false,
597 &no_opaques(),
598 );
599 assert_eq!(result, "backend: val.backend.into()");
600 }
601
602 #[test]
603 fn test_field_conversion_from_core_opaque_non_optional() {
604 let mut opaques = AHashSet::new();
605 opaques.insert("Client".to_string());
606 let result = field_conversion_from_core("client", &TypeRef::Named("Client".into()), false, false, &opaques);
607 assert_eq!(result, "client: Client { inner: Arc::new(val.client) }");
608 }
609
610 #[test]
611 fn test_field_conversion_from_core_opaque_optional() {
612 let mut opaques = AHashSet::new();
613 opaques.insert("Client".to_string());
614 let result = field_conversion_from_core("client", &TypeRef::Named("Client".into()), true, false, &opaques);
615 assert_eq!(result, "client: val.client.map(|v| Client { inner: Arc::new(v) })");
616 }
617
618 #[test]
619 fn test_field_conversion_from_core_vec_json() {
620 let ty = TypeRef::Vec(Box::new(TypeRef::Json));
621 let result = field_conversion_from_core("items", &ty, false, false, &no_opaques());
622 assert_eq!(result, "items: val.items.iter().map(ToString::to_string).collect()");
623 }
624
625 #[test]
626 fn test_field_conversion_from_core_map_json_values() {
627 let ty = TypeRef::Map(Box::new(TypeRef::String), Box::new(TypeRef::Json));
628 let result = field_conversion_from_core("map", &ty, false, false, &no_opaques());
629 assert!(result.contains("v.to_string()"));
630 }
631
632 #[test]
633 fn test_field_conversion_from_core_sanitized_string() {
634 let result = field_conversion_from_core("value", &TypeRef::String, false, true, &no_opaques());
636 assert_eq!(result, "value: format!(\"{:?}\", val.value)");
637 }
638
639 #[test]
640 fn test_field_conversion_from_core_sanitized_string_optional() {
641 let result = field_conversion_from_core("value", &TypeRef::String, true, true, &no_opaques());
642 assert_eq!(result, "value: val.value.as_ref().map(|v| format!(\"{v:?}\"))");
643 }
644
645 #[test]
646 fn test_field_conversion_from_core_sanitized_named_non_optional() {
647 let result = field_conversion_from_core("obj", &TypeRef::Named("Opaque".into()), false, true, &no_opaques());
649 assert_eq!(result, "obj: String::new()");
650 }
651
652 #[test]
653 fn test_field_conversion_from_core_sanitized_named_optional() {
654 let result = field_conversion_from_core("obj", &TypeRef::Named("Opaque".into()), true, true, &no_opaques());
656 assert_eq!(result, "obj: None");
657 }
658
659 #[test]
660 fn test_field_conversion_from_core_sanitized_vec_string() {
661 let ty = TypeRef::Vec(Box::new(TypeRef::String));
662 let result = field_conversion_from_core("tags", &ty, false, true, &no_opaques());
663 assert!(result.contains(r#"format!("{:?}", i)"#));
665 assert!(result.contains(".iter().map("));
666 }
667
668 #[test]
669 fn test_field_conversion_from_core_sanitized_map_string_string() {
670 let ty = TypeRef::Map(Box::new(TypeRef::String), Box::new(TypeRef::String));
671 let result = field_conversion_from_core("headers", &ty, false, true, &no_opaques());
672 assert!(result.contains("k.to_string()"));
673 assert!(result.contains("v.to_string()"));
674 }
675
676 #[test]
677 fn test_field_conversion_from_core_sanitized_vec_u32_non_optional() {
678 let ty = TypeRef::Vec(Box::new(TypeRef::Primitive(PrimitiveType::U32)));
680 let result = field_conversion_from_core("dimensions", &ty, false, true, &no_opaques());
681 assert!(
682 result.contains("serde_json::to_value"),
683 "should use serde_json::to_value for tuple-to-vec conversion, got: {result}"
684 );
685 assert!(
686 result.contains("serde_json::from_value"),
687 "should use serde_json::from_value for tuple-to-vec conversion, got: {result}"
688 );
689 assert!(
690 !result.contains("format!"),
691 "should NOT use format! debug for Vec<Primitive> sanitized field, got: {result}"
692 );
693 }
694
695 #[test]
696 fn test_field_conversion_from_core_sanitized_vec_u32_optional() {
697 let ty = TypeRef::Vec(Box::new(TypeRef::Primitive(PrimitiveType::U32)));
699 let result = field_conversion_from_core("dimensions", &ty, true, true, &no_opaques());
700 assert!(
701 result.contains("serde_json::to_value"),
702 "should use serde_json::to_value for optional tuple-to-vec conversion, got: {result}"
703 );
704 assert!(
705 result.contains("serde_json::from_value"),
706 "should use serde_json::from_value for optional tuple-to-vec conversion, got: {result}"
707 );
708 assert!(
709 !result.contains("format!"),
710 "should NOT use format! debug for optional Vec<Primitive> sanitized field, got: {result}"
711 );
712 }
713
714 #[test]
715 fn test_field_conversion_from_core_sanitized_optional_vec_u32() {
716 let ty = TypeRef::Optional(Box::new(TypeRef::Vec(Box::new(TypeRef::Primitive(PrimitiveType::U32)))));
718 let result = field_conversion_from_core("dimensions", &ty, false, true, &no_opaques());
719 assert!(
720 result.contains("serde_json::to_value"),
721 "should use serde_json::to_value for Optional(Vec(Primitive)) sanitized field, got: {result}"
722 );
723 assert!(
724 result.contains("serde_json::from_value"),
725 "should use serde_json::from_value for Optional(Vec(Primitive)) sanitized field, got: {result}"
726 );
727 assert!(
728 !result.contains("format!"),
729 "should NOT use format! debug for Optional(Vec(Primitive)) sanitized field, got: {result}"
730 );
731 }
732
733 #[test]
734 fn test_gen_from_binding_to_core_sanitized_vec_u32_uses_serde() {
735 let ty = TypeRef::Vec(Box::new(TypeRef::Primitive(PrimitiveType::U32)));
737 let mut field = make_opt_field("dimensions", ty);
738 field.sanitized = true;
739 let typ = make_type("S", "c::S", vec![field]);
740 let result = gen_from_binding_to_core(&typ, "c");
741 assert!(
742 result.contains("serde_json::to_value"),
743 "binding→core for sanitized Vec<Primitive> should use serde_json::to_value, got:\n{result}"
744 );
745 assert!(
746 result.contains("serde_json::from_value"),
747 "binding→core for sanitized Vec<Primitive> should use serde_json::from_value, got:\n{result}"
748 );
749 assert!(
750 !result.contains("Default::default()"),
751 "binding→core for sanitized Vec<Primitive> should NOT use Default::default(), got:\n{result}"
752 );
753 }
754
755 #[test]
760 fn test_is_tuple_variant_true_for_positional_fields() {
761 let fields = vec![
762 make_field("_0", TypeRef::String),
763 make_field("_1", TypeRef::Primitive(PrimitiveType::I32)),
764 ];
765 assert!(is_tuple_variant(&fields));
766 }
767
768 #[test]
769 fn test_is_tuple_variant_false_for_named_fields() {
770 let fields = vec![make_field("name", TypeRef::String)];
771 assert!(!is_tuple_variant(&fields));
772 }
773
774 #[test]
775 fn test_is_tuple_variant_false_for_empty_fields() {
776 assert!(!is_tuple_variant(&[]));
777 }
778
779 #[test]
780 fn test_is_tuple_type_name_true() {
781 assert!(helpers::is_tuple_type_name("(String, u32)"));
782 }
783
784 #[test]
785 fn test_is_tuple_type_name_false() {
786 assert!(!helpers::is_tuple_type_name("Config"));
787 }
788
789 #[test]
794 fn test_field_references_excluded_type_direct_match() {
795 let ty = TypeRef::Named("JsValue".into());
796 assert!(field_references_excluded_type(&ty, &["JsValue".to_string()]));
797 }
798
799 #[test]
800 fn test_field_references_excluded_type_no_match() {
801 let ty = TypeRef::Named("Config".into());
802 assert!(!field_references_excluded_type(&ty, &["JsValue".to_string()]));
803 }
804
805 #[test]
806 fn test_field_references_excluded_type_inside_optional() {
807 let ty = TypeRef::Optional(Box::new(TypeRef::Named("Excluded".into())));
808 assert!(field_references_excluded_type(&ty, &["Excluded".to_string()]));
809 }
810
811 #[test]
812 fn test_field_references_excluded_type_inside_vec() {
813 let ty = TypeRef::Vec(Box::new(TypeRef::Named("Excluded".into())));
814 assert!(field_references_excluded_type(&ty, &["Excluded".to_string()]));
815 }
816
817 #[test]
818 fn test_field_references_excluded_type_inside_map_key() {
819 let ty = TypeRef::Map(Box::new(TypeRef::Named("Excluded".into())), Box::new(TypeRef::String));
820 assert!(field_references_excluded_type(&ty, &["Excluded".to_string()]));
821 }
822
823 #[test]
824 fn test_field_references_excluded_type_inside_map_value() {
825 let ty = TypeRef::Map(Box::new(TypeRef::String), Box::new(TypeRef::Named("Excluded".into())));
826 assert!(field_references_excluded_type(&ty, &["Excluded".to_string()]));
827 }
828
829 #[test]
830 fn test_field_references_excluded_type_primitive_not_excluded() {
831 let ty = TypeRef::Primitive(PrimitiveType::I32);
832 assert!(!field_references_excluded_type(&ty, &["JsValue".to_string()]));
833 }
834
835 #[test]
840 fn test_can_generate_enum_conversion_with_variants() {
841 let e = make_enum("Color", "crate::Color", &["Red", "Green"]);
842 assert!(can_generate_enum_conversion(&e));
843 }
844
845 #[test]
846 fn test_can_generate_enum_conversion_empty_variants() {
847 let e = make_enum("Empty", "crate::Empty", &[]);
848 assert!(!can_generate_enum_conversion(&e));
849 }
850
851 #[test]
852 fn test_can_generate_enum_conversion_from_core_with_variants() {
853 let e = make_enum("Color", "crate::Color", &["Red"]);
854 assert!(can_generate_enum_conversion_from_core(&e));
855 }
856
857 #[test]
862 fn test_core_type_path_with_full_path() {
863 let typ = make_type("Config", "my_crate::types::Config", vec![]);
864 assert_eq!(core_type_path(&typ, "my_crate"), "my_crate::types::Config");
865 }
866
867 #[test]
868 fn test_core_type_path_with_bare_name() {
869 let typ = make_type("Config", "Config", vec![]);
871 assert_eq!(core_type_path(&typ, "my_crate"), "my_crate::Config");
872 }
873
874 #[test]
875 fn test_core_type_path_normalizes_hyphens() {
876 let typ = make_type("Config", "my-crate::Config", vec![]);
877 assert_eq!(core_type_path(&typ, "my_crate"), "my_crate::Config");
878 }
879
880 #[test]
881 fn test_core_enum_path_with_full_path() {
882 let e = make_enum("Backend", "my_crate::Backend", &[]);
883 assert_eq!(core_enum_path(&e, "my_crate"), "my_crate::Backend");
884 }
885
886 #[test]
887 fn test_core_enum_path_bare_name_gets_prefixed() {
888 let e = make_enum("Backend", "Backend", &[]);
889 assert_eq!(core_enum_path(&e, "my_crate"), "my_crate::Backend");
890 }
891
892 #[test]
897 fn test_build_type_path_map_includes_types_and_enums() {
898 let surface = ApiSurface {
899 crate_name: "my_crate".into(),
900 version: "1.0.0".into(),
901 types: vec![make_type("Config", "my_crate::Config", vec![])],
902 functions: vec![],
903 enums: vec![make_enum("Mode", "my_crate::Mode", &["A"])],
904 errors: vec![],
905 };
906 let map = build_type_path_map(&surface, "my_crate");
907 assert_eq!(map.get("Config").map(String::as_str), Some("my_crate::Config"));
908 assert_eq!(map.get("Mode").map(String::as_str), Some("my_crate::Mode"));
909 }
910
911 #[test]
912 fn test_resolve_named_path_found_in_map() {
913 let mut map = ahash::AHashMap::new();
914 map.insert("Config".to_string(), "my_crate::types::Config".to_string());
915 assert_eq!(
916 resolve_named_path("Config", "my_crate", &map),
917 "my_crate::types::Config"
918 );
919 }
920
921 #[test]
922 fn test_resolve_named_path_not_found_falls_back() {
923 let map = ahash::AHashMap::new();
924 assert_eq!(resolve_named_path("Unknown", "my_crate", &map), "my_crate::Unknown");
925 }
926
927 #[test]
932 fn test_input_type_names_from_function_params() {
933 let surface = ApiSurface {
934 crate_name: "my_crate".into(),
935 version: "1.0.0".into(),
936 types: vec![],
937 functions: vec![FunctionDef {
938 name: "process".into(),
939 rust_path: "my_crate::process".into(),
940 original_rust_path: String::new(),
941 params: vec![ParamDef {
942 name: "config".into(),
943 ty: TypeRef::Named("Config".into()),
944 optional: false,
945 default: None,
946 sanitized: false,
947 typed_default: None,
948 is_ref: false,
949 is_mut: false,
950 newtype_wrapper: None,
951 original_type: None,
952 }],
953 return_type: TypeRef::Unit,
954 is_async: false,
955 error_type: None,
956 doc: String::new(),
957 cfg: None,
958 sanitized: false,
959 returns_ref: false,
960 returns_cow: false,
961 return_newtype_wrapper: None,
962 }],
963 enums: vec![],
964 errors: vec![],
965 };
966 let names = input_type_names(&surface);
967 assert!(names.contains("Config"));
968 }
969
970 #[test]
971 fn test_input_type_names_from_return_types() {
972 let surface = ApiSurface {
973 crate_name: "my_crate".into(),
974 version: "1.0.0".into(),
975 types: vec![],
976 functions: vec![FunctionDef {
977 name: "get_result".into(),
978 rust_path: "my_crate::get_result".into(),
979 original_rust_path: String::new(),
980 params: vec![],
981 return_type: TypeRef::Named("Result".into()),
982 is_async: false,
983 error_type: None,
984 doc: String::new(),
985 cfg: None,
986 sanitized: false,
987 returns_ref: false,
988 returns_cow: false,
989 return_newtype_wrapper: None,
990 }],
991 enums: vec![],
992 errors: vec![],
993 };
994 let names = input_type_names(&surface);
995 assert!(names.contains("Result"));
996 }
997
998 #[test]
1003 fn test_binding_to_core_match_arm_unit_variant() {
1004 let result = binding_to_core_match_arm("MyEnum", "Foo", &[]);
1005 assert_eq!(result, "MyEnum::Foo => Self::Foo,");
1006 }
1007
1008 #[test]
1009 fn test_binding_to_core_match_arm_data_variant_no_binding_data() {
1010 let fields = vec![make_field("value", TypeRef::String)];
1012 let result = helpers::binding_to_core_match_arm_ext("MyEnum", "Foo", &fields, false);
1013 assert!(result.contains("value: Default::default()"));
1014 }
1015
1016 #[test]
1017 fn test_binding_to_core_match_arm_tuple_variant_no_binding_data() {
1018 let fields = vec![make_field("_0", TypeRef::String)];
1019 let result = helpers::binding_to_core_match_arm_ext("MyEnum", "Foo", &fields, false);
1020 assert!(result.contains("Default::default()"));
1021 assert!(result.contains("Self::Foo("));
1022 }
1023
1024 #[test]
1025 fn test_binding_to_core_match_arm_data_variant_with_binding_data_named() {
1026 let fields = vec![make_field("value", TypeRef::Named("Inner".into()))];
1028 let result = helpers::binding_to_core_match_arm_ext("MyEnum", "Foo", &fields, true);
1029 assert!(result.contains("value: value.into()"));
1030 }
1031
1032 #[test]
1033 fn test_binding_to_core_match_arm_data_variant_with_binding_data_tuple() {
1034 let fields = vec![make_field("_0", TypeRef::Named("Inner".into()))];
1035 let result = helpers::binding_to_core_match_arm_ext("MyEnum", "Foo", &fields, true);
1036 assert!(result.contains("_0.into()"));
1037 }
1038
1039 #[test]
1040 fn test_core_to_binding_match_arm_unit_variant() {
1041 let result = core_to_binding_match_arm("CoreEnum", "Bar", &[]);
1042 assert_eq!(result, "CoreEnum::Bar => Self::Bar,");
1043 }
1044
1045 #[test]
1046 fn test_core_to_binding_match_arm_data_named_no_binding_data() {
1047 let fields = vec![make_field("x", TypeRef::Primitive(PrimitiveType::I32))];
1048 let result = helpers::core_to_binding_match_arm_ext("CoreEnum", "Bar", &fields, false);
1049 assert!(result.contains("{ .. }"));
1050 assert!(result.contains("Self::Bar"));
1051 }
1052
1053 #[test]
1054 fn test_core_to_binding_match_arm_data_tuple_no_binding_data() {
1055 let fields = vec![make_field("_0", TypeRef::Primitive(PrimitiveType::I32))];
1056 let result = helpers::core_to_binding_match_arm_ext("CoreEnum", "Bar", &fields, false);
1057 assert!(result.contains("(..)"));
1058 assert!(result.contains("Self::Bar"));
1059 }
1060
1061 #[test]
1066 fn test_has_sanitized_fields_false() {
1067 let typ = make_type("Foo", "crate::Foo", vec![make_field("x", TypeRef::String)]);
1068 assert!(!has_sanitized_fields(&typ));
1069 }
1070
1071 #[test]
1072 fn test_has_sanitized_fields_true() {
1073 let mut field = make_field("x", TypeRef::String);
1074 field.sanitized = true;
1075 let typ = make_type("Foo", "crate::Foo", vec![field]);
1076 assert!(has_sanitized_fields(&typ));
1077 }
1078
1079 #[test]
1084 fn test_gen_from_binding_to_core_string_field() {
1085 let typ = make_type("S", "c::S", vec![make_field("title", TypeRef::String)]);
1086 let result = gen_from_binding_to_core(&typ, "c");
1087 assert!(result.contains("impl From<S> for c::S"));
1088 assert!(result.contains("title: val.title"));
1089 }
1090
1091 #[test]
1092 fn test_gen_from_binding_to_core_duration_field() {
1093 let typ = make_type("S", "c::S", vec![make_field("timeout", TypeRef::Duration)]);
1094 let result = gen_from_binding_to_core(&typ, "c");
1095 assert!(result.contains("std::time::Duration::from_millis(val.timeout)"));
1096 }
1097
1098 #[test]
1099 fn test_gen_from_binding_to_core_optional_named_field() {
1100 let field = make_opt_field("backend", TypeRef::Named("Backend".into()));
1101 let typ = make_type("S", "c::S", vec![field]);
1102 let result = gen_from_binding_to_core(&typ, "c");
1103 assert!(result.contains("backend: val.backend.map(Into::into)"));
1104 }
1105
1106 #[test]
1107 fn test_gen_from_binding_to_core_vec_named_field() {
1108 let field = make_field("items", TypeRef::Vec(Box::new(TypeRef::Named("Item".into()))));
1109 let typ = make_type("S", "c::S", vec![field]);
1110 let result = gen_from_binding_to_core(&typ, "c");
1111 assert!(result.contains("into_iter().map(Into::into).collect()"));
1112 }
1113
1114 #[test]
1115 fn test_gen_from_binding_to_core_sanitized_field_uses_default() {
1116 let mut field = make_field("complex", TypeRef::String);
1117 field.sanitized = true;
1118 let typ = make_type("S", "c::S", vec![field]);
1119 let result = gen_from_binding_to_core(&typ, "c");
1120 assert!(result.contains("complex: Default::default()"));
1121 }
1122
1123 #[test]
1124 fn test_gen_from_binding_to_core_with_stripped_cfg_fields_uses_default_update() {
1125 let mut typ = make_type("S", "c::S", vec![make_field("x", TypeRef::String)]);
1126 typ.has_stripped_cfg_fields = true;
1127 let result = gen_from_binding_to_core(&typ, "c");
1128 assert!(result.contains("..Default::default()"));
1129 assert!(result.contains("#[allow(clippy::needless_update)]"));
1130 }
1131
1132 #[test]
1133 fn test_gen_from_binding_to_core_with_type_name_prefix() {
1134 let typ = make_type("Config", "c::Config", vec![make_field("x", TypeRef::String)]);
1135 let config = ConversionConfig {
1136 type_name_prefix: "Js",
1137 ..Default::default()
1138 };
1139 let result = gen_from_binding_to_core_cfg(&typ, "c", &config);
1140 assert!(result.contains("impl From<JsConfig> for c::Config"));
1141 }
1142
1143 #[test]
1144 fn test_gen_from_binding_to_core_path_field() {
1145 let field = make_field("file", TypeRef::Path);
1146 let typ = make_type("S", "c::S", vec![field]);
1147 let result = gen_from_binding_to_core(&typ, "c");
1148 assert!(result.contains("file: val.file.into()"));
1149 }
1150
1151 #[test]
1152 fn test_gen_from_binding_to_core_json_field() {
1153 let field = make_field("meta", TypeRef::Json);
1154 let typ = make_type("S", "c::S", vec![field]);
1155 let result = gen_from_binding_to_core(&typ, "c");
1156 assert!(result.contains("serde_json::from_str(&val.meta).unwrap_or_default()"));
1157 }
1158
1159 #[test]
1160 fn test_gen_from_binding_to_core_newtype_struct() {
1161 let field = make_field("_0", TypeRef::Primitive(PrimitiveType::U32));
1163 let typ = make_type("NodeIndex", "c::NodeIndex", vec![field]);
1164 let result = gen_from_binding_to_core(&typ, "c");
1165 assert!(result.contains("Self(val._0)"));
1166 }
1167
1168 #[test]
1169 fn test_gen_from_binding_to_core_newtype_named_field() {
1170 let field = make_field("_0", TypeRef::Named("Inner".into()));
1171 let typ = make_type("Wrapper", "c::Wrapper", vec![field]);
1172 let result = gen_from_binding_to_core(&typ, "c");
1173 assert!(result.contains("Self(val._0.into())"));
1174 }
1175
1176 #[test]
1177 fn test_gen_from_binding_to_core_newtype_path_field() {
1178 let field = make_field("_0", TypeRef::Path);
1179 let typ = make_type("PathWrapper", "c::PathWrapper", vec![field]);
1180 let result = gen_from_binding_to_core(&typ, "c");
1181 assert!(result.contains("Self(val._0.into())"));
1182 }
1183
1184 #[test]
1185 fn test_gen_from_binding_to_core_newtype_duration_field() {
1186 let field = make_field("_0", TypeRef::Duration);
1187 let typ = make_type("DurWrapper", "c::DurWrapper", vec![field]);
1188 let result = gen_from_binding_to_core(&typ, "c");
1189 assert!(result.contains("Self(std::time::Duration::from_millis(val._0))"));
1190 }
1191
1192 #[test]
1193 fn test_gen_from_binding_to_core_boxed_named_field() {
1194 let mut field = make_field("child", TypeRef::Named("Child".into()));
1195 field.is_boxed = true;
1196 let typ = make_type("S", "c::S", vec![field]);
1197 let result = gen_from_binding_to_core(&typ, "c");
1198 assert!(result.contains("Box::new("));
1199 }
1200
1201 #[test]
1202 fn test_gen_from_binding_to_core_cast_large_ints_to_i64() {
1203 let field = make_field("count", TypeRef::Primitive(PrimitiveType::U64));
1204 let typ = make_type("S", "c::S", vec![field]);
1205 let config = ConversionConfig {
1206 cast_large_ints_to_i64: true,
1207 ..Default::default()
1208 };
1209 let result = gen_from_binding_to_core_cfg(&typ, "c", &config);
1210 assert!(result.contains("val.count as u64"));
1211 }
1212
1213 #[test]
1214 fn test_gen_from_binding_to_core_exclude_types_skips_field() {
1215 let field = make_field("js_val", TypeRef::Named("JsValue".into()));
1216 let typ = make_type("S", "c::S", vec![field]);
1217 let excluded = vec!["JsValue".to_string()];
1218 let config = ConversionConfig {
1219 exclude_types: &excluded,
1220 ..Default::default()
1221 };
1222 let result = gen_from_binding_to_core_cfg(&typ, "c", &config);
1223 assert!(result.contains("js_val: Default::default()"));
1224 }
1225
1226 #[test]
1231 fn test_gen_from_core_to_binding_string_field() {
1232 let typ = make_type("S", "c::S", vec![make_field("title", TypeRef::String)]);
1233 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
1234 assert!(result.contains("impl From<c::S> for S"));
1235 assert!(result.contains("title: val.title"));
1236 }
1237
1238 #[test]
1239 fn test_gen_from_core_to_binding_duration_field() {
1240 let field = make_field("timeout", TypeRef::Duration);
1241 let typ = make_type("S", "c::S", vec![field]);
1242 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
1243 assert!(result.contains("val.timeout.as_millis() as u64"));
1244 }
1245
1246 #[test]
1247 fn test_gen_from_core_to_binding_path_field() {
1248 let field = make_field("path", TypeRef::Path);
1249 let typ = make_type("S", "c::S", vec![field]);
1250 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
1251 assert!(result.contains("to_string_lossy().to_string()"));
1252 }
1253
1254 #[test]
1255 fn test_gen_from_core_to_binding_json_field() {
1256 let field = make_field("meta", TypeRef::Json);
1257 let typ = make_type("S", "c::S", vec![field]);
1258 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
1259 assert!(result.contains("val.meta.to_string()"));
1260 }
1261
1262 #[test]
1263 fn test_gen_from_core_to_binding_opaque_field() {
1264 let mut opaques = AHashSet::new();
1265 opaques.insert("Client".to_string());
1266 let field = make_field("client", TypeRef::Named("Client".into()));
1267 let typ = make_type("S", "c::S", vec![field]);
1268 let result = gen_from_core_to_binding(&typ, "c", &opaques);
1269 assert!(result.contains("Arc::new(val.client)"));
1270 assert!(result.contains("Client { inner:"));
1271 }
1272
1273 #[test]
1274 fn test_gen_from_core_to_binding_with_type_name_prefix_opaque() {
1275 let mut opaques = AHashSet::new();
1276 opaques.insert("Client".to_string());
1277 let field = make_field("client", TypeRef::Named("Client".into()));
1278 let typ = make_type("S", "c::S", vec![field]);
1279 let config = ConversionConfig {
1280 type_name_prefix: "Js",
1281 ..Default::default()
1282 };
1283 let result = gen_from_core_to_binding_cfg(&typ, "c", &opaques, &config);
1284 assert!(result.contains("JsClient { inner: Arc::new(val.client) }"));
1285 }
1286
1287 #[test]
1288 fn test_gen_from_core_to_binding_sanitized_field() {
1289 let mut field = make_field("complex", TypeRef::String);
1290 field.sanitized = true;
1291 let typ = make_type("S", "c::S", vec![field]);
1292 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
1293 assert!(result.contains("format!("));
1294 }
1295
1296 #[test]
1297 fn test_gen_from_core_to_binding_newtype_struct() {
1298 let field = make_field("_0", TypeRef::Primitive(PrimitiveType::U32));
1299 let typ = make_type("NodeIndex", "c::NodeIndex", vec![field]);
1300 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
1301 assert!(result.contains("Self { _0: val.0 }"));
1302 }
1303
1304 #[test]
1305 fn test_gen_from_core_to_binding_newtype_path_field() {
1306 let field = make_field("_0", TypeRef::Path);
1307 let typ = make_type("PathWrapper", "c::PathWrapper", vec![field]);
1308 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
1309 assert!(result.contains("val.0.to_string_lossy().to_string()"));
1310 }
1311
1312 #[test]
1313 fn test_gen_from_core_to_binding_newtype_duration_field() {
1314 let field = make_field("_0", TypeRef::Duration);
1315 let typ = make_type("DurWrapper", "c::DurWrapper", vec![field]);
1316 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
1317 assert!(result.contains("val.0.as_millis() as u64"));
1318 }
1319
1320 #[test]
1321 fn test_gen_from_core_to_binding_newtype_named_field() {
1322 let field = make_field("_0", TypeRef::Named("Inner".into()));
1323 let typ = make_type("Wrapper", "c::Wrapper", vec![field]);
1324 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
1325 assert!(result.contains("val.0.into()"));
1326 }
1327
1328 #[test]
1329 fn test_gen_from_core_to_binding_cast_large_ints_to_i64() {
1330 let field = make_field("count", TypeRef::Primitive(PrimitiveType::U64));
1331 let typ = make_type("S", "c::S", vec![field]);
1332 let config = ConversionConfig {
1333 cast_large_ints_to_i64: true,
1334 ..Default::default()
1335 };
1336 let result = gen_from_core_to_binding_cfg(&typ, "c", &no_opaques(), &config);
1337 assert!(result.contains("val.count as i64"));
1338 }
1339
1340 #[test]
1341 fn test_gen_from_core_to_binding_cast_f32_to_f64() {
1342 let field = make_field("score", TypeRef::Primitive(PrimitiveType::F32));
1343 let typ = make_type("S", "c::S", vec![field]);
1344 let config = ConversionConfig {
1345 cast_f32_to_f64: true,
1346 ..Default::default()
1347 };
1348 let result = gen_from_core_to_binding_cfg(&typ, "c", &no_opaques(), &config);
1349 assert!(result.contains("val.score as f64"));
1350 }
1351
1352 #[test]
1353 fn test_gen_from_core_to_binding_exclude_types_skips_field() {
1354 let field = make_field("js_val", TypeRef::Named("JsValue".into()));
1355 let typ = make_type("S", "c::S", vec![field]);
1356 let excluded = vec!["JsValue".to_string()];
1357 let config = ConversionConfig {
1358 exclude_types: &excluded,
1359 ..Default::default()
1360 };
1361 let result = gen_from_core_to_binding_cfg(&typ, "c", &no_opaques(), &config);
1362 assert!(!result.contains("js_val:"));
1364 }
1365
1366 #[test]
1367 fn test_gen_from_core_to_binding_vec_json_field() {
1368 let field = make_field("items", TypeRef::Vec(Box::new(TypeRef::Json)));
1369 let typ = make_type("S", "c::S", vec![field]);
1370 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
1371 assert!(result.contains("map(ToString::to_string)"));
1372 }
1373
1374 #[test]
1375 fn test_gen_from_core_to_binding_char_field() {
1376 let field = make_field("sep", TypeRef::Char);
1377 let typ = make_type("S", "c::S", vec![field]);
1378 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
1379 assert!(result.contains("val.sep.to_string()"));
1380 }
1381
1382 #[test]
1383 fn test_gen_from_core_to_binding_bytes_field() {
1384 let field = make_field("data", TypeRef::Bytes);
1385 let typ = make_type("S", "c::S", vec![field]);
1386 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
1387 assert!(result.contains("val.data.to_vec()"));
1388 }
1389
1390 #[test]
1395 fn test_field_conversion_to_core_cfg_no_flags_delegates_to_base() {
1396 let config = ConversionConfig::default();
1397 let result = field_conversion_to_core_cfg("x", &TypeRef::String, false, &config);
1398 assert_eq!(result, "x: val.x");
1399 }
1400
1401 #[test]
1402 fn test_field_conversion_to_core_cfg_cast_u64_to_i64() {
1403 let config = ConversionConfig {
1404 cast_large_ints_to_i64: true,
1405 ..Default::default()
1406 };
1407 let result = field_conversion_to_core_cfg("n", &TypeRef::Primitive(PrimitiveType::U64), false, &config);
1408 assert_eq!(result, "n: val.n as u64");
1409 }
1410
1411 #[test]
1412 fn test_field_conversion_to_core_cfg_cast_usize_to_i64() {
1413 let config = ConversionConfig {
1414 cast_large_ints_to_i64: true,
1415 ..Default::default()
1416 };
1417 let result = field_conversion_to_core_cfg("n", &TypeRef::Primitive(PrimitiveType::Usize), false, &config);
1418 assert_eq!(result, "n: val.n as usize");
1419 }
1420
1421 #[test]
1422 fn test_field_conversion_to_core_cfg_cast_isize_to_i64() {
1423 let config = ConversionConfig {
1424 cast_large_ints_to_i64: true,
1425 ..Default::default()
1426 };
1427 let result = field_conversion_to_core_cfg("n", &TypeRef::Primitive(PrimitiveType::Isize), false, &config);
1428 assert_eq!(result, "n: val.n as isize");
1429 }
1430
1431 #[test]
1432 fn test_field_conversion_to_core_cfg_f32_cast() {
1433 let config = ConversionConfig {
1434 cast_f32_to_f64: true,
1435 ..Default::default()
1436 };
1437 let result = field_conversion_to_core_cfg("s", &TypeRef::Primitive(PrimitiveType::F32), false, &config);
1438 assert_eq!(result, "s: val.s as f32");
1439 }
1440
1441 #[test]
1442 fn test_field_conversion_to_core_cfg_duration_cast() {
1443 let config = ConversionConfig {
1444 cast_large_ints_to_i64: true,
1445 ..Default::default()
1446 };
1447 let result = field_conversion_to_core_cfg("t", &TypeRef::Duration, false, &config);
1448 assert_eq!(result, "t: std::time::Duration::from_millis(val.t as u64)");
1449 }
1450
1451 #[test]
1452 fn test_field_conversion_to_core_cfg_json_to_string() {
1453 let config = ConversionConfig {
1454 json_to_string: true,
1455 ..Default::default()
1456 };
1457 let result = field_conversion_to_core_cfg("m", &TypeRef::Json, false, &config);
1458 assert_eq!(result, "m: Default::default()");
1459 }
1460
1461 #[test]
1462 fn test_field_conversion_to_core_cfg_vec_named_to_string() {
1463 let config = ConversionConfig {
1464 vec_named_to_string: true,
1465 ..Default::default()
1466 };
1467 let ty = TypeRef::Vec(Box::new(TypeRef::Named("Item".into())));
1468 let result = field_conversion_to_core_cfg("items", &ty, false, &config);
1469 assert_eq!(result, "items: serde_json::from_str(&val.items).unwrap_or_default()");
1470 }
1471
1472 #[test]
1477 fn test_field_conversion_from_core_cfg_no_flags_delegates_to_base() {
1478 let config = ConversionConfig::default();
1479 let result = field_conversion_from_core_cfg("x", &TypeRef::String, false, false, &no_opaques(), &config);
1480 assert_eq!(result, "x: val.x");
1481 }
1482
1483 #[test]
1484 fn test_field_conversion_from_core_cfg_cast_u64_to_i64() {
1485 let config = ConversionConfig {
1486 cast_large_ints_to_i64: true,
1487 ..Default::default()
1488 };
1489 let result = field_conversion_from_core_cfg(
1490 "n",
1491 &TypeRef::Primitive(PrimitiveType::U64),
1492 false,
1493 false,
1494 &no_opaques(),
1495 &config,
1496 );
1497 assert_eq!(result, "n: val.n as i64");
1498 }
1499
1500 #[test]
1501 fn test_field_conversion_from_core_cfg_cast_f32_to_f64() {
1502 let config = ConversionConfig {
1503 cast_f32_to_f64: true,
1504 ..Default::default()
1505 };
1506 let result = field_conversion_from_core_cfg(
1507 "s",
1508 &TypeRef::Primitive(PrimitiveType::F32),
1509 false,
1510 false,
1511 &no_opaques(),
1512 &config,
1513 );
1514 assert_eq!(result, "s: val.s as f64");
1515 }
1516
1517 #[test]
1518 fn test_field_conversion_from_core_cfg_duration_cast_to_i64() {
1519 let config = ConversionConfig {
1520 cast_large_ints_to_i64: true,
1521 ..Default::default()
1522 };
1523 let result = field_conversion_from_core_cfg("t", &TypeRef::Duration, false, false, &no_opaques(), &config);
1524 assert_eq!(result, "t: val.t.as_millis() as u64 as i64");
1525 }
1526
1527 #[test]
1528 fn test_field_conversion_from_core_cfg_json_to_string() {
1529 let config = ConversionConfig {
1530 json_to_string: true,
1531 ..Default::default()
1532 };
1533 let result = field_conversion_from_core_cfg("m", &TypeRef::Json, false, false, &no_opaques(), &config);
1534 assert_eq!(result, "m: val.m.to_string()");
1535 }
1536
1537 #[test]
1538 fn test_field_conversion_from_core_cfg_vec_named_to_string() {
1539 let config = ConversionConfig {
1540 vec_named_to_string: true,
1541 ..Default::default()
1542 };
1543 let ty = TypeRef::Vec(Box::new(TypeRef::Named("Item".into())));
1544 let result = field_conversion_from_core_cfg("items", &ty, false, false, &no_opaques(), &config);
1545 assert_eq!(result, "items: serde_json::to_string(&val.items).unwrap_or_default()");
1546 }
1547
1548 #[test]
1549 fn test_field_conversion_from_core_cfg_vec_u64_cast() {
1550 let config = ConversionConfig {
1551 cast_large_ints_to_i64: true,
1552 ..Default::default()
1553 };
1554 let ty = TypeRef::Vec(Box::new(TypeRef::Primitive(PrimitiveType::U64)));
1555 let result = field_conversion_from_core_cfg("ids", &ty, false, false, &no_opaques(), &config);
1556 assert!(result.contains("as i64"));
1557 }
1558
1559 #[test]
1560 fn test_field_conversion_from_core_cfg_vec_f32_cast() {
1561 let config = ConversionConfig {
1562 cast_f32_to_f64: true,
1563 ..Default::default()
1564 };
1565 let ty = TypeRef::Vec(Box::new(TypeRef::Primitive(PrimitiveType::F32)));
1566 let result = field_conversion_from_core_cfg("scores", &ty, false, false, &no_opaques(), &config);
1567 assert!(result.contains("as f64"));
1568 }
1569
1570 #[test]
1571 fn test_field_conversion_from_core_cfg_map_u64_values_cast() {
1572 let config = ConversionConfig {
1573 cast_large_ints_to_i64: true,
1574 ..Default::default()
1575 };
1576 let ty = TypeRef::Map(
1577 Box::new(TypeRef::String),
1578 Box::new(TypeRef::Primitive(PrimitiveType::U64)),
1579 );
1580 let result = field_conversion_from_core_cfg("map", &ty, false, false, &no_opaques(), &config);
1581 assert!(result.contains("as i64"));
1582 }
1583
1584 #[test]
1589 fn test_convertible_types_simple_struct() {
1590 let surface = ApiSurface {
1591 crate_name: "c".into(),
1592 version: "1.0".into(),
1593 types: vec![make_type("Config", "c::Config", vec![make_field("x", TypeRef::String)])],
1594 functions: vec![],
1595 enums: vec![],
1596 errors: vec![],
1597 };
1598 let result = convertible_types(&surface);
1599 assert!(result.contains("Config"));
1600 }
1601
1602 #[test]
1603 fn test_convertible_types_excludes_type_with_unconvertible_named_field() {
1604 let field = make_field("inner", TypeRef::Named("Unknown".into()));
1606 let surface = ApiSurface {
1607 crate_name: "c".into(),
1608 version: "1.0".into(),
1609 types: vec![make_type("Wrapper", "c::Wrapper", vec![field])],
1610 functions: vec![],
1611 enums: vec![],
1612 errors: vec![],
1613 };
1614 let result = convertible_types(&surface);
1615 assert!(!result.contains("Wrapper"));
1616 }
1617
1618 #[test]
1619 fn test_core_to_binding_convertible_types_simple() {
1620 let surface = ApiSurface {
1621 crate_name: "c".into(),
1622 version: "1.0".into(),
1623 types: vec![make_type("Config", "c::Config", vec![make_field("x", TypeRef::String)])],
1624 functions: vec![],
1625 enums: vec![],
1626 errors: vec![],
1627 };
1628 let result = core_to_binding_convertible_types(&surface);
1629 assert!(result.contains("Config"));
1630 }
1631
1632 #[test]
1633 fn test_can_generate_conversion_true_when_in_set() {
1634 let mut set = AHashSet::new();
1635 set.insert("Config".to_string());
1636 let typ = make_type("Config", "c::Config", vec![]);
1637 assert!(can_generate_conversion(&typ, &set));
1638 }
1639
1640 #[test]
1641 fn test_can_generate_conversion_false_when_absent() {
1642 let set = AHashSet::new();
1643 let typ = make_type("Config", "c::Config", vec![]);
1644 assert!(!can_generate_conversion(&typ, &set));
1645 }
1646
1647 #[test]
1652 fn test_field_conversion_to_core_cfg_cast_u64_optional() {
1653 let config = ConversionConfig {
1654 cast_large_ints_to_i64: true,
1655 ..Default::default()
1656 };
1657 let result = field_conversion_to_core_cfg("n", &TypeRef::Primitive(PrimitiveType::U64), true, &config);
1658 assert_eq!(result, "n: val.n.map(|v| v as u64)");
1659 }
1660
1661 #[test]
1662 fn test_field_conversion_to_core_cfg_cast_usize_optional() {
1663 let config = ConversionConfig {
1664 cast_large_ints_to_i64: true,
1665 ..Default::default()
1666 };
1667 let result = field_conversion_to_core_cfg("n", &TypeRef::Primitive(PrimitiveType::Usize), true, &config);
1668 assert_eq!(result, "n: val.n.map(|v| v as usize)");
1669 }
1670
1671 #[test]
1672 fn test_field_conversion_to_core_cfg_cast_isize_optional() {
1673 let config = ConversionConfig {
1674 cast_large_ints_to_i64: true,
1675 ..Default::default()
1676 };
1677 let result = field_conversion_to_core_cfg("n", &TypeRef::Primitive(PrimitiveType::Isize), true, &config);
1678 assert_eq!(result, "n: val.n.map(|v| v as isize)");
1679 }
1680
1681 #[test]
1682 fn test_field_conversion_to_core_cfg_cast_f32_optional() {
1683 let config = ConversionConfig {
1684 cast_f32_to_f64: true,
1685 ..Default::default()
1686 };
1687 let result = field_conversion_to_core_cfg("s", &TypeRef::Primitive(PrimitiveType::F32), true, &config);
1688 assert_eq!(result, "s: val.s.map(|v| v as f32)");
1689 }
1690
1691 #[test]
1692 fn test_field_conversion_to_core_cfg_duration_cast_optional() {
1693 let config = ConversionConfig {
1694 cast_large_ints_to_i64: true,
1695 ..Default::default()
1696 };
1697 let result = field_conversion_to_core_cfg("t", &TypeRef::Duration, true, &config);
1698 assert_eq!(result, "t: val.t.map(|v| std::time::Duration::from_millis(v as u64))");
1699 }
1700
1701 #[test]
1702 fn test_field_conversion_to_core_cfg_json_to_string_optional() {
1703 let config = ConversionConfig {
1704 json_to_string: true,
1705 ..Default::default()
1706 };
1707 let result = field_conversion_to_core_cfg("m", &TypeRef::Json, true, &config);
1708 assert_eq!(result, "m: Default::default()");
1710 }
1711
1712 #[test]
1713 fn test_field_conversion_to_core_cfg_vec_named_to_string_optional() {
1714 let config = ConversionConfig {
1715 vec_named_to_string: true,
1716 ..Default::default()
1717 };
1718 let ty = TypeRef::Vec(Box::new(TypeRef::Named("Item".into())));
1719 let result = field_conversion_to_core_cfg("items", &ty, true, &config);
1720 assert_eq!(
1721 result,
1722 "items: val.items.as_ref().and_then(|s| serde_json::from_str(s).ok())"
1723 );
1724 }
1725
1726 #[test]
1727 fn test_field_conversion_to_core_cfg_vec_u64_cast_optional() {
1728 let config = ConversionConfig {
1729 cast_large_ints_to_i64: true,
1730 ..Default::default()
1731 };
1732 let ty = TypeRef::Vec(Box::new(TypeRef::Primitive(PrimitiveType::U64)));
1733 let result = field_conversion_to_core_cfg("ids", &ty, true, &config);
1734 assert!(result.contains("as u64"));
1735 }
1736
1737 #[test]
1738 fn test_field_conversion_to_core_cfg_vec_f32_cast_optional() {
1739 let config = ConversionConfig {
1740 cast_f32_to_f64: true,
1741 ..Default::default()
1742 };
1743 let ty = TypeRef::Vec(Box::new(TypeRef::Primitive(PrimitiveType::F32)));
1744 let result = field_conversion_to_core_cfg("scores", &ty, true, &config);
1745 assert!(result.contains("as f32"));
1746 }
1747
1748 #[test]
1749 fn test_field_conversion_to_core_cfg_map_u64_values_cast_optional() {
1750 let config = ConversionConfig {
1751 cast_large_ints_to_i64: true,
1752 ..Default::default()
1753 };
1754 let ty = TypeRef::Map(
1755 Box::new(TypeRef::String),
1756 Box::new(TypeRef::Primitive(PrimitiveType::U64)),
1757 );
1758 let result = field_conversion_to_core_cfg("map", &ty, true, &config);
1759 assert!(result.contains("as u64"));
1760 }
1761
1762 #[test]
1763 fn test_field_conversion_to_core_cfg_optional_inner_u64_cast() {
1764 let config = ConversionConfig {
1766 cast_large_ints_to_i64: true,
1767 ..Default::default()
1768 };
1769 let ty = TypeRef::Optional(Box::new(TypeRef::Primitive(PrimitiveType::U64)));
1770 let result = field_conversion_to_core_cfg("n", &ty, false, &config);
1771 assert!(result.contains("as u64"));
1772 }
1773
1774 #[test]
1779 fn test_field_conversion_from_core_cfg_cast_u64_optional() {
1780 let config = ConversionConfig {
1781 cast_large_ints_to_i64: true,
1782 ..Default::default()
1783 };
1784 let result = field_conversion_from_core_cfg(
1785 "n",
1786 &TypeRef::Primitive(PrimitiveType::U64),
1787 true,
1788 false,
1789 &no_opaques(),
1790 &config,
1791 );
1792 assert_eq!(result, "n: val.n.map(|v| v as i64)");
1793 }
1794
1795 #[test]
1796 fn test_field_conversion_from_core_cfg_cast_usize_optional() {
1797 let config = ConversionConfig {
1798 cast_large_ints_to_i64: true,
1799 ..Default::default()
1800 };
1801 let result = field_conversion_from_core_cfg(
1802 "n",
1803 &TypeRef::Primitive(PrimitiveType::Usize),
1804 true,
1805 false,
1806 &no_opaques(),
1807 &config,
1808 );
1809 assert_eq!(result, "n: val.n.map(|v| v as i64)");
1810 }
1811
1812 #[test]
1813 fn test_field_conversion_from_core_cfg_cast_isize_optional() {
1814 let config = ConversionConfig {
1815 cast_large_ints_to_i64: true,
1816 ..Default::default()
1817 };
1818 let result = field_conversion_from_core_cfg(
1819 "n",
1820 &TypeRef::Primitive(PrimitiveType::Isize),
1821 true,
1822 false,
1823 &no_opaques(),
1824 &config,
1825 );
1826 assert_eq!(result, "n: val.n.map(|v| v as i64)");
1827 }
1828
1829 #[test]
1830 fn test_field_conversion_from_core_cfg_cast_f32_optional() {
1831 let config = ConversionConfig {
1832 cast_f32_to_f64: true,
1833 ..Default::default()
1834 };
1835 let result = field_conversion_from_core_cfg(
1836 "s",
1837 &TypeRef::Primitive(PrimitiveType::F32),
1838 true,
1839 false,
1840 &no_opaques(),
1841 &config,
1842 );
1843 assert_eq!(result, "s: val.s.map(|v| v as f64)");
1844 }
1845
1846 #[test]
1847 fn test_field_conversion_from_core_cfg_duration_cast_optional() {
1848 let config = ConversionConfig {
1849 cast_large_ints_to_i64: true,
1850 ..Default::default()
1851 };
1852 let result = field_conversion_from_core_cfg("t", &TypeRef::Duration, true, false, &no_opaques(), &config);
1853 assert_eq!(result, "t: val.t.map(|d| d.as_millis() as u64 as i64)");
1854 }
1855
1856 #[test]
1857 fn test_field_conversion_from_core_cfg_json_to_string_optional() {
1858 let config = ConversionConfig {
1859 json_to_string: true,
1860 ..Default::default()
1861 };
1862 let result = field_conversion_from_core_cfg("m", &TypeRef::Json, true, false, &no_opaques(), &config);
1863 assert_eq!(result, "m: val.m.as_ref().map(ToString::to_string)");
1864 }
1865
1866 #[test]
1867 fn test_field_conversion_from_core_cfg_vec_named_to_string_optional() {
1868 let config = ConversionConfig {
1869 vec_named_to_string: true,
1870 ..Default::default()
1871 };
1872 let ty = TypeRef::Vec(Box::new(TypeRef::Named("Item".into())));
1873 let result = field_conversion_from_core_cfg("items", &ty, true, false, &no_opaques(), &config);
1874 assert_eq!(
1875 result,
1876 "items: val.items.as_ref().and_then(|v| serde_json::to_string(v).ok())"
1877 );
1878 }
1879
1880 #[test]
1881 fn test_field_conversion_from_core_cfg_vec_u64_cast_optional() {
1882 let config = ConversionConfig {
1883 cast_large_ints_to_i64: true,
1884 ..Default::default()
1885 };
1886 let ty = TypeRef::Vec(Box::new(TypeRef::Primitive(PrimitiveType::U64)));
1887 let result = field_conversion_from_core_cfg("ids", &ty, true, false, &no_opaques(), &config);
1888 assert!(result.contains("as i64"));
1889 }
1890
1891 #[test]
1892 fn test_field_conversion_from_core_cfg_vec_f32_cast_optional() {
1893 let config = ConversionConfig {
1894 cast_f32_to_f64: true,
1895 ..Default::default()
1896 };
1897 let ty = TypeRef::Vec(Box::new(TypeRef::Primitive(PrimitiveType::F32)));
1898 let result = field_conversion_from_core_cfg("scores", &ty, true, false, &no_opaques(), &config);
1899 assert!(result.contains("as f64"));
1900 }
1901
1902 #[test]
1903 fn test_field_conversion_from_core_cfg_optional_inner_u64_cast() {
1904 let config = ConversionConfig {
1906 cast_large_ints_to_i64: true,
1907 ..Default::default()
1908 };
1909 let ty = TypeRef::Optional(Box::new(TypeRef::Primitive(PrimitiveType::U64)));
1910 let result = field_conversion_from_core_cfg("n", &ty, false, false, &no_opaques(), &config);
1911 assert!(result.contains("as i64"));
1912 }
1913
1914 #[test]
1915 fn test_field_conversion_from_core_cfg_map_u64_values_cast_optional() {
1916 let config = ConversionConfig {
1917 cast_large_ints_to_i64: true,
1918 ..Default::default()
1919 };
1920 let ty = TypeRef::Map(
1921 Box::new(TypeRef::String),
1922 Box::new(TypeRef::Primitive(PrimitiveType::U64)),
1923 );
1924 let result = field_conversion_from_core_cfg("map", &ty, true, false, &no_opaques(), &config);
1925 assert!(result.contains("as i64"));
1926 }
1927
1928 #[test]
1934 fn test_field_conversion_to_core_optional_vec_string() {
1935 let ty = TypeRef::Optional(Box::new(TypeRef::Vec(Box::new(TypeRef::String))));
1937 let result = field_conversion_to_core("items", &ty, false);
1938 assert_eq!(result, "items: val.items");
1939 }
1940
1941 #[test]
1942 fn test_field_conversion_to_core_optional_vec_named_inner() {
1943 let ty = TypeRef::Optional(Box::new(TypeRef::Vec(Box::new(TypeRef::Named("Item".into())))));
1945 let result = field_conversion_to_core("items", &ty, false);
1946 assert_eq!(
1947 result,
1948 "items: val.items.map(|v| v.into_iter().map(Into::into).collect())"
1949 );
1950 }
1951
1952 #[test]
1953 fn test_field_conversion_to_core_map_string_optional_named() {
1954 let ty = TypeRef::Map(
1956 Box::new(TypeRef::String),
1957 Box::new(TypeRef::Optional(Box::new(TypeRef::Named("Val".into())))),
1958 );
1959 let result = field_conversion_to_core("map", &ty, false);
1962 assert_eq!(result, "map: val.map.into_iter().collect()");
1963 }
1964
1965 #[test]
1966 fn test_field_conversion_to_core_map_string_vec_named_value() {
1967 let ty = TypeRef::Map(
1969 Box::new(TypeRef::String),
1970 Box::new(TypeRef::Vec(Box::new(TypeRef::Named("Item".into())))),
1971 );
1972 let result = field_conversion_to_core("map", &ty, false);
1973 assert!(result.contains("v.into_iter().map(Into::into).collect()"));
1974 }
1975
1976 #[test]
1977 fn test_field_conversion_to_core_map_string_vec_json_value() {
1978 let ty = TypeRef::Map(
1980 Box::new(TypeRef::String),
1981 Box::new(TypeRef::Vec(Box::new(TypeRef::Json))),
1982 );
1983 let result = field_conversion_to_core("map", &ty, false);
1984 assert!(result.contains("filter_map(|s| serde_json::from_str(&s).ok()).collect()"));
1985 }
1986
1987 #[test]
1988 fn test_field_conversion_to_core_map_string_string_optional() {
1989 let ty = TypeRef::Map(Box::new(TypeRef::String), Box::new(TypeRef::String));
1991 let result = field_conversion_to_core("map", &ty, true);
1992 assert_eq!(result, "map: val.map.map(|m| m.into_iter().collect())");
1993 }
1994
1995 #[test]
1996 fn test_field_conversion_from_core_optional_vec_named() {
1997 let ty = TypeRef::Optional(Box::new(TypeRef::Vec(Box::new(TypeRef::Named("Item".into())))));
1999 let result = field_conversion_from_core("items", &ty, false, false, &no_opaques());
2000 assert!(result.contains("map(Into::into)") || result.contains("into_iter().map(Into::into)"));
2002 }
2003
2004 #[test]
2005 fn test_field_conversion_from_core_map_string_named_values() {
2006 let ty = TypeRef::Map(Box::new(TypeRef::String), Box::new(TypeRef::Named("Val".into())));
2008 let result = field_conversion_from_core("map", &ty, false, false, &no_opaques());
2009 assert!(result.contains("v.into()"));
2011 }
2012
2013 #[test]
2014 fn test_field_conversion_to_core_vec_string_passthrough() {
2015 let ty = TypeRef::Vec(Box::new(TypeRef::String));
2017 let result = field_conversion_to_core("tags", &ty, false);
2018 assert_eq!(result, "tags: val.tags");
2019 }
2020
2021 #[test]
2022 fn test_field_conversion_to_core_vec_string_optional() {
2023 let ty = TypeRef::Vec(Box::new(TypeRef::String));
2025 let result = field_conversion_to_core("tags", &ty, true);
2026 assert_eq!(result, "tags: val.tags");
2027 }
2028
2029 #[test]
2030 fn test_field_conversion_to_core_map_named_key() {
2031 let ty = TypeRef::Map(Box::new(TypeRef::Named("Key".into())), Box::new(TypeRef::String));
2033 let result = field_conversion_to_core("map", &ty, false);
2034 assert!(result.contains("k.into()"));
2035 }
2036
2037 #[test]
2038 fn test_field_conversion_to_core_map_json_key() {
2039 let ty = TypeRef::Map(Box::new(TypeRef::Json), Box::new(TypeRef::String));
2041 let result = field_conversion_to_core("map", &ty, false);
2042 assert!(result.contains("serde_json::from_str(&k)"));
2043 }
2044
2045 #[test]
2046 fn test_field_conversion_from_core_optional_json_inner() {
2047 let ty = TypeRef::Optional(Box::new(TypeRef::Json));
2049 let result = field_conversion_from_core("meta", &ty, false, false, &no_opaques());
2050 assert_eq!(result, "meta: val.meta.as_ref().map(ToString::to_string)");
2051 }
2052
2053 #[test]
2054 fn test_field_conversion_from_core_optional_path_inner() {
2055 let ty = TypeRef::Optional(Box::new(TypeRef::Path));
2057 let result = field_conversion_from_core("file", &ty, false, false, &no_opaques());
2058 assert_eq!(result, "file: val.file.map(|p| p.to_string_lossy().to_string())");
2059 }
2060
2061 #[test]
2062 fn test_field_conversion_from_core_map_json_keys() {
2063 let ty = TypeRef::Map(Box::new(TypeRef::Json), Box::new(TypeRef::String));
2065 let result = field_conversion_from_core("map", &ty, false, false, &no_opaques());
2066 assert!(result.contains("k.to_string()"));
2067 }
2068
2069 #[test]
2074 fn test_is_tuple_variant_single_positional_field() {
2075 let fields = vec![make_field("_0", TypeRef::String)];
2076 assert!(is_tuple_variant(&fields));
2077 }
2078
2079 #[test]
2080 fn test_is_tuple_variant_true_for_underscore_only() {
2081 let fields = vec![make_field("_", TypeRef::String)];
2083 assert!(is_tuple_variant(&fields));
2084 }
2085
2086 #[test]
2087 fn test_is_tuple_variant_false_for_field_starting_with_underscore_then_alpha() {
2088 let fields = vec![make_field("_foo", TypeRef::String)];
2090 assert!(!is_tuple_variant(&fields));
2091 }
2092
2093 #[test]
2094 fn test_is_tuple_variant_three_positional_fields() {
2095 let fields = vec![
2096 make_field("_0", TypeRef::String),
2097 make_field("_1", TypeRef::Primitive(PrimitiveType::I32)),
2098 make_field("_2", TypeRef::Primitive(PrimitiveType::F64)),
2099 ];
2100 assert!(is_tuple_variant(&fields));
2101 }
2102
2103 #[test]
2104 fn test_is_tuple_type_name_empty_string_is_false() {
2105 assert!(!helpers::is_tuple_type_name(""));
2106 }
2107
2108 #[test]
2109 fn test_is_tuple_type_name_space_is_false() {
2110 assert!(!helpers::is_tuple_type_name("String"));
2111 }
2112
2113 #[test]
2118 fn test_core_type_path_with_hyphen_and_double_colon() {
2119 let typ = make_type("Config", "my-crate::module::Config", vec![]);
2121 assert_eq!(core_type_path(&typ, "my_crate"), "my_crate::module::Config");
2122 }
2123
2124 #[test]
2125 fn test_core_type_path_rust_path_matches_core_import_prefix() {
2126 let typ = make_type("Config", "my_crate::Config", vec![]);
2128 assert_eq!(core_type_path(&typ, "my_crate"), "my_crate::Config");
2129 }
2130
2131 #[test]
2136 fn test_build_type_path_map_multiple_types() {
2137 let surface = ApiSurface {
2138 crate_name: "my_crate".into(),
2139 version: "1.0.0".into(),
2140 types: vec![
2141 make_type("Config", "my_crate::Config", vec![]),
2142 make_type("Result", "my_crate::types::Result", vec![]),
2143 ],
2144 functions: vec![],
2145 enums: vec![
2146 make_enum("Mode", "my_crate::Mode", &["A"]),
2147 make_enum("Status", "Status", &["Ok"]),
2148 ],
2149 errors: vec![],
2150 };
2151 let map = build_type_path_map(&surface, "my_crate");
2152 assert_eq!(map.get("Config").map(String::as_str), Some("my_crate::Config"));
2153 assert_eq!(map.get("Result").map(String::as_str), Some("my_crate::types::Result"));
2154 assert_eq!(map.get("Mode").map(String::as_str), Some("my_crate::Mode"));
2155 assert_eq!(map.get("Status").map(String::as_str), Some("my_crate::Status"));
2157 }
2158
2159 #[test]
2160 fn test_build_type_path_map_normalizes_hyphens() {
2161 let surface = ApiSurface {
2162 crate_name: "my_crate".into(),
2163 version: "1.0.0".into(),
2164 types: vec![make_type("Config", "my-crate::Config", vec![])],
2165 functions: vec![],
2166 enums: vec![],
2167 errors: vec![],
2168 };
2169 let map = build_type_path_map(&surface, "my_crate");
2170 assert_eq!(map.get("Config").map(String::as_str), Some("my_crate::Config"));
2171 }
2172
2173 #[test]
2174 fn test_build_type_path_map_empty_surface() {
2175 let surface = ApiSurface {
2176 crate_name: "c".into(),
2177 version: "1.0".into(),
2178 types: vec![],
2179 functions: vec![],
2180 enums: vec![],
2181 errors: vec![],
2182 };
2183 let map = build_type_path_map(&surface, "c");
2184 assert!(map.is_empty());
2185 }
2186
2187 #[test]
2192 fn test_gen_from_binding_to_core_empty_fields() {
2193 let typ = make_type("Empty", "c::Empty", vec![]);
2194 let result = gen_from_binding_to_core(&typ, "c");
2195 assert!(result.contains("impl From<Empty> for c::Empty"));
2196 assert!(result.contains("Self {"));
2197 }
2198
2199 #[test]
2200 fn test_gen_from_core_to_binding_empty_fields() {
2201 let typ = make_type("Empty", "c::Empty", vec![]);
2202 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
2203 assert!(result.contains("impl From<c::Empty> for Empty"));
2204 assert!(result.contains("Self {"));
2205 }
2206
2207 #[test]
2208 fn test_gen_from_binding_to_core_all_optional_fields() {
2209 let typ = make_type(
2210 "Config",
2211 "c::Config",
2212 vec![
2213 make_opt_field("name", TypeRef::String),
2214 make_opt_field("count", TypeRef::Primitive(PrimitiveType::I32)),
2215 ],
2216 );
2217 let result = gen_from_binding_to_core(&typ, "c");
2218 assert!(result.contains("name: val.name"));
2219 assert!(result.contains("count: val.count"));
2220 }
2221
2222 #[test]
2223 fn test_gen_from_binding_to_core_single_string_field() {
2224 let typ = make_type("S", "c::S", vec![make_field("value", TypeRef::String)]);
2225 let result = gen_from_binding_to_core(&typ, "c");
2226 assert!(result.contains("value: val.value"));
2227 }
2228
2229 #[test]
2230 fn test_gen_from_core_to_binding_single_optional_named_field() {
2231 let field = make_opt_field("inner", TypeRef::Named("Inner".into()));
2232 let typ = make_type("Wrapper", "c::Wrapper", vec![field]);
2233 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
2234 assert!(result.contains("inner: val.inner.map(Into::into)"));
2235 }
2236
2237 #[test]
2242 fn test_binding_to_core_match_arm_ext_cfg_unit_variant() {
2243 let config = ConversionConfig::default();
2244 let result = helpers::binding_to_core_match_arm_ext_cfg("MyEnum", "Foo", &[], false, &config);
2245 assert_eq!(result, "MyEnum::Foo => Self::Foo,");
2246 }
2247
2248 #[test]
2249 fn test_binding_to_core_match_arm_ext_cfg_no_binding_data_named_fields() {
2250 let config = ConversionConfig::default();
2251 let fields = vec![make_field("value", TypeRef::String)];
2252 let result = helpers::binding_to_core_match_arm_ext_cfg("MyEnum", "Bar", &fields, false, &config);
2253 assert!(result.contains("value: Default::default()"));
2254 }
2255
2256 #[test]
2257 fn test_binding_to_core_match_arm_ext_cfg_no_binding_data_tuple_fields() {
2258 let config = ConversionConfig::default();
2259 let fields = vec![make_field("_0", TypeRef::String)];
2260 let result = helpers::binding_to_core_match_arm_ext_cfg("MyEnum", "Bar", &fields, false, &config);
2261 assert!(result.contains("Default::default()"));
2262 assert!(result.contains("Self::Bar("));
2263 }
2264
2265 #[test]
2266 fn test_binding_to_core_match_arm_ext_cfg_with_binding_data_named() {
2267 let config = ConversionConfig::default();
2268 let fields = vec![make_field("value", TypeRef::Named("Inner".into()))];
2269 let result = helpers::binding_to_core_match_arm_ext_cfg("MyEnum", "Bar", &fields, true, &config);
2270 assert!(result.contains("value: value.into()"));
2271 }
2272
2273 #[test]
2274 fn test_binding_to_core_match_arm_ext_cfg_with_binding_data_tuple() {
2275 let config = ConversionConfig::default();
2276 let fields = vec![make_field("_0", TypeRef::Named("Inner".into()))];
2277 let result = helpers::binding_to_core_match_arm_ext_cfg("MyEnum", "Bar", &fields, true, &config);
2278 assert!(result.contains("_0.into()"));
2279 }
2280
2281 #[test]
2282 fn test_binding_to_core_match_arm_ext_cfg_cast_u64_field() {
2283 let config = ConversionConfig {
2284 cast_large_ints_to_i64: true,
2285 ..Default::default()
2286 };
2287 let fields = vec![make_field("count", TypeRef::Primitive(PrimitiveType::U64))];
2288 let result = helpers::binding_to_core_match_arm_ext_cfg("MyEnum", "Bar", &fields, true, &config);
2289 assert!(result.contains("as u64"));
2290 }
2291
2292 #[test]
2297 fn test_core_to_binding_match_arm_ext_cfg_unit_variant() {
2298 let config = ConversionConfig::default();
2299 let result = helpers::core_to_binding_match_arm_ext_cfg("CoreEnum", "Foo", &[], false, &config);
2300 assert_eq!(result, "CoreEnum::Foo => Self::Foo,");
2301 }
2302
2303 #[test]
2304 fn test_core_to_binding_match_arm_ext_cfg_no_binding_data_named() {
2305 let config = ConversionConfig::default();
2306 let fields = vec![make_field("x", TypeRef::Primitive(PrimitiveType::I32))];
2307 let result = helpers::core_to_binding_match_arm_ext_cfg("CoreEnum", "Foo", &fields, false, &config);
2308 assert!(result.contains("{ .. }"));
2309 assert!(result.contains("Self::Foo"));
2310 }
2311
2312 #[test]
2313 fn test_core_to_binding_match_arm_ext_cfg_no_binding_data_tuple() {
2314 let config = ConversionConfig::default();
2315 let fields = vec![make_field("_0", TypeRef::Primitive(PrimitiveType::I32))];
2316 let result = helpers::core_to_binding_match_arm_ext_cfg("CoreEnum", "Foo", &fields, false, &config);
2317 assert!(result.contains("(..)"));
2318 assert!(result.contains("Self::Foo"));
2319 }
2320
2321 #[test]
2322 fn test_core_to_binding_match_arm_ext_cfg_with_binding_data_named_fields() {
2323 let config = ConversionConfig::default();
2324 let fields = vec![make_field("value", TypeRef::Named("Inner".into()))];
2325 let result = helpers::core_to_binding_match_arm_ext_cfg("CoreEnum", "Foo", &fields, true, &config);
2326 assert!(result.contains("value: value.into()"));
2327 }
2328
2329 #[test]
2330 fn test_core_to_binding_match_arm_ext_cfg_with_binding_data_tuple() {
2331 let config = ConversionConfig::default();
2332 let fields = vec![make_field("_0", TypeRef::Named("Inner".into()))];
2333 let result = helpers::core_to_binding_match_arm_ext_cfg("CoreEnum", "Foo", &fields, true, &config);
2334 assert!(result.contains("_0: _0.into()"));
2335 }
2336
2337 #[test]
2338 fn test_core_to_binding_match_arm_ext_cfg_cast_u64_field() {
2339 let config = ConversionConfig {
2340 cast_large_ints_to_i64: true,
2341 ..Default::default()
2342 };
2343 let fields = vec![make_field("count", TypeRef::Primitive(PrimitiveType::U64))];
2344 let result = helpers::core_to_binding_match_arm_ext_cfg("CoreEnum", "Bar", &fields, true, &config);
2345 assert!(result.contains("as i64"));
2346 }
2347
2348 #[test]
2353 fn test_core_to_binding_match_arm_ext_binding_has_data_named_fields() {
2354 let fields = vec![make_field("value", TypeRef::Named("Inner".into()))];
2355 let result = helpers::core_to_binding_match_arm_ext("CoreEnum", "Bar", &fields, true);
2356 assert!(result.contains("value: value.into()"));
2357 }
2358
2359 #[test]
2360 fn test_core_to_binding_match_arm_ext_binding_has_data_tuple_fields() {
2361 let fields = vec![make_field("_0", TypeRef::Named("Inner".into()))];
2362 let result = helpers::core_to_binding_match_arm_ext("CoreEnum", "Bar", &fields, true);
2363 assert!(result.contains("_0: _0.into()"));
2364 }
2365
2366 #[test]
2367 fn test_core_to_binding_match_arm_ext_binding_has_data_plain_field() {
2368 let fields = vec![make_field("x", TypeRef::Primitive(PrimitiveType::I32))];
2369 let result = helpers::core_to_binding_match_arm_ext("CoreEnum", "Bar", &fields, true);
2370 assert!(result.contains("x: x"));
2371 }
2372
2373 #[test]
2374 fn test_core_to_binding_match_arm_ext_binding_has_data_sanitized_field() {
2375 let mut field = make_field("complex", TypeRef::String);
2376 field.sanitized = true;
2377 let result = helpers::core_to_binding_match_arm_ext("CoreEnum", "Bar", &[field], true);
2378 assert!(result.contains("serde_json::to_string("));
2379 }
2380
2381 #[test]
2386 fn test_input_type_names_from_method_params() {
2387 let surface = ApiSurface {
2388 crate_name: "my_crate".into(),
2389 version: "1.0.0".into(),
2390 types: vec![TypeDef {
2391 name: "Client".into(),
2392 rust_path: "my_crate::Client".into(),
2393 original_rust_path: String::new(),
2394 fields: vec![],
2395 methods: vec![MethodDef {
2396 name: "process".into(),
2397 params: vec![ParamDef {
2398 name: "config".into(),
2399 ty: TypeRef::Named("Config".into()),
2400 optional: false,
2401 default: None,
2402 sanitized: false,
2403 typed_default: None,
2404 is_ref: false,
2405 is_mut: false,
2406 newtype_wrapper: None,
2407 original_type: None,
2408 }],
2409 return_type: TypeRef::Unit,
2410 is_async: false,
2411 is_static: false,
2412 error_type: None,
2413 doc: String::new(),
2414 receiver: None,
2415 sanitized: false,
2416 trait_source: None,
2417 returns_ref: false,
2418 returns_cow: false,
2419 return_newtype_wrapper: None,
2420 has_default_impl: false,
2421 }],
2422 is_opaque: false,
2423 is_clone: true,
2424 is_trait: false,
2425 has_default: false,
2426 has_stripped_cfg_fields: false,
2427 is_return_type: false,
2428 serde_rename_all: None,
2429 has_serde: false,
2430 super_traits: vec![],
2431 doc: String::new(),
2432 cfg: None,
2433 }],
2434 functions: vec![],
2435 enums: vec![],
2436 errors: vec![],
2437 };
2438 let names = input_type_names(&surface);
2439 assert!(names.contains("Config"));
2440 }
2441
2442 #[test]
2443 fn test_input_type_names_transitive_closure() {
2444 let config_type = make_type(
2446 "Config",
2447 "c::Config",
2448 vec![make_field("backend", TypeRef::Named("Backend".into()))],
2449 );
2450 let surface = ApiSurface {
2451 crate_name: "c".into(),
2452 version: "1.0".into(),
2453 types: vec![config_type],
2454 functions: vec![FunctionDef {
2455 name: "run".into(),
2456 rust_path: "c::run".into(),
2457 original_rust_path: String::new(),
2458 params: vec![ParamDef {
2459 name: "config".into(),
2460 ty: TypeRef::Named("Config".into()),
2461 optional: false,
2462 default: None,
2463 sanitized: false,
2464 typed_default: None,
2465 is_ref: false,
2466 is_mut: false,
2467 newtype_wrapper: None,
2468 original_type: None,
2469 }],
2470 return_type: TypeRef::Unit,
2471 is_async: false,
2472 error_type: None,
2473 doc: String::new(),
2474 cfg: None,
2475 sanitized: false,
2476 returns_ref: false,
2477 returns_cow: false,
2478 return_newtype_wrapper: None,
2479 }],
2480 enums: vec![],
2481 errors: vec![],
2482 };
2483 let names = input_type_names(&surface);
2484 assert!(names.contains("Config"));
2485 assert!(names.contains("Backend"));
2486 }
2487
2488 #[test]
2493 fn test_convertible_types_sanitized_field_with_has_default() {
2494 let mut field = make_field("complex", TypeRef::String);
2495 field.sanitized = true;
2496 let mut typ = make_type("Config", "c::Config", vec![field]);
2497 typ.has_default = true;
2498 let surface = ApiSurface {
2499 crate_name: "c".into(),
2500 version: "1.0".into(),
2501 types: vec![typ],
2502 functions: vec![],
2503 enums: vec![],
2504 errors: vec![],
2505 };
2506 let result = convertible_types(&surface);
2508 assert!(result.contains("Config"));
2509 }
2510
2511 #[test]
2512 fn test_convertible_types_opaque_type_excluded() {
2513 let mut typ = make_type("Client", "c::Client", vec![]);
2514 typ.is_opaque = true;
2515 let surface = ApiSurface {
2516 crate_name: "c".into(),
2517 version: "1.0".into(),
2518 types: vec![typ],
2519 functions: vec![],
2520 enums: vec![],
2521 errors: vec![],
2522 };
2523 let result = convertible_types(&surface);
2525 assert!(!result.contains("Client"));
2526 }
2527
2528 #[test]
2529 fn test_convertible_types_type_with_named_field_in_surface() {
2530 let config_field = make_field("backend", TypeRef::Named("Backend".into()));
2532 let config = make_type("Config", "c::Config", vec![config_field]);
2533 let backend = make_type("Backend", "c::Backend", vec![]);
2534 let surface = ApiSurface {
2535 crate_name: "c".into(),
2536 version: "1.0".into(),
2537 types: vec![config, backend],
2538 functions: vec![],
2539 enums: vec![],
2540 errors: vec![],
2541 };
2542 let result = convertible_types(&surface);
2543 assert!(result.contains("Config"));
2544 assert!(result.contains("Backend"));
2545 }
2546
2547 #[test]
2552 fn test_core_enum_path_with_hyphen_normalization() {
2553 let e = make_enum("Status", "my-crate::Status", &[]);
2554 assert_eq!(core_enum_path(&e, "my_crate"), "my_crate::Status");
2555 }
2556
2557 #[test]
2558 fn test_core_enum_path_already_starts_with_core_import() {
2559 let e = make_enum("Mode", "my_crate::inner::Mode", &[]);
2561 assert_eq!(core_enum_path(&e, "my_crate"), "my_crate::inner::Mode");
2562 }
2563
2564 #[test]
2569 fn test_needs_i64_cast_true_for_large_ints() {
2570 use super::helpers::*;
2571 assert!(needs_i64_cast(&PrimitiveType::U64));
2572 assert!(needs_i64_cast(&PrimitiveType::Usize));
2573 assert!(needs_i64_cast(&PrimitiveType::Isize));
2574 }
2575
2576 #[test]
2577 fn test_needs_i64_cast_false_for_small_ints() {
2578 use super::helpers::*;
2579 assert!(!needs_i64_cast(&PrimitiveType::I32));
2580 assert!(!needs_i64_cast(&PrimitiveType::U32));
2581 assert!(!needs_i64_cast(&PrimitiveType::F64));
2582 }
2583
2584 #[test]
2585 fn test_core_prim_str_all_variants() {
2586 use super::helpers::core_prim_str;
2587 assert_eq!(core_prim_str(&PrimitiveType::U64), "u64");
2588 assert_eq!(core_prim_str(&PrimitiveType::Usize), "usize");
2589 assert_eq!(core_prim_str(&PrimitiveType::Isize), "isize");
2590 assert_eq!(core_prim_str(&PrimitiveType::F32), "f32");
2591 assert_eq!(core_prim_str(&PrimitiveType::Bool), "bool");
2592 assert_eq!(core_prim_str(&PrimitiveType::U8), "u8");
2593 assert_eq!(core_prim_str(&PrimitiveType::U16), "u16");
2594 assert_eq!(core_prim_str(&PrimitiveType::U32), "u32");
2595 assert_eq!(core_prim_str(&PrimitiveType::I8), "i8");
2596 assert_eq!(core_prim_str(&PrimitiveType::I16), "i16");
2597 assert_eq!(core_prim_str(&PrimitiveType::I32), "i32");
2598 assert_eq!(core_prim_str(&PrimitiveType::I64), "i64");
2599 assert_eq!(core_prim_str(&PrimitiveType::F64), "f64");
2600 }
2601
2602 #[test]
2603 fn test_binding_prim_str_large_ints_map_to_i64() {
2604 use super::helpers::binding_prim_str;
2605 assert_eq!(binding_prim_str(&PrimitiveType::U64), "i64");
2606 assert_eq!(binding_prim_str(&PrimitiveType::Usize), "i64");
2607 assert_eq!(binding_prim_str(&PrimitiveType::Isize), "i64");
2608 }
2609
2610 #[test]
2611 fn test_binding_prim_str_small_ints_map_to_i32() {
2612 use super::helpers::binding_prim_str;
2613 assert_eq!(binding_prim_str(&PrimitiveType::U8), "i32");
2614 assert_eq!(binding_prim_str(&PrimitiveType::U16), "i32");
2615 assert_eq!(binding_prim_str(&PrimitiveType::U32), "i32");
2616 assert_eq!(binding_prim_str(&PrimitiveType::I8), "i32");
2617 assert_eq!(binding_prim_str(&PrimitiveType::I16), "i32");
2618 assert_eq!(binding_prim_str(&PrimitiveType::I32), "i32");
2619 }
2620
2621 #[test]
2622 fn test_binding_prim_str_float_and_i64() {
2623 use super::helpers::binding_prim_str;
2624 assert_eq!(binding_prim_str(&PrimitiveType::F32), "f64");
2625 assert_eq!(binding_prim_str(&PrimitiveType::F64), "f64");
2626 assert_eq!(binding_prim_str(&PrimitiveType::I64), "i64");
2627 assert_eq!(binding_prim_str(&PrimitiveType::Bool), "bool");
2628 }
2629}