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]
681 fn test_is_tuple_variant_true_for_positional_fields() {
682 let fields = vec![
683 make_field("_0", TypeRef::String),
684 make_field("_1", TypeRef::Primitive(PrimitiveType::I32)),
685 ];
686 assert!(is_tuple_variant(&fields));
687 }
688
689 #[test]
690 fn test_is_tuple_variant_false_for_named_fields() {
691 let fields = vec![make_field("name", TypeRef::String)];
692 assert!(!is_tuple_variant(&fields));
693 }
694
695 #[test]
696 fn test_is_tuple_variant_false_for_empty_fields() {
697 assert!(!is_tuple_variant(&[]));
698 }
699
700 #[test]
701 fn test_is_tuple_type_name_true() {
702 assert!(helpers::is_tuple_type_name("(String, u32)"));
703 }
704
705 #[test]
706 fn test_is_tuple_type_name_false() {
707 assert!(!helpers::is_tuple_type_name("Config"));
708 }
709
710 #[test]
715 fn test_field_references_excluded_type_direct_match() {
716 let ty = TypeRef::Named("JsValue".into());
717 assert!(field_references_excluded_type(&ty, &["JsValue".to_string()]));
718 }
719
720 #[test]
721 fn test_field_references_excluded_type_no_match() {
722 let ty = TypeRef::Named("Config".into());
723 assert!(!field_references_excluded_type(&ty, &["JsValue".to_string()]));
724 }
725
726 #[test]
727 fn test_field_references_excluded_type_inside_optional() {
728 let ty = TypeRef::Optional(Box::new(TypeRef::Named("Excluded".into())));
729 assert!(field_references_excluded_type(&ty, &["Excluded".to_string()]));
730 }
731
732 #[test]
733 fn test_field_references_excluded_type_inside_vec() {
734 let ty = TypeRef::Vec(Box::new(TypeRef::Named("Excluded".into())));
735 assert!(field_references_excluded_type(&ty, &["Excluded".to_string()]));
736 }
737
738 #[test]
739 fn test_field_references_excluded_type_inside_map_key() {
740 let ty = TypeRef::Map(Box::new(TypeRef::Named("Excluded".into())), Box::new(TypeRef::String));
741 assert!(field_references_excluded_type(&ty, &["Excluded".to_string()]));
742 }
743
744 #[test]
745 fn test_field_references_excluded_type_inside_map_value() {
746 let ty = TypeRef::Map(Box::new(TypeRef::String), Box::new(TypeRef::Named("Excluded".into())));
747 assert!(field_references_excluded_type(&ty, &["Excluded".to_string()]));
748 }
749
750 #[test]
751 fn test_field_references_excluded_type_primitive_not_excluded() {
752 let ty = TypeRef::Primitive(PrimitiveType::I32);
753 assert!(!field_references_excluded_type(&ty, &["JsValue".to_string()]));
754 }
755
756 #[test]
761 fn test_can_generate_enum_conversion_with_variants() {
762 let e = make_enum("Color", "crate::Color", &["Red", "Green"]);
763 assert!(can_generate_enum_conversion(&e));
764 }
765
766 #[test]
767 fn test_can_generate_enum_conversion_empty_variants() {
768 let e = make_enum("Empty", "crate::Empty", &[]);
769 assert!(!can_generate_enum_conversion(&e));
770 }
771
772 #[test]
773 fn test_can_generate_enum_conversion_from_core_with_variants() {
774 let e = make_enum("Color", "crate::Color", &["Red"]);
775 assert!(can_generate_enum_conversion_from_core(&e));
776 }
777
778 #[test]
783 fn test_core_type_path_with_full_path() {
784 let typ = make_type("Config", "my_crate::types::Config", vec![]);
785 assert_eq!(core_type_path(&typ, "my_crate"), "my_crate::types::Config");
786 }
787
788 #[test]
789 fn test_core_type_path_with_bare_name() {
790 let typ = make_type("Config", "Config", vec![]);
792 assert_eq!(core_type_path(&typ, "my_crate"), "my_crate::Config");
793 }
794
795 #[test]
796 fn test_core_type_path_normalizes_hyphens() {
797 let typ = make_type("Config", "my-crate::Config", vec![]);
798 assert_eq!(core_type_path(&typ, "my_crate"), "my_crate::Config");
799 }
800
801 #[test]
802 fn test_core_enum_path_with_full_path() {
803 let e = make_enum("Backend", "my_crate::Backend", &[]);
804 assert_eq!(core_enum_path(&e, "my_crate"), "my_crate::Backend");
805 }
806
807 #[test]
808 fn test_core_enum_path_bare_name_gets_prefixed() {
809 let e = make_enum("Backend", "Backend", &[]);
810 assert_eq!(core_enum_path(&e, "my_crate"), "my_crate::Backend");
811 }
812
813 #[test]
818 fn test_build_type_path_map_includes_types_and_enums() {
819 let surface = ApiSurface {
820 crate_name: "my_crate".into(),
821 version: "1.0.0".into(),
822 types: vec![make_type("Config", "my_crate::Config", vec![])],
823 functions: vec![],
824 enums: vec![make_enum("Mode", "my_crate::Mode", &["A"])],
825 errors: vec![],
826 };
827 let map = build_type_path_map(&surface, "my_crate");
828 assert_eq!(map.get("Config").map(String::as_str), Some("my_crate::Config"));
829 assert_eq!(map.get("Mode").map(String::as_str), Some("my_crate::Mode"));
830 }
831
832 #[test]
833 fn test_resolve_named_path_found_in_map() {
834 let mut map = ahash::AHashMap::new();
835 map.insert("Config".to_string(), "my_crate::types::Config".to_string());
836 assert_eq!(
837 resolve_named_path("Config", "my_crate", &map),
838 "my_crate::types::Config"
839 );
840 }
841
842 #[test]
843 fn test_resolve_named_path_not_found_falls_back() {
844 let map = ahash::AHashMap::new();
845 assert_eq!(resolve_named_path("Unknown", "my_crate", &map), "my_crate::Unknown");
846 }
847
848 #[test]
853 fn test_input_type_names_from_function_params() {
854 let surface = ApiSurface {
855 crate_name: "my_crate".into(),
856 version: "1.0.0".into(),
857 types: vec![],
858 functions: vec![FunctionDef {
859 name: "process".into(),
860 rust_path: "my_crate::process".into(),
861 original_rust_path: String::new(),
862 params: vec![ParamDef {
863 name: "config".into(),
864 ty: TypeRef::Named("Config".into()),
865 optional: false,
866 default: None,
867 sanitized: false,
868 typed_default: None,
869 is_ref: false,
870 is_mut: false,
871 newtype_wrapper: None,
872 }],
873 return_type: TypeRef::Unit,
874 is_async: false,
875 error_type: None,
876 doc: String::new(),
877 cfg: None,
878 sanitized: false,
879 returns_ref: false,
880 returns_cow: false,
881 return_newtype_wrapper: None,
882 }],
883 enums: vec![],
884 errors: vec![],
885 };
886 let names = input_type_names(&surface);
887 assert!(names.contains("Config"));
888 }
889
890 #[test]
891 fn test_input_type_names_from_return_types() {
892 let surface = ApiSurface {
893 crate_name: "my_crate".into(),
894 version: "1.0.0".into(),
895 types: vec![],
896 functions: vec![FunctionDef {
897 name: "get_result".into(),
898 rust_path: "my_crate::get_result".into(),
899 original_rust_path: String::new(),
900 params: vec![],
901 return_type: TypeRef::Named("Result".into()),
902 is_async: false,
903 error_type: None,
904 doc: String::new(),
905 cfg: None,
906 sanitized: false,
907 returns_ref: false,
908 returns_cow: false,
909 return_newtype_wrapper: None,
910 }],
911 enums: vec![],
912 errors: vec![],
913 };
914 let names = input_type_names(&surface);
915 assert!(names.contains("Result"));
916 }
917
918 #[test]
923 fn test_binding_to_core_match_arm_unit_variant() {
924 let result = binding_to_core_match_arm("MyEnum", "Foo", &[]);
925 assert_eq!(result, "MyEnum::Foo => Self::Foo,");
926 }
927
928 #[test]
929 fn test_binding_to_core_match_arm_data_variant_no_binding_data() {
930 let fields = vec![make_field("value", TypeRef::String)];
932 let result = helpers::binding_to_core_match_arm_ext("MyEnum", "Foo", &fields, false);
933 assert!(result.contains("value: Default::default()"));
934 }
935
936 #[test]
937 fn test_binding_to_core_match_arm_tuple_variant_no_binding_data() {
938 let fields = vec![make_field("_0", TypeRef::String)];
939 let result = helpers::binding_to_core_match_arm_ext("MyEnum", "Foo", &fields, false);
940 assert!(result.contains("Default::default()"));
941 assert!(result.contains("Self::Foo("));
942 }
943
944 #[test]
945 fn test_binding_to_core_match_arm_data_variant_with_binding_data_named() {
946 let fields = vec![make_field("value", TypeRef::Named("Inner".into()))];
948 let result = helpers::binding_to_core_match_arm_ext("MyEnum", "Foo", &fields, true);
949 assert!(result.contains("value: value.into()"));
950 }
951
952 #[test]
953 fn test_binding_to_core_match_arm_data_variant_with_binding_data_tuple() {
954 let fields = vec![make_field("_0", TypeRef::Named("Inner".into()))];
955 let result = helpers::binding_to_core_match_arm_ext("MyEnum", "Foo", &fields, true);
956 assert!(result.contains("_0.into()"));
957 }
958
959 #[test]
960 fn test_core_to_binding_match_arm_unit_variant() {
961 let result = core_to_binding_match_arm("CoreEnum", "Bar", &[]);
962 assert_eq!(result, "CoreEnum::Bar => Self::Bar,");
963 }
964
965 #[test]
966 fn test_core_to_binding_match_arm_data_named_no_binding_data() {
967 let fields = vec![make_field("x", TypeRef::Primitive(PrimitiveType::I32))];
968 let result = helpers::core_to_binding_match_arm_ext("CoreEnum", "Bar", &fields, false);
969 assert!(result.contains("{ .. }"));
970 assert!(result.contains("Self::Bar"));
971 }
972
973 #[test]
974 fn test_core_to_binding_match_arm_data_tuple_no_binding_data() {
975 let fields = vec![make_field("_0", TypeRef::Primitive(PrimitiveType::I32))];
976 let result = helpers::core_to_binding_match_arm_ext("CoreEnum", "Bar", &fields, false);
977 assert!(result.contains("(..)"));
978 assert!(result.contains("Self::Bar"));
979 }
980
981 #[test]
986 fn test_has_sanitized_fields_false() {
987 let typ = make_type("Foo", "crate::Foo", vec![make_field("x", TypeRef::String)]);
988 assert!(!has_sanitized_fields(&typ));
989 }
990
991 #[test]
992 fn test_has_sanitized_fields_true() {
993 let mut field = make_field("x", TypeRef::String);
994 field.sanitized = true;
995 let typ = make_type("Foo", "crate::Foo", vec![field]);
996 assert!(has_sanitized_fields(&typ));
997 }
998
999 #[test]
1004 fn test_gen_from_binding_to_core_string_field() {
1005 let typ = make_type("S", "c::S", vec![make_field("title", TypeRef::String)]);
1006 let result = gen_from_binding_to_core(&typ, "c");
1007 assert!(result.contains("impl From<S> for c::S"));
1008 assert!(result.contains("title: val.title"));
1009 }
1010
1011 #[test]
1012 fn test_gen_from_binding_to_core_duration_field() {
1013 let typ = make_type("S", "c::S", vec![make_field("timeout", TypeRef::Duration)]);
1014 let result = gen_from_binding_to_core(&typ, "c");
1015 assert!(result.contains("std::time::Duration::from_millis(val.timeout)"));
1016 }
1017
1018 #[test]
1019 fn test_gen_from_binding_to_core_optional_named_field() {
1020 let field = make_opt_field("backend", TypeRef::Named("Backend".into()));
1021 let typ = make_type("S", "c::S", vec![field]);
1022 let result = gen_from_binding_to_core(&typ, "c");
1023 assert!(result.contains("backend: val.backend.map(Into::into)"));
1024 }
1025
1026 #[test]
1027 fn test_gen_from_binding_to_core_vec_named_field() {
1028 let field = make_field("items", TypeRef::Vec(Box::new(TypeRef::Named("Item".into()))));
1029 let typ = make_type("S", "c::S", vec![field]);
1030 let result = gen_from_binding_to_core(&typ, "c");
1031 assert!(result.contains("into_iter().map(Into::into).collect()"));
1032 }
1033
1034 #[test]
1035 fn test_gen_from_binding_to_core_sanitized_field_uses_default() {
1036 let mut field = make_field("complex", TypeRef::String);
1037 field.sanitized = true;
1038 let typ = make_type("S", "c::S", vec![field]);
1039 let result = gen_from_binding_to_core(&typ, "c");
1040 assert!(result.contains("complex: Default::default()"));
1041 }
1042
1043 #[test]
1044 fn test_gen_from_binding_to_core_with_stripped_cfg_fields_uses_default_update() {
1045 let mut typ = make_type("S", "c::S", vec![make_field("x", TypeRef::String)]);
1046 typ.has_stripped_cfg_fields = true;
1047 let result = gen_from_binding_to_core(&typ, "c");
1048 assert!(result.contains("..Default::default()"));
1049 assert!(result.contains("#[allow(clippy::needless_update)]"));
1050 }
1051
1052 #[test]
1053 fn test_gen_from_binding_to_core_with_type_name_prefix() {
1054 let typ = make_type("Config", "c::Config", vec![make_field("x", TypeRef::String)]);
1055 let config = ConversionConfig {
1056 type_name_prefix: "Js",
1057 ..Default::default()
1058 };
1059 let result = gen_from_binding_to_core_cfg(&typ, "c", &config);
1060 assert!(result.contains("impl From<JsConfig> for c::Config"));
1061 }
1062
1063 #[test]
1064 fn test_gen_from_binding_to_core_path_field() {
1065 let field = make_field("file", TypeRef::Path);
1066 let typ = make_type("S", "c::S", vec![field]);
1067 let result = gen_from_binding_to_core(&typ, "c");
1068 assert!(result.contains("file: val.file.into()"));
1069 }
1070
1071 #[test]
1072 fn test_gen_from_binding_to_core_json_field() {
1073 let field = make_field("meta", TypeRef::Json);
1074 let typ = make_type("S", "c::S", vec![field]);
1075 let result = gen_from_binding_to_core(&typ, "c");
1076 assert!(result.contains("serde_json::from_str(&val.meta).unwrap_or_default()"));
1077 }
1078
1079 #[test]
1080 fn test_gen_from_binding_to_core_newtype_struct() {
1081 let field = make_field("_0", TypeRef::Primitive(PrimitiveType::U32));
1083 let typ = make_type("NodeIndex", "c::NodeIndex", vec![field]);
1084 let result = gen_from_binding_to_core(&typ, "c");
1085 assert!(result.contains("Self(val._0)"));
1086 }
1087
1088 #[test]
1089 fn test_gen_from_binding_to_core_newtype_named_field() {
1090 let field = make_field("_0", TypeRef::Named("Inner".into()));
1091 let typ = make_type("Wrapper", "c::Wrapper", vec![field]);
1092 let result = gen_from_binding_to_core(&typ, "c");
1093 assert!(result.contains("Self(val._0.into())"));
1094 }
1095
1096 #[test]
1097 fn test_gen_from_binding_to_core_newtype_path_field() {
1098 let field = make_field("_0", TypeRef::Path);
1099 let typ = make_type("PathWrapper", "c::PathWrapper", vec![field]);
1100 let result = gen_from_binding_to_core(&typ, "c");
1101 assert!(result.contains("Self(val._0.into())"));
1102 }
1103
1104 #[test]
1105 fn test_gen_from_binding_to_core_newtype_duration_field() {
1106 let field = make_field("_0", TypeRef::Duration);
1107 let typ = make_type("DurWrapper", "c::DurWrapper", vec![field]);
1108 let result = gen_from_binding_to_core(&typ, "c");
1109 assert!(result.contains("Self(std::time::Duration::from_millis(val._0))"));
1110 }
1111
1112 #[test]
1113 fn test_gen_from_binding_to_core_boxed_named_field() {
1114 let mut field = make_field("child", TypeRef::Named("Child".into()));
1115 field.is_boxed = true;
1116 let typ = make_type("S", "c::S", vec![field]);
1117 let result = gen_from_binding_to_core(&typ, "c");
1118 assert!(result.contains("Box::new("));
1119 }
1120
1121 #[test]
1122 fn test_gen_from_binding_to_core_cast_large_ints_to_i64() {
1123 let field = make_field("count", TypeRef::Primitive(PrimitiveType::U64));
1124 let typ = make_type("S", "c::S", vec![field]);
1125 let config = ConversionConfig {
1126 cast_large_ints_to_i64: true,
1127 ..Default::default()
1128 };
1129 let result = gen_from_binding_to_core_cfg(&typ, "c", &config);
1130 assert!(result.contains("val.count as u64"));
1131 }
1132
1133 #[test]
1134 fn test_gen_from_binding_to_core_exclude_types_skips_field() {
1135 let field = make_field("js_val", TypeRef::Named("JsValue".into()));
1136 let typ = make_type("S", "c::S", vec![field]);
1137 let excluded = vec!["JsValue".to_string()];
1138 let config = ConversionConfig {
1139 exclude_types: &excluded,
1140 ..Default::default()
1141 };
1142 let result = gen_from_binding_to_core_cfg(&typ, "c", &config);
1143 assert!(result.contains("js_val: Default::default()"));
1144 }
1145
1146 #[test]
1151 fn test_gen_from_core_to_binding_string_field() {
1152 let typ = make_type("S", "c::S", vec![make_field("title", TypeRef::String)]);
1153 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
1154 assert!(result.contains("impl From<c::S> for S"));
1155 assert!(result.contains("title: val.title"));
1156 }
1157
1158 #[test]
1159 fn test_gen_from_core_to_binding_duration_field() {
1160 let field = make_field("timeout", TypeRef::Duration);
1161 let typ = make_type("S", "c::S", vec![field]);
1162 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
1163 assert!(result.contains("val.timeout.as_millis() as u64"));
1164 }
1165
1166 #[test]
1167 fn test_gen_from_core_to_binding_path_field() {
1168 let field = make_field("path", TypeRef::Path);
1169 let typ = make_type("S", "c::S", vec![field]);
1170 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
1171 assert!(result.contains("to_string_lossy().to_string()"));
1172 }
1173
1174 #[test]
1175 fn test_gen_from_core_to_binding_json_field() {
1176 let field = make_field("meta", TypeRef::Json);
1177 let typ = make_type("S", "c::S", vec![field]);
1178 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
1179 assert!(result.contains("val.meta.to_string()"));
1180 }
1181
1182 #[test]
1183 fn test_gen_from_core_to_binding_opaque_field() {
1184 let mut opaques = AHashSet::new();
1185 opaques.insert("Client".to_string());
1186 let field = make_field("client", TypeRef::Named("Client".into()));
1187 let typ = make_type("S", "c::S", vec![field]);
1188 let result = gen_from_core_to_binding(&typ, "c", &opaques);
1189 assert!(result.contains("Arc::new(val.client)"));
1190 assert!(result.contains("Client { inner:"));
1191 }
1192
1193 #[test]
1194 fn test_gen_from_core_to_binding_with_type_name_prefix_opaque() {
1195 let mut opaques = AHashSet::new();
1196 opaques.insert("Client".to_string());
1197 let field = make_field("client", TypeRef::Named("Client".into()));
1198 let typ = make_type("S", "c::S", vec![field]);
1199 let config = ConversionConfig {
1200 type_name_prefix: "Js",
1201 ..Default::default()
1202 };
1203 let result = gen_from_core_to_binding_cfg(&typ, "c", &opaques, &config);
1204 assert!(result.contains("JsClient { inner: Arc::new(val.client) }"));
1205 }
1206
1207 #[test]
1208 fn test_gen_from_core_to_binding_sanitized_field() {
1209 let mut field = make_field("complex", TypeRef::String);
1210 field.sanitized = true;
1211 let typ = make_type("S", "c::S", vec![field]);
1212 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
1213 assert!(result.contains("format!("));
1214 }
1215
1216 #[test]
1217 fn test_gen_from_core_to_binding_newtype_struct() {
1218 let field = make_field("_0", TypeRef::Primitive(PrimitiveType::U32));
1219 let typ = make_type("NodeIndex", "c::NodeIndex", vec![field]);
1220 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
1221 assert!(result.contains("Self { _0: val.0 }"));
1222 }
1223
1224 #[test]
1225 fn test_gen_from_core_to_binding_newtype_path_field() {
1226 let field = make_field("_0", TypeRef::Path);
1227 let typ = make_type("PathWrapper", "c::PathWrapper", vec![field]);
1228 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
1229 assert!(result.contains("val.0.to_string_lossy().to_string()"));
1230 }
1231
1232 #[test]
1233 fn test_gen_from_core_to_binding_newtype_duration_field() {
1234 let field = make_field("_0", TypeRef::Duration);
1235 let typ = make_type("DurWrapper", "c::DurWrapper", vec![field]);
1236 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
1237 assert!(result.contains("val.0.as_millis() as u64"));
1238 }
1239
1240 #[test]
1241 fn test_gen_from_core_to_binding_newtype_named_field() {
1242 let field = make_field("_0", TypeRef::Named("Inner".into()));
1243 let typ = make_type("Wrapper", "c::Wrapper", vec![field]);
1244 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
1245 assert!(result.contains("val.0.into()"));
1246 }
1247
1248 #[test]
1249 fn test_gen_from_core_to_binding_cast_large_ints_to_i64() {
1250 let field = make_field("count", TypeRef::Primitive(PrimitiveType::U64));
1251 let typ = make_type("S", "c::S", vec![field]);
1252 let config = ConversionConfig {
1253 cast_large_ints_to_i64: true,
1254 ..Default::default()
1255 };
1256 let result = gen_from_core_to_binding_cfg(&typ, "c", &no_opaques(), &config);
1257 assert!(result.contains("val.count as i64"));
1258 }
1259
1260 #[test]
1261 fn test_gen_from_core_to_binding_cast_f32_to_f64() {
1262 let field = make_field("score", TypeRef::Primitive(PrimitiveType::F32));
1263 let typ = make_type("S", "c::S", vec![field]);
1264 let config = ConversionConfig {
1265 cast_f32_to_f64: true,
1266 ..Default::default()
1267 };
1268 let result = gen_from_core_to_binding_cfg(&typ, "c", &no_opaques(), &config);
1269 assert!(result.contains("val.score as f64"));
1270 }
1271
1272 #[test]
1273 fn test_gen_from_core_to_binding_exclude_types_skips_field() {
1274 let field = make_field("js_val", TypeRef::Named("JsValue".into()));
1275 let typ = make_type("S", "c::S", vec![field]);
1276 let excluded = vec!["JsValue".to_string()];
1277 let config = ConversionConfig {
1278 exclude_types: &excluded,
1279 ..Default::default()
1280 };
1281 let result = gen_from_core_to_binding_cfg(&typ, "c", &no_opaques(), &config);
1282 assert!(!result.contains("js_val:"));
1284 }
1285
1286 #[test]
1287 fn test_gen_from_core_to_binding_vec_json_field() {
1288 let field = make_field("items", TypeRef::Vec(Box::new(TypeRef::Json)));
1289 let typ = make_type("S", "c::S", vec![field]);
1290 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
1291 assert!(result.contains("map(ToString::to_string)"));
1292 }
1293
1294 #[test]
1295 fn test_gen_from_core_to_binding_char_field() {
1296 let field = make_field("sep", TypeRef::Char);
1297 let typ = make_type("S", "c::S", vec![field]);
1298 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
1299 assert!(result.contains("val.sep.to_string()"));
1300 }
1301
1302 #[test]
1303 fn test_gen_from_core_to_binding_bytes_field() {
1304 let field = make_field("data", TypeRef::Bytes);
1305 let typ = make_type("S", "c::S", vec![field]);
1306 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
1307 assert!(result.contains("val.data.to_vec()"));
1308 }
1309
1310 #[test]
1315 fn test_field_conversion_to_core_cfg_no_flags_delegates_to_base() {
1316 let config = ConversionConfig::default();
1317 let result = field_conversion_to_core_cfg("x", &TypeRef::String, false, &config);
1318 assert_eq!(result, "x: val.x");
1319 }
1320
1321 #[test]
1322 fn test_field_conversion_to_core_cfg_cast_u64_to_i64() {
1323 let config = ConversionConfig {
1324 cast_large_ints_to_i64: true,
1325 ..Default::default()
1326 };
1327 let result = field_conversion_to_core_cfg("n", &TypeRef::Primitive(PrimitiveType::U64), false, &config);
1328 assert_eq!(result, "n: val.n as u64");
1329 }
1330
1331 #[test]
1332 fn test_field_conversion_to_core_cfg_cast_usize_to_i64() {
1333 let config = ConversionConfig {
1334 cast_large_ints_to_i64: true,
1335 ..Default::default()
1336 };
1337 let result = field_conversion_to_core_cfg("n", &TypeRef::Primitive(PrimitiveType::Usize), false, &config);
1338 assert_eq!(result, "n: val.n as usize");
1339 }
1340
1341 #[test]
1342 fn test_field_conversion_to_core_cfg_cast_isize_to_i64() {
1343 let config = ConversionConfig {
1344 cast_large_ints_to_i64: true,
1345 ..Default::default()
1346 };
1347 let result = field_conversion_to_core_cfg("n", &TypeRef::Primitive(PrimitiveType::Isize), false, &config);
1348 assert_eq!(result, "n: val.n as isize");
1349 }
1350
1351 #[test]
1352 fn test_field_conversion_to_core_cfg_f32_cast() {
1353 let config = ConversionConfig {
1354 cast_f32_to_f64: true,
1355 ..Default::default()
1356 };
1357 let result = field_conversion_to_core_cfg("s", &TypeRef::Primitive(PrimitiveType::F32), false, &config);
1358 assert_eq!(result, "s: val.s as f32");
1359 }
1360
1361 #[test]
1362 fn test_field_conversion_to_core_cfg_duration_cast() {
1363 let config = ConversionConfig {
1364 cast_large_ints_to_i64: true,
1365 ..Default::default()
1366 };
1367 let result = field_conversion_to_core_cfg("t", &TypeRef::Duration, false, &config);
1368 assert_eq!(result, "t: std::time::Duration::from_millis(val.t as u64)");
1369 }
1370
1371 #[test]
1372 fn test_field_conversion_to_core_cfg_json_to_string() {
1373 let config = ConversionConfig {
1374 json_to_string: true,
1375 ..Default::default()
1376 };
1377 let result = field_conversion_to_core_cfg("m", &TypeRef::Json, false, &config);
1378 assert_eq!(result, "m: Default::default()");
1379 }
1380
1381 #[test]
1382 fn test_field_conversion_to_core_cfg_vec_named_to_string() {
1383 let config = ConversionConfig {
1384 vec_named_to_string: true,
1385 ..Default::default()
1386 };
1387 let ty = TypeRef::Vec(Box::new(TypeRef::Named("Item".into())));
1388 let result = field_conversion_to_core_cfg("items", &ty, false, &config);
1389 assert_eq!(result, "items: serde_json::from_str(&val.items).unwrap_or_default()");
1390 }
1391
1392 #[test]
1397 fn test_field_conversion_from_core_cfg_no_flags_delegates_to_base() {
1398 let config = ConversionConfig::default();
1399 let result = field_conversion_from_core_cfg("x", &TypeRef::String, false, false, &no_opaques(), &config);
1400 assert_eq!(result, "x: val.x");
1401 }
1402
1403 #[test]
1404 fn test_field_conversion_from_core_cfg_cast_u64_to_i64() {
1405 let config = ConversionConfig {
1406 cast_large_ints_to_i64: true,
1407 ..Default::default()
1408 };
1409 let result = field_conversion_from_core_cfg(
1410 "n",
1411 &TypeRef::Primitive(PrimitiveType::U64),
1412 false,
1413 false,
1414 &no_opaques(),
1415 &config,
1416 );
1417 assert_eq!(result, "n: val.n as i64");
1418 }
1419
1420 #[test]
1421 fn test_field_conversion_from_core_cfg_cast_f32_to_f64() {
1422 let config = ConversionConfig {
1423 cast_f32_to_f64: true,
1424 ..Default::default()
1425 };
1426 let result = field_conversion_from_core_cfg(
1427 "s",
1428 &TypeRef::Primitive(PrimitiveType::F32),
1429 false,
1430 false,
1431 &no_opaques(),
1432 &config,
1433 );
1434 assert_eq!(result, "s: val.s as f64");
1435 }
1436
1437 #[test]
1438 fn test_field_conversion_from_core_cfg_duration_cast_to_i64() {
1439 let config = ConversionConfig {
1440 cast_large_ints_to_i64: true,
1441 ..Default::default()
1442 };
1443 let result = field_conversion_from_core_cfg("t", &TypeRef::Duration, false, false, &no_opaques(), &config);
1444 assert_eq!(result, "t: val.t.as_millis() as u64 as i64");
1445 }
1446
1447 #[test]
1448 fn test_field_conversion_from_core_cfg_json_to_string() {
1449 let config = ConversionConfig {
1450 json_to_string: true,
1451 ..Default::default()
1452 };
1453 let result = field_conversion_from_core_cfg("m", &TypeRef::Json, false, false, &no_opaques(), &config);
1454 assert_eq!(result, "m: val.m.to_string()");
1455 }
1456
1457 #[test]
1458 fn test_field_conversion_from_core_cfg_vec_named_to_string() {
1459 let config = ConversionConfig {
1460 vec_named_to_string: true,
1461 ..Default::default()
1462 };
1463 let ty = TypeRef::Vec(Box::new(TypeRef::Named("Item".into())));
1464 let result = field_conversion_from_core_cfg("items", &ty, false, false, &no_opaques(), &config);
1465 assert_eq!(result, "items: serde_json::to_string(&val.items).unwrap_or_default()");
1466 }
1467
1468 #[test]
1469 fn test_field_conversion_from_core_cfg_vec_u64_cast() {
1470 let config = ConversionConfig {
1471 cast_large_ints_to_i64: true,
1472 ..Default::default()
1473 };
1474 let ty = TypeRef::Vec(Box::new(TypeRef::Primitive(PrimitiveType::U64)));
1475 let result = field_conversion_from_core_cfg("ids", &ty, false, false, &no_opaques(), &config);
1476 assert!(result.contains("as i64"));
1477 }
1478
1479 #[test]
1480 fn test_field_conversion_from_core_cfg_vec_f32_cast() {
1481 let config = ConversionConfig {
1482 cast_f32_to_f64: true,
1483 ..Default::default()
1484 };
1485 let ty = TypeRef::Vec(Box::new(TypeRef::Primitive(PrimitiveType::F32)));
1486 let result = field_conversion_from_core_cfg("scores", &ty, false, false, &no_opaques(), &config);
1487 assert!(result.contains("as f64"));
1488 }
1489
1490 #[test]
1491 fn test_field_conversion_from_core_cfg_map_u64_values_cast() {
1492 let config = ConversionConfig {
1493 cast_large_ints_to_i64: true,
1494 ..Default::default()
1495 };
1496 let ty = TypeRef::Map(
1497 Box::new(TypeRef::String),
1498 Box::new(TypeRef::Primitive(PrimitiveType::U64)),
1499 );
1500 let result = field_conversion_from_core_cfg("map", &ty, false, false, &no_opaques(), &config);
1501 assert!(result.contains("as i64"));
1502 }
1503
1504 #[test]
1509 fn test_convertible_types_simple_struct() {
1510 let surface = ApiSurface {
1511 crate_name: "c".into(),
1512 version: "1.0".into(),
1513 types: vec![make_type("Config", "c::Config", vec![make_field("x", TypeRef::String)])],
1514 functions: vec![],
1515 enums: vec![],
1516 errors: vec![],
1517 };
1518 let result = convertible_types(&surface);
1519 assert!(result.contains("Config"));
1520 }
1521
1522 #[test]
1523 fn test_convertible_types_excludes_type_with_unconvertible_named_field() {
1524 let field = make_field("inner", TypeRef::Named("Unknown".into()));
1526 let surface = ApiSurface {
1527 crate_name: "c".into(),
1528 version: "1.0".into(),
1529 types: vec![make_type("Wrapper", "c::Wrapper", vec![field])],
1530 functions: vec![],
1531 enums: vec![],
1532 errors: vec![],
1533 };
1534 let result = convertible_types(&surface);
1535 assert!(!result.contains("Wrapper"));
1536 }
1537
1538 #[test]
1539 fn test_core_to_binding_convertible_types_simple() {
1540 let surface = ApiSurface {
1541 crate_name: "c".into(),
1542 version: "1.0".into(),
1543 types: vec![make_type("Config", "c::Config", vec![make_field("x", TypeRef::String)])],
1544 functions: vec![],
1545 enums: vec![],
1546 errors: vec![],
1547 };
1548 let result = core_to_binding_convertible_types(&surface);
1549 assert!(result.contains("Config"));
1550 }
1551
1552 #[test]
1553 fn test_can_generate_conversion_true_when_in_set() {
1554 let mut set = AHashSet::new();
1555 set.insert("Config".to_string());
1556 let typ = make_type("Config", "c::Config", vec![]);
1557 assert!(can_generate_conversion(&typ, &set));
1558 }
1559
1560 #[test]
1561 fn test_can_generate_conversion_false_when_absent() {
1562 let set = AHashSet::new();
1563 let typ = make_type("Config", "c::Config", vec![]);
1564 assert!(!can_generate_conversion(&typ, &set));
1565 }
1566
1567 #[test]
1572 fn test_field_conversion_to_core_cfg_cast_u64_optional() {
1573 let config = ConversionConfig {
1574 cast_large_ints_to_i64: true,
1575 ..Default::default()
1576 };
1577 let result = field_conversion_to_core_cfg("n", &TypeRef::Primitive(PrimitiveType::U64), true, &config);
1578 assert_eq!(result, "n: val.n.map(|v| v as u64)");
1579 }
1580
1581 #[test]
1582 fn test_field_conversion_to_core_cfg_cast_usize_optional() {
1583 let config = ConversionConfig {
1584 cast_large_ints_to_i64: true,
1585 ..Default::default()
1586 };
1587 let result = field_conversion_to_core_cfg("n", &TypeRef::Primitive(PrimitiveType::Usize), true, &config);
1588 assert_eq!(result, "n: val.n.map(|v| v as usize)");
1589 }
1590
1591 #[test]
1592 fn test_field_conversion_to_core_cfg_cast_isize_optional() {
1593 let config = ConversionConfig {
1594 cast_large_ints_to_i64: true,
1595 ..Default::default()
1596 };
1597 let result = field_conversion_to_core_cfg("n", &TypeRef::Primitive(PrimitiveType::Isize), true, &config);
1598 assert_eq!(result, "n: val.n.map(|v| v as isize)");
1599 }
1600
1601 #[test]
1602 fn test_field_conversion_to_core_cfg_cast_f32_optional() {
1603 let config = ConversionConfig {
1604 cast_f32_to_f64: true,
1605 ..Default::default()
1606 };
1607 let result = field_conversion_to_core_cfg("s", &TypeRef::Primitive(PrimitiveType::F32), true, &config);
1608 assert_eq!(result, "s: val.s.map(|v| v as f32)");
1609 }
1610
1611 #[test]
1612 fn test_field_conversion_to_core_cfg_duration_cast_optional() {
1613 let config = ConversionConfig {
1614 cast_large_ints_to_i64: true,
1615 ..Default::default()
1616 };
1617 let result = field_conversion_to_core_cfg("t", &TypeRef::Duration, true, &config);
1618 assert_eq!(result, "t: val.t.map(|v| std::time::Duration::from_millis(v as u64))");
1619 }
1620
1621 #[test]
1622 fn test_field_conversion_to_core_cfg_json_to_string_optional() {
1623 let config = ConversionConfig {
1624 json_to_string: true,
1625 ..Default::default()
1626 };
1627 let result = field_conversion_to_core_cfg("m", &TypeRef::Json, true, &config);
1628 assert_eq!(result, "m: Default::default()");
1630 }
1631
1632 #[test]
1633 fn test_field_conversion_to_core_cfg_vec_named_to_string_optional() {
1634 let config = ConversionConfig {
1635 vec_named_to_string: true,
1636 ..Default::default()
1637 };
1638 let ty = TypeRef::Vec(Box::new(TypeRef::Named("Item".into())));
1639 let result = field_conversion_to_core_cfg("items", &ty, true, &config);
1640 assert_eq!(
1641 result,
1642 "items: val.items.as_ref().and_then(|s| serde_json::from_str(s).ok())"
1643 );
1644 }
1645
1646 #[test]
1647 fn test_field_conversion_to_core_cfg_vec_u64_cast_optional() {
1648 let config = ConversionConfig {
1649 cast_large_ints_to_i64: true,
1650 ..Default::default()
1651 };
1652 let ty = TypeRef::Vec(Box::new(TypeRef::Primitive(PrimitiveType::U64)));
1653 let result = field_conversion_to_core_cfg("ids", &ty, true, &config);
1654 assert!(result.contains("as u64"));
1655 }
1656
1657 #[test]
1658 fn test_field_conversion_to_core_cfg_vec_f32_cast_optional() {
1659 let config = ConversionConfig {
1660 cast_f32_to_f64: true,
1661 ..Default::default()
1662 };
1663 let ty = TypeRef::Vec(Box::new(TypeRef::Primitive(PrimitiveType::F32)));
1664 let result = field_conversion_to_core_cfg("scores", &ty, true, &config);
1665 assert!(result.contains("as f32"));
1666 }
1667
1668 #[test]
1669 fn test_field_conversion_to_core_cfg_map_u64_values_cast_optional() {
1670 let config = ConversionConfig {
1671 cast_large_ints_to_i64: true,
1672 ..Default::default()
1673 };
1674 let ty = TypeRef::Map(
1675 Box::new(TypeRef::String),
1676 Box::new(TypeRef::Primitive(PrimitiveType::U64)),
1677 );
1678 let result = field_conversion_to_core_cfg("map", &ty, true, &config);
1679 assert!(result.contains("as u64"));
1680 }
1681
1682 #[test]
1683 fn test_field_conversion_to_core_cfg_optional_inner_u64_cast() {
1684 let config = ConversionConfig {
1686 cast_large_ints_to_i64: true,
1687 ..Default::default()
1688 };
1689 let ty = TypeRef::Optional(Box::new(TypeRef::Primitive(PrimitiveType::U64)));
1690 let result = field_conversion_to_core_cfg("n", &ty, false, &config);
1691 assert!(result.contains("as u64"));
1692 }
1693
1694 #[test]
1699 fn test_field_conversion_from_core_cfg_cast_u64_optional() {
1700 let config = ConversionConfig {
1701 cast_large_ints_to_i64: true,
1702 ..Default::default()
1703 };
1704 let result = field_conversion_from_core_cfg(
1705 "n",
1706 &TypeRef::Primitive(PrimitiveType::U64),
1707 true,
1708 false,
1709 &no_opaques(),
1710 &config,
1711 );
1712 assert_eq!(result, "n: val.n.map(|v| v as i64)");
1713 }
1714
1715 #[test]
1716 fn test_field_conversion_from_core_cfg_cast_usize_optional() {
1717 let config = ConversionConfig {
1718 cast_large_ints_to_i64: true,
1719 ..Default::default()
1720 };
1721 let result = field_conversion_from_core_cfg(
1722 "n",
1723 &TypeRef::Primitive(PrimitiveType::Usize),
1724 true,
1725 false,
1726 &no_opaques(),
1727 &config,
1728 );
1729 assert_eq!(result, "n: val.n.map(|v| v as i64)");
1730 }
1731
1732 #[test]
1733 fn test_field_conversion_from_core_cfg_cast_isize_optional() {
1734 let config = ConversionConfig {
1735 cast_large_ints_to_i64: true,
1736 ..Default::default()
1737 };
1738 let result = field_conversion_from_core_cfg(
1739 "n",
1740 &TypeRef::Primitive(PrimitiveType::Isize),
1741 true,
1742 false,
1743 &no_opaques(),
1744 &config,
1745 );
1746 assert_eq!(result, "n: val.n.map(|v| v as i64)");
1747 }
1748
1749 #[test]
1750 fn test_field_conversion_from_core_cfg_cast_f32_optional() {
1751 let config = ConversionConfig {
1752 cast_f32_to_f64: true,
1753 ..Default::default()
1754 };
1755 let result = field_conversion_from_core_cfg(
1756 "s",
1757 &TypeRef::Primitive(PrimitiveType::F32),
1758 true,
1759 false,
1760 &no_opaques(),
1761 &config,
1762 );
1763 assert_eq!(result, "s: val.s.map(|v| v as f64)");
1764 }
1765
1766 #[test]
1767 fn test_field_conversion_from_core_cfg_duration_cast_optional() {
1768 let config = ConversionConfig {
1769 cast_large_ints_to_i64: true,
1770 ..Default::default()
1771 };
1772 let result = field_conversion_from_core_cfg("t", &TypeRef::Duration, true, false, &no_opaques(), &config);
1773 assert_eq!(result, "t: val.t.map(|d| d.as_millis() as u64 as i64)");
1774 }
1775
1776 #[test]
1777 fn test_field_conversion_from_core_cfg_json_to_string_optional() {
1778 let config = ConversionConfig {
1779 json_to_string: true,
1780 ..Default::default()
1781 };
1782 let result = field_conversion_from_core_cfg("m", &TypeRef::Json, true, false, &no_opaques(), &config);
1783 assert_eq!(result, "m: val.m.as_ref().map(ToString::to_string)");
1784 }
1785
1786 #[test]
1787 fn test_field_conversion_from_core_cfg_vec_named_to_string_optional() {
1788 let config = ConversionConfig {
1789 vec_named_to_string: true,
1790 ..Default::default()
1791 };
1792 let ty = TypeRef::Vec(Box::new(TypeRef::Named("Item".into())));
1793 let result = field_conversion_from_core_cfg("items", &ty, true, false, &no_opaques(), &config);
1794 assert_eq!(
1795 result,
1796 "items: val.items.as_ref().and_then(|v| serde_json::to_string(v).ok())"
1797 );
1798 }
1799
1800 #[test]
1801 fn test_field_conversion_from_core_cfg_vec_u64_cast_optional() {
1802 let config = ConversionConfig {
1803 cast_large_ints_to_i64: true,
1804 ..Default::default()
1805 };
1806 let ty = TypeRef::Vec(Box::new(TypeRef::Primitive(PrimitiveType::U64)));
1807 let result = field_conversion_from_core_cfg("ids", &ty, true, false, &no_opaques(), &config);
1808 assert!(result.contains("as i64"));
1809 }
1810
1811 #[test]
1812 fn test_field_conversion_from_core_cfg_vec_f32_cast_optional() {
1813 let config = ConversionConfig {
1814 cast_f32_to_f64: true,
1815 ..Default::default()
1816 };
1817 let ty = TypeRef::Vec(Box::new(TypeRef::Primitive(PrimitiveType::F32)));
1818 let result = field_conversion_from_core_cfg("scores", &ty, true, false, &no_opaques(), &config);
1819 assert!(result.contains("as f64"));
1820 }
1821
1822 #[test]
1823 fn test_field_conversion_from_core_cfg_optional_inner_u64_cast() {
1824 let config = ConversionConfig {
1826 cast_large_ints_to_i64: true,
1827 ..Default::default()
1828 };
1829 let ty = TypeRef::Optional(Box::new(TypeRef::Primitive(PrimitiveType::U64)));
1830 let result = field_conversion_from_core_cfg("n", &ty, false, false, &no_opaques(), &config);
1831 assert!(result.contains("as i64"));
1832 }
1833
1834 #[test]
1835 fn test_field_conversion_from_core_cfg_map_u64_values_cast_optional() {
1836 let config = ConversionConfig {
1837 cast_large_ints_to_i64: true,
1838 ..Default::default()
1839 };
1840 let ty = TypeRef::Map(
1841 Box::new(TypeRef::String),
1842 Box::new(TypeRef::Primitive(PrimitiveType::U64)),
1843 );
1844 let result = field_conversion_from_core_cfg("map", &ty, true, false, &no_opaques(), &config);
1845 assert!(result.contains("as i64"));
1846 }
1847
1848 #[test]
1854 fn test_field_conversion_to_core_optional_vec_string() {
1855 let ty = TypeRef::Optional(Box::new(TypeRef::Vec(Box::new(TypeRef::String))));
1857 let result = field_conversion_to_core("items", &ty, false);
1858 assert_eq!(result, "items: val.items");
1859 }
1860
1861 #[test]
1862 fn test_field_conversion_to_core_optional_vec_named_inner() {
1863 let ty = TypeRef::Optional(Box::new(TypeRef::Vec(Box::new(TypeRef::Named("Item".into())))));
1865 let result = field_conversion_to_core("items", &ty, false);
1866 assert_eq!(
1867 result,
1868 "items: val.items.map(|v| v.into_iter().map(Into::into).collect())"
1869 );
1870 }
1871
1872 #[test]
1873 fn test_field_conversion_to_core_map_string_optional_named() {
1874 let ty = TypeRef::Map(
1876 Box::new(TypeRef::String),
1877 Box::new(TypeRef::Optional(Box::new(TypeRef::Named("Val".into())))),
1878 );
1879 let result = field_conversion_to_core("map", &ty, false);
1882 assert_eq!(result, "map: val.map.into_iter().collect()");
1883 }
1884
1885 #[test]
1886 fn test_field_conversion_to_core_map_string_vec_named_value() {
1887 let ty = TypeRef::Map(
1889 Box::new(TypeRef::String),
1890 Box::new(TypeRef::Vec(Box::new(TypeRef::Named("Item".into())))),
1891 );
1892 let result = field_conversion_to_core("map", &ty, false);
1893 assert!(result.contains("v.into_iter().map(Into::into).collect()"));
1894 }
1895
1896 #[test]
1897 fn test_field_conversion_to_core_map_string_vec_json_value() {
1898 let ty = TypeRef::Map(
1900 Box::new(TypeRef::String),
1901 Box::new(TypeRef::Vec(Box::new(TypeRef::Json))),
1902 );
1903 let result = field_conversion_to_core("map", &ty, false);
1904 assert!(result.contains("filter_map(|s| serde_json::from_str(&s).ok()).collect()"));
1905 }
1906
1907 #[test]
1908 fn test_field_conversion_to_core_map_string_string_optional() {
1909 let ty = TypeRef::Map(Box::new(TypeRef::String), Box::new(TypeRef::String));
1911 let result = field_conversion_to_core("map", &ty, true);
1912 assert_eq!(result, "map: val.map.map(|m| m.into_iter().collect())");
1913 }
1914
1915 #[test]
1916 fn test_field_conversion_from_core_optional_vec_named() {
1917 let ty = TypeRef::Optional(Box::new(TypeRef::Vec(Box::new(TypeRef::Named("Item".into())))));
1919 let result = field_conversion_from_core("items", &ty, false, false, &no_opaques());
1920 assert!(result.contains("map(Into::into)") || result.contains("into_iter().map(Into::into)"));
1922 }
1923
1924 #[test]
1925 fn test_field_conversion_from_core_map_string_named_values() {
1926 let ty = TypeRef::Map(Box::new(TypeRef::String), Box::new(TypeRef::Named("Val".into())));
1928 let result = field_conversion_from_core("map", &ty, false, false, &no_opaques());
1929 assert!(result.contains("v.into()"));
1931 }
1932
1933 #[test]
1934 fn test_field_conversion_to_core_vec_string_passthrough() {
1935 let ty = TypeRef::Vec(Box::new(TypeRef::String));
1937 let result = field_conversion_to_core("tags", &ty, false);
1938 assert_eq!(result, "tags: val.tags");
1939 }
1940
1941 #[test]
1942 fn test_field_conversion_to_core_vec_string_optional() {
1943 let ty = TypeRef::Vec(Box::new(TypeRef::String));
1945 let result = field_conversion_to_core("tags", &ty, true);
1946 assert_eq!(result, "tags: val.tags");
1947 }
1948
1949 #[test]
1950 fn test_field_conversion_to_core_map_named_key() {
1951 let ty = TypeRef::Map(Box::new(TypeRef::Named("Key".into())), Box::new(TypeRef::String));
1953 let result = field_conversion_to_core("map", &ty, false);
1954 assert!(result.contains("k.into()"));
1955 }
1956
1957 #[test]
1958 fn test_field_conversion_to_core_map_json_key() {
1959 let ty = TypeRef::Map(Box::new(TypeRef::Json), Box::new(TypeRef::String));
1961 let result = field_conversion_to_core("map", &ty, false);
1962 assert!(result.contains("serde_json::from_str(&k)"));
1963 }
1964
1965 #[test]
1966 fn test_field_conversion_from_core_optional_json_inner() {
1967 let ty = TypeRef::Optional(Box::new(TypeRef::Json));
1969 let result = field_conversion_from_core("meta", &ty, false, false, &no_opaques());
1970 assert_eq!(result, "meta: val.meta.as_ref().map(ToString::to_string)");
1971 }
1972
1973 #[test]
1974 fn test_field_conversion_from_core_optional_path_inner() {
1975 let ty = TypeRef::Optional(Box::new(TypeRef::Path));
1977 let result = field_conversion_from_core("file", &ty, false, false, &no_opaques());
1978 assert_eq!(result, "file: val.file.map(|p| p.to_string_lossy().to_string())");
1979 }
1980
1981 #[test]
1982 fn test_field_conversion_from_core_map_json_keys() {
1983 let ty = TypeRef::Map(Box::new(TypeRef::Json), Box::new(TypeRef::String));
1985 let result = field_conversion_from_core("map", &ty, false, false, &no_opaques());
1986 assert!(result.contains("k.to_string()"));
1987 }
1988
1989 #[test]
1994 fn test_is_tuple_variant_single_positional_field() {
1995 let fields = vec![make_field("_0", TypeRef::String)];
1996 assert!(is_tuple_variant(&fields));
1997 }
1998
1999 #[test]
2000 fn test_is_tuple_variant_true_for_underscore_only() {
2001 let fields = vec![make_field("_", TypeRef::String)];
2003 assert!(is_tuple_variant(&fields));
2004 }
2005
2006 #[test]
2007 fn test_is_tuple_variant_false_for_field_starting_with_underscore_then_alpha() {
2008 let fields = vec![make_field("_foo", TypeRef::String)];
2010 assert!(!is_tuple_variant(&fields));
2011 }
2012
2013 #[test]
2014 fn test_is_tuple_variant_three_positional_fields() {
2015 let fields = vec![
2016 make_field("_0", TypeRef::String),
2017 make_field("_1", TypeRef::Primitive(PrimitiveType::I32)),
2018 make_field("_2", TypeRef::Primitive(PrimitiveType::F64)),
2019 ];
2020 assert!(is_tuple_variant(&fields));
2021 }
2022
2023 #[test]
2024 fn test_is_tuple_type_name_empty_string_is_false() {
2025 assert!(!helpers::is_tuple_type_name(""));
2026 }
2027
2028 #[test]
2029 fn test_is_tuple_type_name_space_is_false() {
2030 assert!(!helpers::is_tuple_type_name("String"));
2031 }
2032
2033 #[test]
2038 fn test_core_type_path_with_hyphen_and_double_colon() {
2039 let typ = make_type("Config", "my-crate::module::Config", vec![]);
2041 assert_eq!(core_type_path(&typ, "my_crate"), "my_crate::module::Config");
2042 }
2043
2044 #[test]
2045 fn test_core_type_path_rust_path_matches_core_import_prefix() {
2046 let typ = make_type("Config", "my_crate::Config", vec![]);
2048 assert_eq!(core_type_path(&typ, "my_crate"), "my_crate::Config");
2049 }
2050
2051 #[test]
2056 fn test_build_type_path_map_multiple_types() {
2057 let surface = ApiSurface {
2058 crate_name: "my_crate".into(),
2059 version: "1.0.0".into(),
2060 types: vec![
2061 make_type("Config", "my_crate::Config", vec![]),
2062 make_type("Result", "my_crate::types::Result", vec![]),
2063 ],
2064 functions: vec![],
2065 enums: vec![
2066 make_enum("Mode", "my_crate::Mode", &["A"]),
2067 make_enum("Status", "Status", &["Ok"]),
2068 ],
2069 errors: vec![],
2070 };
2071 let map = build_type_path_map(&surface, "my_crate");
2072 assert_eq!(map.get("Config").map(String::as_str), Some("my_crate::Config"));
2073 assert_eq!(map.get("Result").map(String::as_str), Some("my_crate::types::Result"));
2074 assert_eq!(map.get("Mode").map(String::as_str), Some("my_crate::Mode"));
2075 assert_eq!(map.get("Status").map(String::as_str), Some("my_crate::Status"));
2077 }
2078
2079 #[test]
2080 fn test_build_type_path_map_normalizes_hyphens() {
2081 let surface = ApiSurface {
2082 crate_name: "my_crate".into(),
2083 version: "1.0.0".into(),
2084 types: vec![make_type("Config", "my-crate::Config", vec![])],
2085 functions: vec![],
2086 enums: vec![],
2087 errors: vec![],
2088 };
2089 let map = build_type_path_map(&surface, "my_crate");
2090 assert_eq!(map.get("Config").map(String::as_str), Some("my_crate::Config"));
2091 }
2092
2093 #[test]
2094 fn test_build_type_path_map_empty_surface() {
2095 let surface = ApiSurface {
2096 crate_name: "c".into(),
2097 version: "1.0".into(),
2098 types: vec![],
2099 functions: vec![],
2100 enums: vec![],
2101 errors: vec![],
2102 };
2103 let map = build_type_path_map(&surface, "c");
2104 assert!(map.is_empty());
2105 }
2106
2107 #[test]
2112 fn test_gen_from_binding_to_core_empty_fields() {
2113 let typ = make_type("Empty", "c::Empty", vec![]);
2114 let result = gen_from_binding_to_core(&typ, "c");
2115 assert!(result.contains("impl From<Empty> for c::Empty"));
2116 assert!(result.contains("Self {"));
2117 }
2118
2119 #[test]
2120 fn test_gen_from_core_to_binding_empty_fields() {
2121 let typ = make_type("Empty", "c::Empty", vec![]);
2122 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
2123 assert!(result.contains("impl From<c::Empty> for Empty"));
2124 assert!(result.contains("Self {"));
2125 }
2126
2127 #[test]
2128 fn test_gen_from_binding_to_core_all_optional_fields() {
2129 let typ = make_type(
2130 "Config",
2131 "c::Config",
2132 vec![
2133 make_opt_field("name", TypeRef::String),
2134 make_opt_field("count", TypeRef::Primitive(PrimitiveType::I32)),
2135 ],
2136 );
2137 let result = gen_from_binding_to_core(&typ, "c");
2138 assert!(result.contains("name: val.name"));
2139 assert!(result.contains("count: val.count"));
2140 }
2141
2142 #[test]
2143 fn test_gen_from_binding_to_core_single_string_field() {
2144 let typ = make_type("S", "c::S", vec![make_field("value", TypeRef::String)]);
2145 let result = gen_from_binding_to_core(&typ, "c");
2146 assert!(result.contains("value: val.value"));
2147 }
2148
2149 #[test]
2150 fn test_gen_from_core_to_binding_single_optional_named_field() {
2151 let field = make_opt_field("inner", TypeRef::Named("Inner".into()));
2152 let typ = make_type("Wrapper", "c::Wrapper", vec![field]);
2153 let result = gen_from_core_to_binding(&typ, "c", &no_opaques());
2154 assert!(result.contains("inner: val.inner.map(Into::into)"));
2155 }
2156
2157 #[test]
2162 fn test_binding_to_core_match_arm_ext_cfg_unit_variant() {
2163 let config = ConversionConfig::default();
2164 let result = helpers::binding_to_core_match_arm_ext_cfg("MyEnum", "Foo", &[], false, &config);
2165 assert_eq!(result, "MyEnum::Foo => Self::Foo,");
2166 }
2167
2168 #[test]
2169 fn test_binding_to_core_match_arm_ext_cfg_no_binding_data_named_fields() {
2170 let config = ConversionConfig::default();
2171 let fields = vec![make_field("value", TypeRef::String)];
2172 let result = helpers::binding_to_core_match_arm_ext_cfg("MyEnum", "Bar", &fields, false, &config);
2173 assert!(result.contains("value: Default::default()"));
2174 }
2175
2176 #[test]
2177 fn test_binding_to_core_match_arm_ext_cfg_no_binding_data_tuple_fields() {
2178 let config = ConversionConfig::default();
2179 let fields = vec![make_field("_0", TypeRef::String)];
2180 let result = helpers::binding_to_core_match_arm_ext_cfg("MyEnum", "Bar", &fields, false, &config);
2181 assert!(result.contains("Default::default()"));
2182 assert!(result.contains("Self::Bar("));
2183 }
2184
2185 #[test]
2186 fn test_binding_to_core_match_arm_ext_cfg_with_binding_data_named() {
2187 let config = ConversionConfig::default();
2188 let fields = vec![make_field("value", TypeRef::Named("Inner".into()))];
2189 let result = helpers::binding_to_core_match_arm_ext_cfg("MyEnum", "Bar", &fields, true, &config);
2190 assert!(result.contains("value: value.into()"));
2191 }
2192
2193 #[test]
2194 fn test_binding_to_core_match_arm_ext_cfg_with_binding_data_tuple() {
2195 let config = ConversionConfig::default();
2196 let fields = vec![make_field("_0", TypeRef::Named("Inner".into()))];
2197 let result = helpers::binding_to_core_match_arm_ext_cfg("MyEnum", "Bar", &fields, true, &config);
2198 assert!(result.contains("_0.into()"));
2199 }
2200
2201 #[test]
2202 fn test_binding_to_core_match_arm_ext_cfg_cast_u64_field() {
2203 let config = ConversionConfig {
2204 cast_large_ints_to_i64: true,
2205 ..Default::default()
2206 };
2207 let fields = vec![make_field("count", TypeRef::Primitive(PrimitiveType::U64))];
2208 let result = helpers::binding_to_core_match_arm_ext_cfg("MyEnum", "Bar", &fields, true, &config);
2209 assert!(result.contains("as u64"));
2210 }
2211
2212 #[test]
2217 fn test_core_to_binding_match_arm_ext_cfg_unit_variant() {
2218 let config = ConversionConfig::default();
2219 let result = helpers::core_to_binding_match_arm_ext_cfg("CoreEnum", "Foo", &[], false, &config);
2220 assert_eq!(result, "CoreEnum::Foo => Self::Foo,");
2221 }
2222
2223 #[test]
2224 fn test_core_to_binding_match_arm_ext_cfg_no_binding_data_named() {
2225 let config = ConversionConfig::default();
2226 let fields = vec![make_field("x", TypeRef::Primitive(PrimitiveType::I32))];
2227 let result = helpers::core_to_binding_match_arm_ext_cfg("CoreEnum", "Foo", &fields, false, &config);
2228 assert!(result.contains("{ .. }"));
2229 assert!(result.contains("Self::Foo"));
2230 }
2231
2232 #[test]
2233 fn test_core_to_binding_match_arm_ext_cfg_no_binding_data_tuple() {
2234 let config = ConversionConfig::default();
2235 let fields = vec![make_field("_0", TypeRef::Primitive(PrimitiveType::I32))];
2236 let result = helpers::core_to_binding_match_arm_ext_cfg("CoreEnum", "Foo", &fields, false, &config);
2237 assert!(result.contains("(..)"));
2238 assert!(result.contains("Self::Foo"));
2239 }
2240
2241 #[test]
2242 fn test_core_to_binding_match_arm_ext_cfg_with_binding_data_named_fields() {
2243 let config = ConversionConfig::default();
2244 let fields = vec![make_field("value", TypeRef::Named("Inner".into()))];
2245 let result = helpers::core_to_binding_match_arm_ext_cfg("CoreEnum", "Foo", &fields, true, &config);
2246 assert!(result.contains("value: value.into()"));
2247 }
2248
2249 #[test]
2250 fn test_core_to_binding_match_arm_ext_cfg_with_binding_data_tuple() {
2251 let config = ConversionConfig::default();
2252 let fields = vec![make_field("_0", TypeRef::Named("Inner".into()))];
2253 let result = helpers::core_to_binding_match_arm_ext_cfg("CoreEnum", "Foo", &fields, true, &config);
2254 assert!(result.contains("_0: _0.into()"));
2255 }
2256
2257 #[test]
2258 fn test_core_to_binding_match_arm_ext_cfg_cast_u64_field() {
2259 let config = ConversionConfig {
2260 cast_large_ints_to_i64: true,
2261 ..Default::default()
2262 };
2263 let fields = vec![make_field("count", TypeRef::Primitive(PrimitiveType::U64))];
2264 let result = helpers::core_to_binding_match_arm_ext_cfg("CoreEnum", "Bar", &fields, true, &config);
2265 assert!(result.contains("as i64"));
2266 }
2267
2268 #[test]
2273 fn test_core_to_binding_match_arm_ext_binding_has_data_named_fields() {
2274 let fields = vec![make_field("value", TypeRef::Named("Inner".into()))];
2275 let result = helpers::core_to_binding_match_arm_ext("CoreEnum", "Bar", &fields, true);
2276 assert!(result.contains("value: value.into()"));
2277 }
2278
2279 #[test]
2280 fn test_core_to_binding_match_arm_ext_binding_has_data_tuple_fields() {
2281 let fields = vec![make_field("_0", TypeRef::Named("Inner".into()))];
2282 let result = helpers::core_to_binding_match_arm_ext("CoreEnum", "Bar", &fields, true);
2283 assert!(result.contains("_0: _0.into()"));
2284 }
2285
2286 #[test]
2287 fn test_core_to_binding_match_arm_ext_binding_has_data_plain_field() {
2288 let fields = vec![make_field("x", TypeRef::Primitive(PrimitiveType::I32))];
2289 let result = helpers::core_to_binding_match_arm_ext("CoreEnum", "Bar", &fields, true);
2290 assert!(result.contains("x: x"));
2291 }
2292
2293 #[test]
2294 fn test_core_to_binding_match_arm_ext_binding_has_data_sanitized_field() {
2295 let mut field = make_field("complex", TypeRef::String);
2296 field.sanitized = true;
2297 let result = helpers::core_to_binding_match_arm_ext("CoreEnum", "Bar", &[field], true);
2298 assert!(result.contains("serde_json::to_string("));
2299 }
2300
2301 #[test]
2306 fn test_input_type_names_from_method_params() {
2307 let surface = ApiSurface {
2308 crate_name: "my_crate".into(),
2309 version: "1.0.0".into(),
2310 types: vec![TypeDef {
2311 name: "Client".into(),
2312 rust_path: "my_crate::Client".into(),
2313 original_rust_path: String::new(),
2314 fields: vec![],
2315 methods: vec![MethodDef {
2316 name: "process".into(),
2317 params: vec![ParamDef {
2318 name: "config".into(),
2319 ty: TypeRef::Named("Config".into()),
2320 optional: false,
2321 default: None,
2322 sanitized: false,
2323 typed_default: None,
2324 is_ref: false,
2325 is_mut: false,
2326 newtype_wrapper: None,
2327 }],
2328 return_type: TypeRef::Unit,
2329 is_async: false,
2330 is_static: false,
2331 error_type: None,
2332 doc: String::new(),
2333 receiver: None,
2334 sanitized: false,
2335 trait_source: None,
2336 returns_ref: false,
2337 returns_cow: false,
2338 return_newtype_wrapper: None,
2339 has_default_impl: false,
2340 }],
2341 is_opaque: false,
2342 is_clone: true,
2343 is_trait: false,
2344 has_default: false,
2345 has_stripped_cfg_fields: false,
2346 is_return_type: false,
2347 serde_rename_all: None,
2348 has_serde: false,
2349 super_traits: vec![],
2350 doc: String::new(),
2351 cfg: None,
2352 }],
2353 functions: vec![],
2354 enums: vec![],
2355 errors: vec![],
2356 };
2357 let names = input_type_names(&surface);
2358 assert!(names.contains("Config"));
2359 }
2360
2361 #[test]
2362 fn test_input_type_names_transitive_closure() {
2363 let config_type = make_type(
2365 "Config",
2366 "c::Config",
2367 vec![make_field("backend", TypeRef::Named("Backend".into()))],
2368 );
2369 let surface = ApiSurface {
2370 crate_name: "c".into(),
2371 version: "1.0".into(),
2372 types: vec![config_type],
2373 functions: vec![FunctionDef {
2374 name: "run".into(),
2375 rust_path: "c::run".into(),
2376 original_rust_path: String::new(),
2377 params: vec![ParamDef {
2378 name: "config".into(),
2379 ty: TypeRef::Named("Config".into()),
2380 optional: false,
2381 default: None,
2382 sanitized: false,
2383 typed_default: None,
2384 is_ref: false,
2385 is_mut: false,
2386 newtype_wrapper: None,
2387 }],
2388 return_type: TypeRef::Unit,
2389 is_async: false,
2390 error_type: None,
2391 doc: String::new(),
2392 cfg: None,
2393 sanitized: false,
2394 returns_ref: false,
2395 returns_cow: false,
2396 return_newtype_wrapper: None,
2397 }],
2398 enums: vec![],
2399 errors: vec![],
2400 };
2401 let names = input_type_names(&surface);
2402 assert!(names.contains("Config"));
2403 assert!(names.contains("Backend"));
2404 }
2405
2406 #[test]
2411 fn test_convertible_types_sanitized_field_with_has_default() {
2412 let mut field = make_field("complex", TypeRef::String);
2413 field.sanitized = true;
2414 let mut typ = make_type("Config", "c::Config", vec![field]);
2415 typ.has_default = true;
2416 let surface = ApiSurface {
2417 crate_name: "c".into(),
2418 version: "1.0".into(),
2419 types: vec![typ],
2420 functions: vec![],
2421 enums: vec![],
2422 errors: vec![],
2423 };
2424 let result = convertible_types(&surface);
2426 assert!(result.contains("Config"));
2427 }
2428
2429 #[test]
2430 fn test_convertible_types_opaque_type_excluded() {
2431 let mut typ = make_type("Client", "c::Client", vec![]);
2432 typ.is_opaque = true;
2433 let surface = ApiSurface {
2434 crate_name: "c".into(),
2435 version: "1.0".into(),
2436 types: vec![typ],
2437 functions: vec![],
2438 enums: vec![],
2439 errors: vec![],
2440 };
2441 let result = convertible_types(&surface);
2443 assert!(!result.contains("Client"));
2444 }
2445
2446 #[test]
2447 fn test_convertible_types_type_with_named_field_in_surface() {
2448 let config_field = make_field("backend", TypeRef::Named("Backend".into()));
2450 let config = make_type("Config", "c::Config", vec![config_field]);
2451 let backend = make_type("Backend", "c::Backend", vec![]);
2452 let surface = ApiSurface {
2453 crate_name: "c".into(),
2454 version: "1.0".into(),
2455 types: vec![config, backend],
2456 functions: vec![],
2457 enums: vec![],
2458 errors: vec![],
2459 };
2460 let result = convertible_types(&surface);
2461 assert!(result.contains("Config"));
2462 assert!(result.contains("Backend"));
2463 }
2464
2465 #[test]
2470 fn test_core_enum_path_with_hyphen_normalization() {
2471 let e = make_enum("Status", "my-crate::Status", &[]);
2472 assert_eq!(core_enum_path(&e, "my_crate"), "my_crate::Status");
2473 }
2474
2475 #[test]
2476 fn test_core_enum_path_already_starts_with_core_import() {
2477 let e = make_enum("Mode", "my_crate::inner::Mode", &[]);
2479 assert_eq!(core_enum_path(&e, "my_crate"), "my_crate::inner::Mode");
2480 }
2481
2482 #[test]
2487 fn test_needs_i64_cast_true_for_large_ints() {
2488 use super::helpers::*;
2489 assert!(needs_i64_cast(&PrimitiveType::U64));
2490 assert!(needs_i64_cast(&PrimitiveType::Usize));
2491 assert!(needs_i64_cast(&PrimitiveType::Isize));
2492 }
2493
2494 #[test]
2495 fn test_needs_i64_cast_false_for_small_ints() {
2496 use super::helpers::*;
2497 assert!(!needs_i64_cast(&PrimitiveType::I32));
2498 assert!(!needs_i64_cast(&PrimitiveType::U32));
2499 assert!(!needs_i64_cast(&PrimitiveType::F64));
2500 }
2501
2502 #[test]
2503 fn test_core_prim_str_all_variants() {
2504 use super::helpers::core_prim_str;
2505 assert_eq!(core_prim_str(&PrimitiveType::U64), "u64");
2506 assert_eq!(core_prim_str(&PrimitiveType::Usize), "usize");
2507 assert_eq!(core_prim_str(&PrimitiveType::Isize), "isize");
2508 assert_eq!(core_prim_str(&PrimitiveType::F32), "f32");
2509 assert_eq!(core_prim_str(&PrimitiveType::Bool), "bool");
2510 assert_eq!(core_prim_str(&PrimitiveType::U8), "u8");
2511 assert_eq!(core_prim_str(&PrimitiveType::U16), "u16");
2512 assert_eq!(core_prim_str(&PrimitiveType::U32), "u32");
2513 assert_eq!(core_prim_str(&PrimitiveType::I8), "i8");
2514 assert_eq!(core_prim_str(&PrimitiveType::I16), "i16");
2515 assert_eq!(core_prim_str(&PrimitiveType::I32), "i32");
2516 assert_eq!(core_prim_str(&PrimitiveType::I64), "i64");
2517 assert_eq!(core_prim_str(&PrimitiveType::F64), "f64");
2518 }
2519
2520 #[test]
2521 fn test_binding_prim_str_large_ints_map_to_i64() {
2522 use super::helpers::binding_prim_str;
2523 assert_eq!(binding_prim_str(&PrimitiveType::U64), "i64");
2524 assert_eq!(binding_prim_str(&PrimitiveType::Usize), "i64");
2525 assert_eq!(binding_prim_str(&PrimitiveType::Isize), "i64");
2526 }
2527
2528 #[test]
2529 fn test_binding_prim_str_small_ints_map_to_i32() {
2530 use super::helpers::binding_prim_str;
2531 assert_eq!(binding_prim_str(&PrimitiveType::U8), "i32");
2532 assert_eq!(binding_prim_str(&PrimitiveType::U16), "i32");
2533 assert_eq!(binding_prim_str(&PrimitiveType::U32), "i32");
2534 assert_eq!(binding_prim_str(&PrimitiveType::I8), "i32");
2535 assert_eq!(binding_prim_str(&PrimitiveType::I16), "i32");
2536 assert_eq!(binding_prim_str(&PrimitiveType::I32), "i32");
2537 }
2538
2539 #[test]
2540 fn test_binding_prim_str_float_and_i64() {
2541 use super::helpers::binding_prim_str;
2542 assert_eq!(binding_prim_str(&PrimitiveType::F32), "f64");
2543 assert_eq!(binding_prim_str(&PrimitiveType::F64), "f64");
2544 assert_eq!(binding_prim_str(&PrimitiveType::I64), "i64");
2545 assert_eq!(binding_prim_str(&PrimitiveType::Bool), "bool");
2546 }
2547}