1#![deny(unsafe_code)]
2
3#[allow(unsafe_code)]
4pub mod raw;
5
6pub mod decode;
7pub mod deserialize;
8pub mod encode;
9pub mod error;
10pub mod plan;
11pub mod scatter;
12pub mod serialize;
13
14pub use deserialize::{
15 deserialize_into, from_slice, from_slice_borrowed, from_slice_borrowed_with_plan,
16 from_slice_with_plan,
17};
18pub use error::{DeserializeError, SerializeError, TranslationError, TranslationErrorKind};
19pub use plan::{FieldOp, PlanInput, SchemaSet, TranslationPlan, build_identity_plan, build_plan};
20pub use raw::opaque_encoded_borrowed;
21pub use scatter::{ScatterPlan, Segment, peek_to_scatter_plan};
22pub use serialize::to_vec;
23
24#[cfg(test)]
25mod tests {
26 use super::*;
27 use facet::Facet;
28
29 fn round_trip<T>(value: &T)
33 where
34 for<'de> T: Facet<'de> + std::fmt::Debug + PartialEq,
35 {
36 let direct_bytes = to_vec(value).unwrap();
38
39 let peek = facet_reflect::Peek::new(value);
41 let plan = peek_to_scatter_plan(peek).unwrap();
42 let mut scatter_bytes = vec![0u8; plan.total_size()];
43 plan.write_into(&mut scatter_bytes);
44
45 assert_eq!(
47 direct_bytes, scatter_bytes,
48 "to_vec and scatter plan produced different bytes for {:?}",
49 value
50 );
51
52 let io_slices = plan.to_io_slices();
54 let mut io_bytes = Vec::new();
55 for slice in &io_slices {
56 io_bytes.extend_from_slice(slice);
57 }
58 assert_eq!(
59 direct_bytes, io_bytes,
60 "to_io_slices produced different bytes for {:?}",
61 value
62 );
63
64 let _ = plan.staging();
66
67 let result: T = from_slice_borrowed(&direct_bytes).unwrap();
69 assert_eq!(&result, value, "round-trip mismatch for {:?}", value);
70 }
71
72 #[test]
73 fn round_trip_u32() {
74 round_trip(&42u32);
75 }
76
77 #[test]
78 fn round_trip_bool() {
79 round_trip(&true);
80 round_trip(&false);
81 }
82
83 #[test]
84 fn round_trip_string() {
85 round_trip(&"hello world".to_string());
86 }
87
88 #[test]
89 fn round_trip_empty_string() {
90 round_trip(&String::new());
91 }
92
93 #[test]
94 fn round_trip_f64() {
95 round_trip(&std::f64::consts::PI);
96 }
97
98 #[test]
99 fn round_trip_negative_i32() {
100 round_trip(&-12345i32);
101 }
102
103 #[test]
104 fn round_trip_struct() {
105 #[derive(Facet, Debug, PartialEq)]
106 struct Point {
107 x: f64,
108 y: f64,
109 }
110
111 round_trip(&Point { x: 1.5, y: -2.5 });
112 }
113
114 #[test]
115 fn round_trip_enum() {
116 #[derive(Facet, Debug, PartialEq)]
117 #[repr(u8)]
118 enum Color {
119 Red,
120 Green,
121 Blue,
122 }
123
124 round_trip(&Color::Red);
125 round_trip(&Color::Green);
126 round_trip(&Color::Blue);
127 }
128
129 #[test]
130 fn round_trip_enum_with_payload() {
131 #[derive(Facet, Debug, PartialEq)]
132 #[repr(u8)]
133 enum Shape {
134 Circle(f64),
135 Rect { w: f64, h: f64 },
136 Empty,
137 }
138
139 round_trip(&Shape::Circle(std::f64::consts::PI));
140 round_trip(&Shape::Rect { w: 10.0, h: 20.0 });
141 round_trip(&Shape::Empty);
142 }
143
144 #[test]
145 fn round_trip_vec() {
146 round_trip(&vec![1u32, 2, 3, 100, 0]);
147 }
148
149 #[test]
150 fn round_trip_vec_u8() {
151 round_trip(&vec![0xFFu8, 0x00, 0x42, 0xAB]);
152 }
153
154 #[test]
155 fn round_trip_option() {
156 round_trip(&Some(42u32));
157 round_trip(&None::<u32>);
158 }
159
160 #[test]
161 fn round_trip_nested() {
162 #[derive(Facet, Debug, PartialEq)]
163 struct Inner {
164 value: u32,
165 }
166
167 #[derive(Facet, Debug, PartialEq)]
168 struct Outer {
169 name: String,
170 inner: Inner,
171 tags: Vec<String>,
172 }
173
174 round_trip(&Outer {
175 name: "test".to_string(),
176 inner: Inner { value: 99 },
177 tags: vec!["a".into(), "bb".into()],
178 });
179 }
180
181 #[test]
182 fn round_trip_tuple() {
183 round_trip(&(42u32, "hello".to_string(), true));
184 }
185
186 #[test]
187 fn cross_compat_with_facet_postcard() {
188 let val: u32 = 300;
190 let ours = to_vec(&val).unwrap();
191 let theirs = facet_postcard::to_vec(&val).unwrap();
192 assert_eq!(ours, theirs, "u32 encoding mismatch");
193
194 let val = "hello".to_string();
195 let ours = to_vec(&val).unwrap();
196 let theirs = facet_postcard::to_vec(&val).unwrap();
197 assert_eq!(ours, theirs, "String encoding mismatch");
198
199 let val: i32 = -42;
200 let ours = to_vec(&val).unwrap();
201 let theirs = facet_postcard::to_vec(&val).unwrap();
202 assert_eq!(ours, theirs, "i32 encoding mismatch");
203
204 let val = true;
205 let ours = to_vec(&val).unwrap();
206 let theirs = facet_postcard::to_vec(&val).unwrap();
207 assert_eq!(ours, theirs, "bool encoding mismatch");
208
209 let val: f64 = std::f64::consts::E;
210 let ours = to_vec(&val).unwrap();
211 let theirs = facet_postcard::to_vec(&val).unwrap();
212 assert_eq!(ours, theirs, "f64 encoding mismatch");
213
214 let val: Vec<u32> = vec![1, 2, 3];
215 let ours = to_vec(&val).unwrap();
216 let theirs = facet_postcard::to_vec(&val).unwrap();
217 assert_eq!(ours, theirs, "Vec<u32> encoding mismatch");
218
219 let val: Option<u32> = Some(42);
220 let ours = to_vec(&val).unwrap();
221 let theirs = facet_postcard::to_vec(&val).unwrap();
222 assert_eq!(ours, theirs, "Option<u32> Some encoding mismatch");
223
224 let val: Option<u32> = None;
225 let ours = to_vec(&val).unwrap();
226 let theirs = facet_postcard::to_vec(&val).unwrap();
227 assert_eq!(ours, theirs, "Option<u32> None encoding mismatch");
228 }
229
230 #[test]
231 fn cross_compat_struct() {
232 #[derive(Facet, Debug, PartialEq)]
233 struct Point {
234 x: f64,
235 y: f64,
236 }
237
238 let val = Point { x: 1.0, y: 2.0 };
239 let ours = to_vec(&val).unwrap();
240 let theirs = facet_postcard::to_vec(&val).unwrap();
241 assert_eq!(ours, theirs, "Point struct encoding mismatch");
242
243 let result: Point = from_slice(&theirs).unwrap();
245 assert_eq!(result, val);
246 }
247
248 #[test]
249 fn cross_compat_enum() {
250 #[derive(Facet, Debug, PartialEq)]
251 #[repr(u8)]
252 enum Color {
253 Red,
254 Green,
255 Blue,
256 }
257
258 for color in [Color::Red, Color::Green, Color::Blue] {
259 let ours = to_vec(&color).unwrap();
260 let theirs = facet_postcard::to_vec(&color).unwrap();
261 assert_eq!(ours, theirs, "Color enum encoding mismatch");
262 }
263 }
264
265 #[test]
266 fn scatter_plan_matches_to_vec() {
267 #[derive(Facet, Debug, PartialEq)]
268 struct Msg {
269 id: u32,
270 name: String,
271 tags: Vec<String>,
272 }
273
274 let val = Msg {
275 id: 42,
276 name: "hello".to_string(),
277 tags: vec!["a".into(), "bb".into()],
278 };
279
280 let direct = to_vec(&val).unwrap();
281
282 let peek = facet_reflect::Peek::new(&val);
283 let plan = peek_to_scatter_plan(peek).unwrap();
284 assert_eq!(plan.total_size(), direct.len());
285
286 let mut scattered = vec![0u8; plan.total_size()];
287 plan.write_into(&mut scattered);
288 assert_eq!(scattered, direct);
289 }
290
291 #[test]
292 fn scatter_plan_small_blobs_get_staged() {
293 #[derive(Facet)]
294 struct Borrowed<'a> {
295 id: u32,
296 name: &'a str,
297 data: &'a [u8],
298 }
299
300 let val = Borrowed {
301 id: 42,
302 name: "hello",
303 data: &[1, 2, 3, 4, 5],
304 };
305
306 let peek = facet_reflect::Peek::new(&val);
307 let plan = peek_to_scatter_plan(peek).unwrap();
308
309 let direct = to_vec(&val).unwrap();
311 let mut scattered = vec![0u8; plan.total_size()];
312 plan.write_into(&mut scattered);
313 assert_eq!(scattered, direct);
314
315 let segments = plan.segments();
317 let ref_count = segments
318 .iter()
319 .filter(|s| matches!(s, Segment::Reference { .. }))
320 .count();
321 assert_eq!(
322 ref_count, 0,
323 "small borrowed fields should be staged, not referenced"
324 );
325 assert_eq!(
327 segments.len(),
328 1,
329 "all small data should coalesce into 1 segment"
330 );
331 }
332
333 #[test]
334 fn scatter_plan_large_blobs_get_referenced() {
335 #[derive(Facet)]
336 struct BigPayload<'a> {
337 id: u32,
338 data: &'a [u8],
339 }
340
341 let big_blob = vec![0xABu8; 8192];
342 let val = BigPayload {
343 id: 1,
344 data: &big_blob,
345 };
346
347 let peek = facet_reflect::Peek::new(&val);
348 let plan = peek_to_scatter_plan(peek).unwrap();
349
350 let direct = to_vec(&val).unwrap();
352 let mut scattered = vec![0u8; plan.total_size()];
353 plan.write_into(&mut scattered);
354 assert_eq!(scattered, direct);
355
356 let segments = plan.segments();
358 let ref_count = segments
359 .iter()
360 .filter(|s| matches!(s, Segment::Reference { .. }))
361 .count();
362 assert_eq!(ref_count, 1, "large blob should be a Reference segment");
363 assert_eq!(segments.len(), 2);
365 }
366
367 #[test]
368 fn scatter_staged_segments_coalesce() {
369 #[derive(Facet)]
372 struct AllScalar {
373 a: u32,
374 b: u32,
375 c: bool,
376 d: u64,
377 }
378
379 let val = AllScalar {
380 a: 1,
381 b: 2,
382 c: true,
383 d: 999,
384 };
385
386 let peek = facet_reflect::Peek::new(&val);
387 let plan = peek_to_scatter_plan(peek).unwrap();
388
389 let segments = plan.segments();
390 assert_eq!(
391 segments.len(),
392 1,
393 "all-scalar struct should produce exactly 1 coalesced staged segment, got {segments:?}"
394 );
395 assert!(
396 matches!(segments[0], Segment::Staged { .. }),
397 "single segment should be Staged"
398 );
399
400 let direct = to_vec(&val).unwrap();
402 let mut scattered = vec![0u8; plan.total_size()];
403 plan.write_into(&mut scattered);
404 assert_eq!(scattered, direct);
405 }
406
407 #[test]
408 fn round_trip_borrowed_identity() {
409 #[derive(Facet, Debug, PartialEq)]
410 struct Msg {
411 id: u32,
412 name: String,
413 }
414
415 let val = Msg {
416 id: 42,
417 name: "hello".to_string(),
418 };
419 let bytes = to_vec(&val).unwrap();
420 let result: Msg = from_slice_borrowed(&bytes).unwrap();
421 assert_eq!(result, val);
422 }
423
424 #[derive(Debug)]
427 struct PlanResult {
428 plan: TranslationPlan,
429 remote: SchemaSet,
430 #[allow(dead_code)]
431 local: SchemaSet,
432 }
433
434 fn plan_for(
436 remote_shape: &'static facet_core::Shape,
437 local_shape: &'static facet_core::Shape,
438 ) -> Result<PlanResult, error::TranslationError> {
439 let remote_extracted = vox_types::extract_schemas(remote_shape).expect("schema extraction");
440 let remote =
441 SchemaSet::from_root_and_schemas(remote_extracted.root, remote_extracted.schemas);
442 let local_extracted = vox_types::extract_schemas(local_shape).expect("schema extraction");
443 let local = SchemaSet::from_root_and_schemas(local_extracted.root, local_extracted.schemas);
444 let plan = build_plan(&PlanInput {
445 remote: &remote,
446 local: &local,
447 })?;
448 Ok(PlanResult {
449 plan,
450 remote,
451 local,
452 })
453 }
454
455 #[test]
456 fn translation_bytes_and_list_u8_are_compatible() {
457 let remote = SchemaSet::from_schemas(vec![
458 vox_types::Schema {
459 id: vox_types::SchemaHash(1),
460 type_params: vec![],
461 kind: vox_types::SchemaKind::Primitive {
462 primitive_type: vox_types::PrimitiveType::U8,
463 },
464 },
465 vox_types::Schema {
466 id: vox_types::SchemaHash(2),
467 type_params: vec![],
468 kind: vox_types::SchemaKind::List {
469 element: vox_types::TypeRef::concrete(vox_types::SchemaHash(1)),
470 },
471 },
472 ]);
473 let local_extracted =
474 vox_types::extract_schemas(<Vec<u8> as Facet>::SHAPE).expect("schema extraction");
475 let local = SchemaSet::from_root_and_schemas(local_extracted.root, local_extracted.schemas);
476 let plan = build_plan(&PlanInput {
477 remote: &remote,
478 local: &local,
479 })
480 .expect("list<u8> and bytes should be translation-compatible");
481 assert!(matches!(plan, TranslationPlan::Identity));
482
483 let bytes = to_vec(&vec![1u8, 2, 3, 4]).expect("serialize remote bytes");
484 let result: Vec<u8> =
485 from_slice_with_plan(&bytes, &plan, &remote.registry).expect("translate byte buffer");
486 assert_eq!(result, vec![1, 2, 3, 4]);
487 }
488
489 #[test]
490 fn translation_never_and_unit_are_incompatible() {
491 let error = plan_for(
492 <std::convert::Infallible as Facet>::SHAPE,
493 <() as Facet>::SHAPE,
494 )
495 .expect_err("never and unit should not be translation-compatible");
496
497 assert!(matches!(
498 *error.kind,
499 error::TranslationErrorKind::KindMismatch { .. }
500 ));
501 }
502
503 #[test]
505 fn translation_remote_has_extra_field() {
506 mod remote {
508 use facet::Facet;
509 #[derive(Facet, Debug)]
510 pub struct Point {
511 pub x: f64,
512 pub y: f64,
513 pub z: f64,
514 }
515 }
516
517 mod local {
518 use facet::Facet;
519 #[derive(Facet, Debug, PartialEq)]
520 pub struct Point {
521 pub x: f64,
522 pub z: f64,
523 }
524 }
525
526 let r = plan_for(remote::Point::SHAPE, local::Point::SHAPE).unwrap();
527
528 let remote_val = remote::Point {
530 x: 1.0,
531 y: 2.0,
532 z: 3.0,
533 };
534 let bytes = to_vec(&remote_val).unwrap();
535
536 let local_val: local::Point =
538 from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
539 assert_eq!(local_val, local::Point { x: 1.0, z: 3.0 });
540 }
541
542 #[test]
544 fn translation_remote_missing_field_with_default() {
545 mod remote {
547 use facet::Facet;
548 #[derive(Facet, Debug)]
549 pub struct Point {
550 pub x: f64,
551 }
552 }
553
554 mod local {
555 use facet::Facet;
556 #[derive(Facet, Debug, PartialEq)]
557 pub struct Point {
558 pub x: f64,
559 #[facet(default)]
560 pub y: f64,
561 }
562 }
563
564 let r = plan_for(remote::Point::SHAPE, local::Point::SHAPE).unwrap();
565
566 let remote_val = remote::Point { x: 42.0 };
567 let bytes = to_vec(&remote_val).unwrap();
568
569 let local_val: local::Point =
570 from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
571 assert_eq!(local_val, local::Point { x: 42.0, y: 0.0 });
572 }
573
574 #[test]
578 fn translation_missing_required_field_errors() {
579 mod remote {
581 use facet::Facet;
582 #[derive(Facet, Debug)]
583 pub struct Point {
584 pub x: f64,
585 }
586 }
587
588 mod local {
589 use facet::Facet;
590 #[derive(Facet, Debug)]
591 pub struct Point {
592 pub x: f64,
593 pub y: f64,
594 }
595 }
596
597 let result = plan_for(remote::Point::SHAPE, local::Point::SHAPE);
598
599 assert!(result.is_err());
600 let err = result.unwrap_err();
601 match &*err.kind {
602 error::TranslationErrorKind::MissingRequiredField { field, .. } => {
603 assert_eq!(field.name, "y");
604 }
605 other => panic!("expected MissingRequiredField, got {other:?}"),
606 }
607 let msg = err.to_string();
609 assert!(msg.contains("y"), "error should name the field: {msg}");
610 assert!(
611 msg.contains("required field") && msg.contains("missing"),
612 "error should say missing required: {msg}"
613 );
614 }
615
616 #[test]
618 fn translation_field_reorder() {
619 mod remote {
621 use facet::Facet;
622 #[derive(Facet, Debug)]
623 pub struct Pair {
624 pub b: String,
625 pub a: u32,
626 }
627 }
628
629 mod local {
630 use facet::Facet;
631 #[derive(Facet, Debug, PartialEq)]
632 pub struct Pair {
633 pub a: u32,
634 pub b: String,
635 }
636 }
637
638 let r = plan_for(remote::Pair::SHAPE, local::Pair::SHAPE).unwrap();
639
640 let remote_val = remote::Pair {
641 b: "hello".to_string(),
642 a: 42,
643 };
644 let bytes = to_vec(&remote_val).unwrap();
645
646 let local_val: local::Pair =
647 from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
648 assert_eq!(
649 local_val,
650 local::Pair {
651 a: 42,
652 b: "hello".to_string()
653 }
654 );
655 }
656
657 #[test]
659 fn translation_skip_complex_field() {
660 mod remote {
662 use facet::Facet;
663 #[derive(Facet, Debug)]
664 pub struct Msg {
665 pub id: u32,
666 pub tags: Vec<String>,
667 pub name: String,
668 }
669 }
670
671 mod local {
672 use facet::Facet;
673 #[derive(Facet, Debug, PartialEq)]
674 pub struct Msg {
675 pub id: u32,
676 pub name: String,
677 }
678 }
679
680 let r = plan_for(remote::Msg::SHAPE, local::Msg::SHAPE).unwrap();
681
682 let remote_val = remote::Msg {
683 id: 99,
684 tags: vec!["a".into(), "bb".into(), "ccc".into()],
685 name: "test".to_string(),
686 };
687 let bytes = to_vec(&remote_val).unwrap();
688
689 let local_val: local::Msg =
690 from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
691 assert_eq!(
692 local_val,
693 local::Msg {
694 id: 99,
695 name: "test".to_string()
696 }
697 );
698 }
699
700 #[test]
703 fn translation_identity_plan_matches_direct() {
704 #[derive(Facet, Debug, PartialEq)]
706 struct Point {
707 x: f64,
708 y: f64,
709 }
710
711 let val = Point { x: 1.0, y: 2.0 };
712 let bytes = to_vec(&val).unwrap();
713
714 let direct: Point = from_slice(&bytes).unwrap();
715
716 let r = plan_for(Point::SHAPE, Point::SHAPE).unwrap();
717 let translated: Point = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
718
719 assert_eq!(direct, translated);
720 }
721
722 #[test]
723 fn translation_combined_add_remove_reorder() {
724 mod remote {
726 use facet::Facet;
727 #[derive(Facet, Debug)]
728 pub struct Combo {
729 pub a: u32,
730 pub b: String,
731 pub c: bool,
732 }
733 }
734
735 mod local {
736 use facet::Facet;
737 #[derive(Facet, Debug, PartialEq)]
738 pub struct Combo {
739 pub c: bool,
740 #[facet(default)]
741 pub d: u64,
742 pub a: u32,
743 }
744 }
745
746 let r = plan_for(remote::Combo::SHAPE, local::Combo::SHAPE).unwrap();
747
748 let remote_val = remote::Combo {
749 a: 42,
750 b: "dropped".to_string(),
751 c: true,
752 };
753 let bytes = to_vec(&remote_val).unwrap();
754
755 let local_val: local::Combo =
756 from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
757 assert_eq!(
758 local_val,
759 local::Combo {
760 c: true,
761 d: 0, a: 42,
763 }
764 );
765 }
766
767 #[test]
769 fn translation_error_includes_context() {
770 mod remote {
772 use facet::Facet;
773 #[derive(Facet, Debug)]
774 pub struct Outer {
775 pub inner: u32,
776 }
777 }
778
779 mod local {
781 use facet::Facet;
782 #[derive(Facet, Debug)]
783 pub struct Outer {
784 pub inner: u32,
785 pub missing: String,
786 }
787 }
788
789 let err = plan_for(remote::Outer::SHAPE, local::Outer::SHAPE).unwrap_err();
790
791 let msg = err.to_string();
792 assert!(
794 msg.contains("Outer"),
795 "error should include local type name: {msg}"
796 );
797 assert!(
799 msg.contains("missing"),
800 "error should include field name: {msg}"
801 );
802 assert!(
804 msg.contains("required field") && msg.contains("missing"),
805 "error should describe the incompatibility: {msg}"
806 );
807 assert!(
809 !format!("{err}").is_empty(),
810 "error Display should produce output"
811 );
812 }
813
814 #[test]
816 fn translation_error_kind_mismatch() {
817 #[derive(Facet, Debug)]
819 struct RemoteStruct {
820 x: u32,
821 }
822
823 let err = plan_for(RemoteStruct::SHAPE, <u32 as Facet>::SHAPE).unwrap_err();
824
825 let msg = err.to_string();
826 assert!(msg.contains("struct"), "error should mention struct: {msg}");
827 }
828
829 #[test]
832 fn translation_enum_variant_added() {
833 mod remote {
835 use facet::Facet;
836 #[derive(Facet, Debug)]
837 #[repr(u8)]
838 pub enum Cmd {
839 Start,
840 Stop,
841 }
842 }
843
844 mod local {
845 use facet::Facet;
846 #[derive(Facet, Debug, PartialEq)]
847 #[repr(u8)]
848 pub enum Cmd {
849 Start,
850 Stop,
851 Restart,
852 }
853 }
854
855 let r = plan_for(remote::Cmd::SHAPE, local::Cmd::SHAPE).unwrap();
856
857 let bytes = to_vec(&remote::Cmd::Stop).unwrap();
858 let result: local::Cmd = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
859 assert_eq!(result, local::Cmd::Stop);
860 }
861
862 #[test]
865 fn translation_enum_unknown_variant_errors_at_runtime() {
866 mod remote {
868 use facet::Facet;
869 #[derive(Facet, Debug)]
870 #[repr(u8)]
871 pub enum Cmd {
872 Start,
873 Stop,
874 Restart,
875 }
876 }
877
878 mod local {
879 use facet::Facet;
880 #[derive(Facet, Debug, PartialEq)]
881 #[repr(u8)]
882 pub enum Cmd {
883 Start,
884 Stop,
885 }
886 }
887
888 let r = plan_for(remote::Cmd::SHAPE, local::Cmd::SHAPE).unwrap();
889
890 let bytes = to_vec(&remote::Cmd::Start).unwrap();
892 let result: local::Cmd = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
893 assert_eq!(result, local::Cmd::Start);
894
895 let bytes = to_vec(&remote::Cmd::Restart).unwrap();
897 let result: Result<local::Cmd, _> =
898 from_slice_with_plan(&bytes, &r.plan, &r.remote.registry);
899 assert!(result.is_err());
900 }
901
902 #[test]
903 fn translation_result_ok_round_trip() {
904 type T = Result<String, u32>;
905 let r = plan_for(T::SHAPE, T::SHAPE).unwrap();
906 let bytes = to_vec(&Ok::<_, u32>("hello".to_string())).unwrap();
907 let result: T = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
908 assert_eq!(result, Ok("hello".to_string()));
909 }
910
911 #[test]
912 fn translation_result_err_round_trip() {
913 type T = Result<String, u32>;
914 let r = plan_for(T::SHAPE, T::SHAPE).unwrap();
915 let bytes = to_vec(&Err::<String, _>(42u32)).unwrap();
916 let result: T = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
917 assert_eq!(result, Err(42));
918 }
919
920 #[test]
921 fn translation_result_with_enum_error() {
922 #[derive(Facet, Debug, PartialEq)]
923 #[repr(u8)]
924 enum MyError {
925 NotFound,
926 Forbidden(String),
927 }
928
929 type T = Result<u32, MyError>;
930 let r = plan_for(T::SHAPE, T::SHAPE).unwrap();
931
932 let result: T = from_slice_with_plan(
933 &to_vec(&Ok::<u32, MyError>(42u32)).unwrap(),
934 &r.plan,
935 &r.remote.registry,
936 )
937 .unwrap();
938 assert_eq!(result, Ok(42));
939
940 let result: T = from_slice_with_plan(
941 &to_vec(&Err::<u32, _>(MyError::Forbidden("nope".into()))).unwrap(),
942 &r.plan,
943 &r.remote.registry,
944 )
945 .unwrap();
946 assert_eq!(result, Err(MyError::Forbidden("nope".into())));
947 }
948
949 #[test]
950 fn translation_result_with_vox_error_shape() {
951 use std::convert::Infallible;
952 use vox_types::VoxError;
953
954 type T = Result<String, VoxError<Infallible>>;
955 let r = plan_for(T::SHAPE, T::SHAPE).unwrap();
956 let bytes = to_vec(&Ok::<_, VoxError<Infallible>>("hello".to_string())).unwrap();
957 let result: T = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
958 assert_eq!(result.unwrap(), "hello");
959 }
960
961 #[test]
962 fn translation_result_with_vox_error_err_variant() {
963 use std::convert::Infallible;
964 use vox_types::VoxError;
965
966 type T = Result<String, VoxError<Infallible>>;
967 let r = plan_for(T::SHAPE, T::SHAPE).unwrap();
968 let bytes = to_vec(&Err::<String, _>(VoxError::<Infallible>::InvalidPayload(
969 "bad data".into(),
970 )))
971 .unwrap();
972 let result: T = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
973 match result {
974 Err(VoxError::InvalidPayload(msg)) => assert_eq!(msg, "bad data"),
975 other => panic!("expected InvalidPayload, got {other:?}"),
976 }
977 }
978
979 #[test]
980 fn translation_nested_struct_in_result() {
981 #[derive(Facet, Debug, PartialEq)]
982 struct Payload {
983 id: u32,
984 name: String,
985 }
986
987 type T = Result<Payload, u32>;
988 let r = plan_for(T::SHAPE, T::SHAPE).unwrap();
989 let bytes = to_vec(&Ok::<_, u32>(Payload {
990 id: 1,
991 name: "test".into(),
992 }))
993 .unwrap();
994 let result: T = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
995 assert_eq!(
996 result,
997 Ok(Payload {
998 id: 1,
999 name: "test".into()
1000 })
1001 );
1002 }
1003
1004 #[test]
1005 fn translation_enum_newtype_variant() {
1006 #[derive(Facet, Debug, PartialEq)]
1007 #[repr(u8)]
1008 enum Message {
1009 Text(String),
1010 Number(i64),
1011 Data(Vec<u8>),
1012 }
1013
1014 let r = plan_for(Message::SHAPE, Message::SHAPE).unwrap();
1015 for val in [
1016 Message::Text("hello".into()),
1017 Message::Number(42),
1018 Message::Data(vec![1, 2, 3]),
1019 ] {
1020 let bytes = to_vec(&val).unwrap();
1021 let result: Message =
1022 from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
1023 assert_eq!(result, val);
1024 }
1025 }
1026
1027 #[test]
1028 fn opaque_round_trip() {
1029 use facet::{
1030 Facet, FacetOpaqueAdapter, OpaqueDeserialize, OpaqueSerialize, PtrConst, Shape,
1031 };
1032 use std::marker::PhantomData;
1033
1034 #[derive(Debug, Facet)]
1036 #[repr(u8)]
1037 #[facet(opaque = TestPayloadAdapter, traits(Debug))]
1038 enum TestPayload<'a> {
1039 Outgoing {
1040 ptr: PtrConst,
1041 shape: &'static Shape,
1042 _lt: PhantomData<&'a ()>,
1043 },
1044 Incoming(&'a [u8]),
1045 }
1046
1047 struct TestPayloadAdapter;
1048
1049 impl FacetOpaqueAdapter for TestPayloadAdapter {
1050 type Error = String;
1051 type SendValue<'a> = TestPayload<'a>;
1052 type RecvValue<'de> = TestPayload<'de>;
1053
1054 fn serialize_map(value: &Self::SendValue<'_>) -> OpaqueSerialize {
1055 match value {
1056 TestPayload::Outgoing { ptr, shape, .. } => {
1057 OpaqueSerialize { ptr: *ptr, shape }
1058 }
1059 TestPayload::Incoming(bytes) => crate::opaque_encoded_borrowed(bytes),
1060 }
1061 }
1062
1063 fn deserialize_build<'de>(
1064 input: OpaqueDeserialize<'de>,
1065 ) -> Result<Self::RecvValue<'de>, Self::Error> {
1066 match input {
1067 OpaqueDeserialize::Borrowed(bytes) => Ok(TestPayload::Incoming(bytes)),
1068 OpaqueDeserialize::Owned(_) => Err("must be borrowed".into()),
1069 }
1070 }
1071 }
1072
1073 #[derive(Debug, Facet)]
1075 struct TestCall<'a> {
1076 id: u32,
1077 payload: TestPayload<'a>,
1078 }
1079
1080 let val: u32 = 42;
1082 let call = TestCall {
1083 id: 7,
1084 payload: TestPayload::Outgoing {
1085 ptr: PtrConst::new((&val as *const u32).cast::<u8>()),
1086 shape: <u32 as Facet>::SHAPE,
1087 _lt: PhantomData,
1088 },
1089 };
1090
1091 let our_bytes = to_vec(&call).unwrap();
1092 eprintln!("outgoing - vox: {:02x?}", our_bytes);
1093 let round_tripped: TestCall<'_> = from_slice_borrowed(&our_bytes).unwrap();
1098 let payload_bytes = match round_tripped.payload {
1099 TestPayload::Incoming(b) => b,
1100 _ => panic!("expected incoming"),
1101 };
1102 let result: u32 = from_slice(payload_bytes).unwrap();
1103 assert_eq!(result, 42, "payload should contain 42");
1104
1105 let reserialized = to_vec(&round_tripped).unwrap();
1107 eprintln!("incoming - vox: {:02x?}", reserialized);
1108 assert_eq!(
1109 reserialized, our_bytes,
1110 "re-serialized incoming must match original outgoing"
1111 );
1112 }
1113
1114 #[test]
1115 fn opaque_vec_u8_round_trip() {
1116 use facet::{
1117 Facet, FacetOpaqueAdapter, OpaqueDeserialize, OpaqueSerialize, PtrConst, Shape,
1118 };
1119 use std::marker::PhantomData;
1120
1121 #[derive(Debug, Facet)]
1122 #[repr(u8)]
1123 #[facet(opaque = TestPayloadAdapter2, traits(Debug))]
1124 enum TestPayload2<'a> {
1125 Outgoing {
1126 ptr: PtrConst,
1127 shape: &'static Shape,
1128 _lt: PhantomData<&'a ()>,
1129 },
1130 Incoming(&'a [u8]),
1131 }
1132
1133 struct TestPayloadAdapter2;
1134
1135 impl FacetOpaqueAdapter for TestPayloadAdapter2 {
1136 type Error = String;
1137 type SendValue<'a> = TestPayload2<'a>;
1138 type RecvValue<'de> = TestPayload2<'de>;
1139
1140 fn serialize_map(value: &Self::SendValue<'_>) -> OpaqueSerialize {
1141 match value {
1142 TestPayload2::Outgoing { ptr, shape, .. } => {
1143 OpaqueSerialize { ptr: *ptr, shape }
1144 }
1145 TestPayload2::Incoming(bytes) => crate::opaque_encoded_borrowed(bytes),
1146 }
1147 }
1148
1149 fn deserialize_build<'de>(
1150 input: OpaqueDeserialize<'de>,
1151 ) -> Result<Self::RecvValue<'de>, Self::Error> {
1152 match input {
1153 OpaqueDeserialize::Borrowed(bytes) => Ok(TestPayload2::Incoming(bytes)),
1154 OpaqueDeserialize::Owned(_) => Err("must be borrowed".into()),
1155 }
1156 }
1157 }
1158
1159 #[derive(Debug, Facet)]
1160 struct TestCall2<'a> {
1161 id: u32,
1162 payload: TestPayload2<'a>,
1163 }
1164
1165 let blob = vec![0u8; 32];
1167 let call = TestCall2 {
1168 id: 7,
1169 payload: TestPayload2::Outgoing {
1170 ptr: PtrConst::new((&blob as *const Vec<u8>).cast::<u8>()),
1171 shape: <Vec<u8> as Facet>::SHAPE,
1172 _lt: PhantomData,
1173 },
1174 };
1175
1176 let encoded = to_vec(&call).unwrap();
1178 eprintln!(
1179 "encoded ({} bytes): {:02x?}",
1180 encoded.len(),
1181 &encoded[..encoded.len().min(40)]
1182 );
1183
1184 let round_tripped: TestCall2<'_> = from_slice_borrowed(&encoded).unwrap();
1186 let payload_bytes = match &round_tripped.payload {
1187 TestPayload2::Incoming(b) => *b,
1188 _ => panic!("expected incoming"),
1189 };
1190 eprintln!(
1191 "payload_bytes ({} bytes): {:02x?}",
1192 payload_bytes.len(),
1193 &payload_bytes[..payload_bytes.len().min(40)]
1194 );
1195
1196 let result: Vec<u8> = from_slice(payload_bytes).unwrap();
1198 assert_eq!(result.len(), 32, "should get back 32 bytes");
1199 assert_eq!(result, blob, "round-trip should preserve Vec<u8> content");
1200
1201 let reserialized = to_vec(&round_tripped).unwrap();
1203 assert_eq!(reserialized, encoded, "re-serialized must match original");
1204
1205 let final_trip: TestCall2<'_> = from_slice_borrowed(&reserialized).unwrap();
1207 let final_bytes = match &final_trip.payload {
1208 TestPayload2::Incoming(b) => *b,
1209 _ => panic!("expected incoming"),
1210 };
1211 let final_result: Vec<u8> = from_slice(final_bytes).unwrap();
1212 assert_eq!(
1213 final_result, blob,
1214 "double round-trip should preserve content"
1215 );
1216 }
1217
1218 #[test]
1223 fn skip_struct_field() {
1224 mod remote {
1228 use facet::Facet;
1229 #[derive(Facet, Debug)]
1230 pub struct Msg {
1231 pub id: u32,
1232 pub extra: Point,
1233 pub name: String,
1234 }
1235 #[derive(Facet, Debug)]
1236 pub struct Point {
1237 pub x: f64,
1238 pub y: f64,
1239 }
1240 }
1241 mod local {
1242 use facet::Facet;
1243 #[derive(Facet, Debug, PartialEq)]
1244 pub struct Msg {
1245 pub id: u32,
1246 pub name: String,
1247 }
1248 }
1249
1250 let r = plan_for(remote::Msg::SHAPE, local::Msg::SHAPE).unwrap();
1251 let bytes = to_vec(&remote::Msg {
1252 id: 1,
1253 extra: remote::Point {
1254 #[allow(clippy::approx_constant)]
1255 x: 3.14,
1256 y: 2.72,
1257 },
1258 name: "hi".into(),
1259 })
1260 .unwrap();
1261 let result: local::Msg = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
1262 assert_eq!(
1263 result,
1264 local::Msg {
1265 id: 1,
1266 name: "hi".into()
1267 }
1268 );
1269 }
1270
1271 #[test]
1272 fn skip_enum_field() {
1273 mod remote {
1274 use facet::Facet;
1275 #[derive(Facet, Debug)]
1276 pub struct Msg {
1277 pub id: u32,
1278 pub status: Status,
1279 pub name: String,
1280 }
1281 #[derive(Facet, Debug)]
1282 #[repr(u8)]
1283 pub enum Status {
1284 Active = 0,
1285 Inactive = 1,
1286 }
1287 }
1288 mod local {
1289 use facet::Facet;
1290 #[derive(Facet, Debug, PartialEq)]
1291 pub struct Msg {
1292 pub id: u32,
1293 pub name: String,
1294 }
1295 }
1296
1297 let r = plan_for(remote::Msg::SHAPE, local::Msg::SHAPE).unwrap();
1298 let bytes = to_vec(&remote::Msg {
1299 id: 5,
1300 status: remote::Status::Inactive,
1301 name: "test".into(),
1302 })
1303 .unwrap();
1304 let result: local::Msg = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
1305 assert_eq!(
1306 result,
1307 local::Msg {
1308 id: 5,
1309 name: "test".into()
1310 }
1311 );
1312 }
1313
1314 #[test]
1315 fn skip_option_field() {
1316 mod remote {
1317 use facet::Facet;
1318 #[derive(Facet, Debug)]
1319 pub struct Msg {
1320 pub id: u32,
1321 pub maybe: Option<String>,
1322 pub name: String,
1323 }
1324 }
1325 mod local {
1326 use facet::Facet;
1327 #[derive(Facet, Debug, PartialEq)]
1328 pub struct Msg {
1329 pub id: u32,
1330 pub name: String,
1331 }
1332 }
1333
1334 let r = plan_for(remote::Msg::SHAPE, local::Msg::SHAPE).unwrap();
1336 let bytes = to_vec(&remote::Msg {
1337 id: 1,
1338 maybe: Some("present".into()),
1339 name: "hi".into(),
1340 })
1341 .unwrap();
1342 let result: local::Msg = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
1343 assert_eq!(
1344 result,
1345 local::Msg {
1346 id: 1,
1347 name: "hi".into()
1348 }
1349 );
1350
1351 let bytes = to_vec(&remote::Msg {
1353 id: 2,
1354 maybe: None,
1355 name: "bye".into(),
1356 })
1357 .unwrap();
1358 let result: local::Msg = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
1359 assert_eq!(
1360 result,
1361 local::Msg {
1362 id: 2,
1363 name: "bye".into()
1364 }
1365 );
1366 }
1367
1368 #[test]
1369 fn skip_map_field() {
1370 mod remote {
1371 use facet::Facet;
1372 #[derive(Facet, Debug)]
1373 pub struct Msg {
1374 pub id: u32,
1375 pub meta: std::collections::HashMap<String, u32>,
1376 pub name: String,
1377 }
1378 }
1379 mod local {
1380 use facet::Facet;
1381 #[derive(Facet, Debug, PartialEq)]
1382 pub struct Msg {
1383 pub id: u32,
1384 pub name: String,
1385 }
1386 }
1387
1388 let r = plan_for(remote::Msg::SHAPE, local::Msg::SHAPE).unwrap();
1389 let mut meta = std::collections::HashMap::new();
1390 meta.insert("a".into(), 1);
1391 meta.insert("b".into(), 2);
1392 let bytes = to_vec(&remote::Msg {
1393 id: 10,
1394 meta,
1395 name: "x".into(),
1396 })
1397 .unwrap();
1398 let result: local::Msg = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
1399 assert_eq!(
1400 result,
1401 local::Msg {
1402 id: 10,
1403 name: "x".into()
1404 }
1405 );
1406 }
1407
1408 #[test]
1409 fn skip_array_field() {
1410 mod remote {
1411 use facet::Facet;
1412 #[derive(Facet, Debug)]
1413 pub struct Msg {
1414 pub id: u32,
1415 pub coords: [f64; 3],
1416 pub name: String,
1417 }
1418 }
1419 mod local {
1420 use facet::Facet;
1421 #[derive(Facet, Debug, PartialEq)]
1422 pub struct Msg {
1423 pub id: u32,
1424 pub name: String,
1425 }
1426 }
1427
1428 let r = plan_for(remote::Msg::SHAPE, local::Msg::SHAPE).unwrap();
1429 let bytes = to_vec(&remote::Msg {
1430 id: 7,
1431 coords: [1.0, 2.0, 3.0],
1432 name: "y".into(),
1433 })
1434 .unwrap();
1435 let result: local::Msg = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
1436 assert_eq!(
1437 result,
1438 local::Msg {
1439 id: 7,
1440 name: "y".into()
1441 }
1442 );
1443 }
1444
1445 #[test]
1446 fn skip_tuple_field() {
1447 mod remote {
1448 use facet::Facet;
1449 #[derive(Facet, Debug)]
1450 pub struct Msg {
1451 pub id: u32,
1452 pub pair: (String, u64),
1453 pub name: String,
1454 }
1455 }
1456 mod local {
1457 use facet::Facet;
1458 #[derive(Facet, Debug, PartialEq)]
1459 pub struct Msg {
1460 pub id: u32,
1461 pub name: String,
1462 }
1463 }
1464
1465 let r = plan_for(remote::Msg::SHAPE, local::Msg::SHAPE).unwrap();
1466 let bytes = to_vec(&remote::Msg {
1467 id: 3,
1468 pair: ("hello".into(), 999),
1469 name: "z".into(),
1470 })
1471 .unwrap();
1472 let result: local::Msg = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
1473 assert_eq!(
1474 result,
1475 local::Msg {
1476 id: 3,
1477 name: "z".into()
1478 }
1479 );
1480 }
1481
1482 #[test]
1483 fn skip_enum_with_newtype_payload() {
1484 mod remote {
1485 use facet::Facet;
1486 #[derive(Facet, Debug)]
1487 pub struct Msg {
1488 pub id: u32,
1489 pub shape: Shape,
1490 pub name: String,
1491 }
1492 #[derive(Facet, Debug)]
1493 #[repr(u8)]
1494 #[allow(dead_code)]
1495 pub enum Shape {
1496 Circle(f64) = 0,
1497 Rect { w: f64, h: f64 } = 1,
1498 Empty = 2,
1499 }
1500 }
1501 mod local {
1502 use facet::Facet;
1503 #[derive(Facet, Debug, PartialEq)]
1504 pub struct Msg {
1505 pub id: u32,
1506 pub name: String,
1507 }
1508 }
1509
1510 let r = plan_for(remote::Msg::SHAPE, local::Msg::SHAPE).unwrap();
1511
1512 let bytes = to_vec(&remote::Msg {
1514 id: 1,
1515 shape: remote::Shape::Circle(5.0),
1516 name: "a".into(),
1517 })
1518 .unwrap();
1519 let result: local::Msg = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
1520 assert_eq!(
1521 result,
1522 local::Msg {
1523 id: 1,
1524 name: "a".into()
1525 }
1526 );
1527
1528 let bytes = to_vec(&remote::Msg {
1530 id: 2,
1531 shape: remote::Shape::Rect { w: 3.0, h: 4.0 },
1532 name: "b".into(),
1533 })
1534 .unwrap();
1535 let result: local::Msg = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
1536 assert_eq!(
1537 result,
1538 local::Msg {
1539 id: 2,
1540 name: "b".into()
1541 }
1542 );
1543
1544 let bytes = to_vec(&remote::Msg {
1546 id: 3,
1547 shape: remote::Shape::Empty,
1548 name: "c".into(),
1549 })
1550 .unwrap();
1551 let result: local::Msg = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
1552 assert_eq!(
1553 result,
1554 local::Msg {
1555 id: 3,
1556 name: "c".into()
1557 }
1558 );
1559 }
1560
1561 #[test]
1566 fn translation_tuple_identity() {
1567 let r = plan_for(
1568 <(u32, String) as Facet>::SHAPE,
1569 <(u32, String) as Facet>::SHAPE,
1570 )
1571 .unwrap();
1572 let bytes = to_vec(&(42u32, "hello".to_string())).unwrap();
1573 let result: (u32, String) =
1574 from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
1575 assert_eq!(result, (42, "hello".to_string()));
1576 }
1577
1578 #[test]
1579 fn translation_nested_unary_tuple_identity() {
1580 let r = plan_for(
1581 <((i32, String),) as Facet>::SHAPE,
1582 <((i32, String),) as Facet>::SHAPE,
1583 )
1584 .unwrap();
1585 let bytes = to_vec(&((42i32, "hello".to_string()),)).unwrap();
1586 let result: ((i32, String),) =
1587 from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
1588 assert_eq!(result, ((42, "hello".to_string()),));
1589 }
1590
1591 #[test]
1592 fn translation_tuple_length_mismatch_errors() {
1593 let result = plan_for(
1594 <(u32, String) as Facet>::SHAPE,
1595 <(u32, String, bool) as Facet>::SHAPE,
1596 );
1597 assert!(result.is_err(), "tuple length mismatch should error");
1598 let err = result.unwrap_err();
1599 assert!(
1600 format!("{err}").contains("tuple length"),
1601 "error should mention tuple length: {err}"
1602 );
1603 }
1604
1605 #[test]
1610 fn translation_struct_name_mismatch_errors() {
1611 mod a {
1612 use facet::Facet;
1613 #[derive(Facet, Debug)]
1614 pub struct Foo {
1615 pub x: u32,
1616 }
1617 }
1618 mod b {
1619 use facet::Facet;
1620 #[derive(Facet, Debug)]
1621 pub struct Bar {
1622 pub x: u32,
1623 }
1624 }
1625
1626 let result = plan_for(a::Foo::SHAPE, b::Bar::SHAPE);
1627 assert!(result.is_err(), "name mismatch should error");
1628 let err = result.unwrap_err();
1629 assert!(
1630 format!("{err}").contains("name mismatch") || format!("{err}").contains("NameMismatch"),
1631 "error should mention name mismatch: {err}"
1632 );
1633 }
1634
1635 #[test]
1640 fn schema_set_from_schemas_works() {
1641 let schemas = vox_types::extract_schemas(<Vec<u32> as Facet>::SHAPE)
1642 .expect("schema extraction")
1643 .schemas;
1644 let set = SchemaSet::from_schemas(schemas);
1645 assert!(
1646 matches!(set.root.kind, vox_types::SchemaKind::List { .. }),
1647 "root should be List"
1648 );
1649 }
1650
1651 #[test]
1656 fn decode_eof_on_empty_input() {
1657 let result: Result<u32, _> = from_slice(&[]);
1658 assert!(result.is_err());
1659 }
1660
1661 #[test]
1662 fn decode_invalid_option_tag() {
1663 let result: Result<Option<u32>, _> = from_slice(&[0x02]);
1667 assert!(result.is_err());
1668 }
1669
1670 #[test]
1671 fn decode_invalid_bool() {
1672 let result: Result<bool, _> = from_slice(&[0x02]);
1674 assert!(result.is_err());
1675 }
1676
1677 #[test]
1682 fn deserialize_into_with_plan() {
1683 mod remote {
1684 use facet::Facet;
1685 #[derive(Facet, Debug)]
1686 pub struct Point {
1687 pub y: f64,
1688 pub x: f64,
1689 }
1690 }
1691 mod local {
1692 use facet::Facet;
1693 #[derive(Facet, Debug, PartialEq)]
1694 pub struct Point {
1695 pub x: f64,
1696 pub y: f64,
1697 }
1698 }
1699
1700 let r = plan_for(remote::Point::SHAPE, local::Point::SHAPE).unwrap();
1701 let bytes = to_vec(&remote::Point { y: 2.0, x: 1.0 }).unwrap();
1702
1703 let partial = facet_reflect::Partial::alloc_owned::<local::Point>().unwrap();
1705 let partial =
1706 deserialize_into::<false>(partial, &bytes, &r.plan, &r.remote.registry).unwrap();
1707 let heap = partial.build().unwrap();
1708 let value: local::Point = heap.materialize().unwrap();
1709 assert_eq!(value, local::Point { x: 1.0, y: 2.0 });
1710 }
1711
1712 #[test]
1717 fn translation_enum_struct_variant_with_plan() {
1718 mod remote {
1719 use facet::Facet;
1720 #[derive(Facet, Debug)]
1721 #[repr(u8)]
1722 #[allow(dead_code)]
1723 pub enum Shape {
1724 Circle { radius: f64 } = 0,
1725 Rect { w: f64, h: f64 } = 1,
1726 }
1727 }
1728 mod local {
1729 use facet::Facet;
1730 #[derive(Facet, Debug, PartialEq)]
1731 #[repr(u8)]
1732 pub enum Shape {
1733 Circle { radius: f64 } = 0,
1734 Rect { w: f64, h: f64 } = 1,
1735 Triangle { base: f64, height: f64 } = 2,
1736 }
1737 }
1738
1739 let r = plan_for(remote::Shape::SHAPE, local::Shape::SHAPE).unwrap();
1740
1741 let bytes = to_vec(&remote::Shape::Rect { w: 3.0, h: 4.0 }).unwrap();
1742 let result: local::Shape =
1743 from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
1744 assert_eq!(result, local::Shape::Rect { w: 3.0, h: 4.0 });
1745 }
1746
1747 #[test]
1748 fn translation_enum_tuple_variant() {
1749 mod remote {
1750 use facet::Facet;
1751 #[derive(Facet, Debug)]
1752 #[repr(u8)]
1753 #[allow(dead_code)]
1754 pub enum Msg {
1755 Pair(u32, String) = 0,
1756 Single(u32) = 1,
1757 }
1758 }
1759 mod local {
1760 use facet::Facet;
1761 #[derive(Facet, Debug, PartialEq)]
1762 #[repr(u8)]
1763 pub enum Msg {
1764 Pair(u32, String) = 0,
1765 Single(u32) = 1,
1766 }
1767 }
1768
1769 let r = plan_for(remote::Msg::SHAPE, local::Msg::SHAPE).unwrap();
1770 let bytes = to_vec(&remote::Msg::Pair(42, "hi".into())).unwrap();
1771 let result: local::Msg = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
1772 assert_eq!(result, local::Msg::Pair(42, "hi".into()));
1773 }
1774
1775 #[test]
1780 fn round_trip_array() {
1781 round_trip(&[1u32, 2, 3, 4]);
1782 }
1783
1784 #[test]
1785 fn round_trip_hashmap() {
1786 let mut val = std::collections::HashMap::new();
1787 val.insert("one".to_string(), 1u32);
1788 round_trip(&val);
1789 }
1790
1791 #[test]
1792 fn round_trip_btreemap() {
1793 let mut val = std::collections::BTreeMap::new();
1794 val.insert("alpha".to_string(), 1u32);
1795 val.insert("beta".to_string(), 2);
1796 val.insert("gamma".to_string(), 3);
1797 round_trip(&val);
1798 }
1799
1800 #[test]
1801 fn round_trip_unit_struct() {
1802 #[derive(Facet, Debug, PartialEq)]
1803 struct Empty;
1804
1805 round_trip(&Empty);
1806 }
1807
1808 #[test]
1813 fn translation_struct_with_array_field() {
1814 mod remote {
1815 use facet::Facet;
1816 #[derive(Facet, Debug)]
1817 pub struct Data {
1818 pub coords: [f64; 3],
1819 pub label: String,
1820 }
1821 }
1822 mod local {
1823 use facet::Facet;
1824 #[derive(Facet, Debug, PartialEq)]
1825 pub struct Data {
1826 pub label: String,
1827 pub coords: [f64; 3],
1828 }
1829 }
1830
1831 let r = plan_for(remote::Data::SHAPE, local::Data::SHAPE).unwrap();
1832 let bytes = to_vec(&remote::Data {
1833 coords: [1.0, 2.0, 3.0],
1834 label: "pt".into(),
1835 })
1836 .unwrap();
1837 let result: local::Data =
1838 from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
1839 assert_eq!(
1840 result,
1841 local::Data {
1842 label: "pt".into(),
1843 coords: [1.0, 2.0, 3.0],
1844 }
1845 );
1846 }
1847
1848 #[test]
1849 fn translation_struct_with_map_field() {
1850 mod remote {
1851 use facet::Facet;
1852 #[derive(Facet, Debug)]
1853 pub struct Data {
1854 pub meta: std::collections::HashMap<String, u32>,
1855 pub id: u32,
1856 }
1857 }
1858 mod local {
1859 use facet::Facet;
1860 #[derive(Facet, Debug, PartialEq)]
1861 pub struct Data {
1862 pub id: u32,
1863 pub meta: std::collections::HashMap<String, u32>,
1864 }
1865 }
1866
1867 let r = plan_for(remote::Data::SHAPE, local::Data::SHAPE).unwrap();
1868 let mut meta = std::collections::HashMap::new();
1869 meta.insert("x".into(), 10);
1870 let bytes = to_vec(&remote::Data {
1871 meta: meta.clone(),
1872 id: 5,
1873 })
1874 .unwrap();
1875 let result: local::Data =
1876 from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
1877 assert_eq!(result, local::Data { id: 5, meta });
1878 }
1879
1880 #[test]
1881 fn translation_struct_with_pointer_field() {
1882 mod remote {
1884 use facet::Facet;
1885 #[derive(Facet, Debug)]
1886 pub struct Data {
1887 #[allow(clippy::box_collection)]
1888 pub inner: Box<String>,
1889 pub id: u32,
1890 }
1891 }
1892 mod local {
1893 use facet::Facet;
1894 #[derive(Facet, Debug, PartialEq)]
1895 pub struct Data {
1896 pub id: u32,
1897 #[allow(clippy::box_collection)]
1898 pub inner: Box<String>,
1899 }
1900 }
1901
1902 let r = plan_for(remote::Data::SHAPE, local::Data::SHAPE).unwrap();
1903 let bytes = to_vec(&remote::Data {
1904 inner: Box::new("hello".into()),
1905 id: 7,
1906 })
1907 .unwrap();
1908 let result: local::Data =
1909 from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
1910 assert_eq!(
1911 result,
1912 local::Data {
1913 id: 7,
1914 inner: Box::new("hello".into()),
1915 }
1916 );
1917 }
1918
1919 #[test]
1924 fn round_trip_transparent_wrapper() {
1925 #[derive(Facet, Debug, PartialEq)]
1926 #[facet(transparent)]
1927 struct Wrapper(u32);
1928
1929 round_trip(&Wrapper(42));
1930 }
1931
1932 #[test]
1933 fn translation_struct_field_reorder_with_nested_struct() {
1934 mod remote {
1937 use facet::Facet;
1938 #[derive(Facet, Debug)]
1939 pub struct Outer {
1940 pub inner: Inner,
1941 pub id: u32,
1942 }
1943 #[derive(Facet, Debug)]
1944 pub struct Inner {
1945 pub value: String,
1946 }
1947 }
1948 mod local {
1949 use facet::Facet;
1950 #[derive(Facet, Debug, PartialEq)]
1951 pub struct Outer {
1952 pub id: u32,
1953 pub inner: Inner,
1954 }
1955 #[derive(Facet, Debug, PartialEq)]
1956 pub struct Inner {
1957 pub value: String,
1958 }
1959 }
1960
1961 let r = plan_for(remote::Outer::SHAPE, local::Outer::SHAPE).unwrap();
1962 let bytes = to_vec(&remote::Outer {
1963 inner: remote::Inner {
1964 value: "hello".into(),
1965 },
1966 id: 42,
1967 })
1968 .unwrap();
1969 let result: local::Outer =
1970 from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
1971 assert_eq!(
1972 result,
1973 local::Outer {
1974 id: 42,
1975 inner: local::Inner {
1976 value: "hello".into(),
1977 },
1978 }
1979 );
1980 }
1981
1982 #[test]
1987 fn round_trip_u128() {
1988 round_trip(&u128::MAX);
1989 }
1990
1991 #[test]
1992 fn round_trip_i128() {
1993 round_trip(&i128::MIN);
1994 }
1995
1996 #[test]
1997 fn round_trip_u128_zero() {
1998 round_trip(&0u128);
1999 }
2000
2001 #[test]
2006 fn skip_enum_tuple_variant_field() {
2007 mod remote {
2008 use facet::Facet;
2009 #[derive(Facet, Debug)]
2010 pub struct Msg {
2011 pub id: u32,
2012 pub ev: Event,
2013 pub name: String,
2014 }
2015 #[derive(Facet, Debug)]
2016 #[repr(u8)]
2017 #[allow(dead_code)]
2018 pub enum Event {
2019 Click(u32, u32) = 0,
2020 Key(String) = 1,
2021 }
2022 }
2023 mod local {
2024 use facet::Facet;
2025 #[derive(Facet, Debug, PartialEq)]
2026 pub struct Msg {
2027 pub id: u32,
2028 pub name: String,
2029 }
2030 }
2031
2032 let r = plan_for(remote::Msg::SHAPE, local::Msg::SHAPE).unwrap();
2033
2034 let bytes = to_vec(&remote::Msg {
2036 id: 1,
2037 ev: remote::Event::Click(10, 20),
2038 name: "a".into(),
2039 })
2040 .unwrap();
2041 let result: local::Msg = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
2042 assert_eq!(
2043 result,
2044 local::Msg {
2045 id: 1,
2046 name: "a".into()
2047 }
2048 );
2049
2050 let bytes = to_vec(&remote::Msg {
2052 id: 2,
2053 ev: remote::Event::Key("enter".into()),
2054 name: "b".into(),
2055 })
2056 .unwrap();
2057 let result: local::Msg = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
2058 assert_eq!(
2059 result,
2060 local::Msg {
2061 id: 2,
2062 name: "b".into()
2063 }
2064 );
2065 }
2066
2067 #[test]
2072 fn round_trip_char() {
2073 round_trip(&'🦀');
2074 }
2075
2076 #[test]
2077 fn round_trip_unit() {
2078 round_trip(&());
2079 }
2080
2081 #[test]
2082 fn skip_unit_field() {
2083 mod remote {
2084 use facet::Facet;
2085 #[derive(Facet, Debug)]
2086 pub struct Msg {
2087 pub id: u32,
2088 pub nothing: (),
2089 pub name: String,
2090 }
2091 }
2092 mod local {
2093 use facet::Facet;
2094 #[derive(Facet, Debug, PartialEq)]
2095 pub struct Msg {
2096 pub id: u32,
2097 pub name: String,
2098 }
2099 }
2100
2101 let r = plan_for(remote::Msg::SHAPE, local::Msg::SHAPE).unwrap();
2102 let bytes = to_vec(&remote::Msg {
2103 id: 1,
2104 nothing: (),
2105 name: "x".into(),
2106 })
2107 .unwrap();
2108 let result: local::Msg = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
2109 assert_eq!(
2110 result,
2111 local::Msg {
2112 id: 1,
2113 name: "x".into()
2114 }
2115 );
2116 }
2117
2118 #[test]
2119 fn skip_char_field() {
2120 mod remote {
2121 use facet::Facet;
2122 #[derive(Facet, Debug)]
2123 pub struct Msg {
2124 pub id: u32,
2125 pub ch: char,
2126 pub name: String,
2127 }
2128 }
2129 mod local {
2130 use facet::Facet;
2131 #[derive(Facet, Debug, PartialEq)]
2132 pub struct Msg {
2133 pub id: u32,
2134 pub name: String,
2135 }
2136 }
2137
2138 let r = plan_for(remote::Msg::SHAPE, local::Msg::SHAPE).unwrap();
2139 let bytes = to_vec(&remote::Msg {
2140 id: 1,
2141 ch: 'Z',
2142 name: "y".into(),
2143 })
2144 .unwrap();
2145 let result: local::Msg = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
2146 assert_eq!(
2147 result,
2148 local::Msg {
2149 id: 1,
2150 name: "y".into()
2151 }
2152 );
2153 }
2154
2155 #[test]
2160 fn skip_u128_field() {
2161 mod remote {
2162 use facet::Facet;
2163 #[derive(Facet, Debug)]
2164 pub struct Msg {
2165 pub id: u32,
2166 pub big: u128,
2167 pub name: String,
2168 }
2169 }
2170 mod local {
2171 use facet::Facet;
2172 #[derive(Facet, Debug, PartialEq)]
2173 pub struct Msg {
2174 pub id: u32,
2175 pub name: String,
2176 }
2177 }
2178
2179 let r = plan_for(remote::Msg::SHAPE, local::Msg::SHAPE).unwrap();
2180 let bytes = to_vec(&remote::Msg {
2181 id: 1,
2182 big: u128::MAX,
2183 name: "z".into(),
2184 })
2185 .unwrap();
2186 let result: local::Msg = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
2187 assert_eq!(
2188 result,
2189 local::Msg {
2190 id: 1,
2191 name: "z".into()
2192 }
2193 );
2194 }
2195
2196 #[test]
2201 fn borrowed_deserialization_with_plan() {
2202 mod remote {
2203 use facet::Facet;
2204 #[derive(Facet, Debug)]
2205 pub struct Msg<'a> {
2206 pub extra: u32,
2207 pub name: &'a str,
2208 }
2209 }
2210 mod local {
2211 use facet::Facet;
2212 #[derive(Facet, Debug, PartialEq)]
2213 pub struct Msg<'a> {
2214 pub name: &'a str,
2215 }
2216 }
2217
2218 let r = plan_for(remote::Msg::SHAPE, local::Msg::SHAPE).unwrap();
2219 let bytes = to_vec(&remote::Msg {
2220 extra: 99,
2221 name: "hello",
2222 })
2223 .unwrap();
2224 let result: local::Msg =
2225 from_slice_borrowed_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
2226 assert_eq!(result, local::Msg { name: "hello" });
2227 }
2228
2229 #[test]
2234 fn round_trip_hashset() {
2235 use std::collections::HashSet;
2236 let mut val = HashSet::new();
2238 val.insert(42u32);
2239 round_trip(&val);
2240 }
2241
2242 #[test]
2247 fn skip_f32_field() {
2248 mod remote {
2249 use facet::Facet;
2250 #[derive(Facet, Debug)]
2251 pub struct Msg {
2252 pub id: u32,
2253 pub temp: f32,
2254 pub name: String,
2255 }
2256 }
2257 mod local {
2258 use facet::Facet;
2259 #[derive(Facet, Debug, PartialEq)]
2260 pub struct Msg {
2261 pub id: u32,
2262 pub name: String,
2263 }
2264 }
2265
2266 let r = plan_for(remote::Msg::SHAPE, local::Msg::SHAPE).unwrap();
2267 let bytes = to_vec(&remote::Msg {
2268 id: 1,
2269 #[allow(clippy::approx_constant)]
2270 temp: 3.14,
2271 name: "x".into(),
2272 })
2273 .unwrap();
2274 let result: local::Msg = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
2275 assert_eq!(
2276 result,
2277 local::Msg {
2278 id: 1,
2279 name: "x".into()
2280 }
2281 );
2282 }
2283
2284 #[test]
2285 fn skip_i128_field() {
2286 mod remote {
2287 use facet::Facet;
2288 #[derive(Facet, Debug)]
2289 pub struct Msg {
2290 pub id: u32,
2291 pub big: i128,
2292 pub name: String,
2293 }
2294 }
2295 mod local {
2296 use facet::Facet;
2297 #[derive(Facet, Debug, PartialEq)]
2298 pub struct Msg {
2299 pub id: u32,
2300 pub name: String,
2301 }
2302 }
2303
2304 let r = plan_for(remote::Msg::SHAPE, local::Msg::SHAPE).unwrap();
2305 let bytes = to_vec(&remote::Msg {
2306 id: 1,
2307 big: i128::MIN,
2308 name: "y".into(),
2309 })
2310 .unwrap();
2311 let result: local::Msg = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
2312 assert_eq!(
2313 result,
2314 local::Msg {
2315 id: 1,
2316 name: "y".into()
2317 }
2318 );
2319 }
2320
2321 #[test]
2326 fn truncated_struct_input_errors() {
2327 #[derive(Facet, Debug)]
2328 struct Msg {
2329 id: u32,
2330 name: String,
2331 }
2332
2333 let bytes = to_vec(&Msg {
2335 id: 42,
2336 name: "hello world".into(),
2337 })
2338 .unwrap();
2339 let truncated = &bytes[..1];
2341 let result: Result<Msg, _> = from_slice(truncated);
2342 assert!(result.is_err(), "truncated input should error");
2343 }
2344
2345 #[test]
2346 fn truncated_list_input_errors() {
2347 let bytes = to_vec(&vec![1u32, 2, 3, 4, 5]).unwrap();
2349 let truncated = &bytes[..2]; let result: Result<Vec<u32>, _> = from_slice(truncated);
2351 assert!(result.is_err(), "truncated list should error");
2352 }
2353
2354 #[test]
2359 fn round_trip_f32() {
2360 round_trip(
2361 #[allow(clippy::approx_constant)]
2362 &3.14f32,
2363 );
2364 }
2365
2366 #[test]
2371 fn round_trip_boxed_str() {
2372 round_trip(&Box::<str>::from("hello"));
2373 }
2374
2375 #[test]
2380 fn proxy_type_round_trip() {
2381 #[derive(Debug, PartialEq, Facet)]
2384 #[facet(proxy = u32)]
2385 struct Proxied {
2386 inner: u32,
2387 }
2388
2389 impl From<u32> for Proxied {
2390 fn from(v: u32) -> Self {
2391 Proxied { inner: v }
2392 }
2393 }
2394
2395 impl From<&Proxied> for u32 {
2396 fn from(p: &Proxied) -> Self {
2397 p.inner
2398 }
2399 }
2400
2401 let bytes = to_vec(&42u32).unwrap();
2403 let result: Proxied = from_slice(&bytes).unwrap();
2405 assert_eq!(result, Proxied { inner: 42 });
2406 }
2407
2408 #[test]
2409 fn proxy_type_in_struct_round_trip() {
2410 #[derive(Debug, PartialEq, Facet)]
2412 #[facet(proxy = u32)]
2413 struct Proxied {
2414 inner: u32,
2415 }
2416
2417 impl From<u32> for Proxied {
2418 fn from(v: u32) -> Self {
2419 Proxied { inner: v }
2420 }
2421 }
2422
2423 impl From<&Proxied> for u32 {
2424 fn from(p: &Proxied) -> Self {
2425 p.inner
2426 }
2427 }
2428
2429 #[derive(Debug, PartialEq, Facet)]
2430 struct Msg {
2431 name: String,
2432 value: Proxied,
2433 }
2434
2435 let msg = Msg {
2436 name: "test".into(),
2437 value: Proxied { inner: 99 },
2438 };
2439 let bytes = to_vec(&msg).unwrap();
2440 let result: Msg = from_slice(&bytes).unwrap();
2441 assert_eq!(result, msg);
2442 }
2443
2444 #[test]
2449 fn round_trip_struct_with_bytes_field() {
2450 #[derive(Facet, Debug, PartialEq)]
2451 struct Msg {
2452 id: u32,
2453 data: Vec<u8>,
2454 }
2455
2456 round_trip(&Msg {
2457 id: 42,
2458 data: vec![1, 2, 3, 4, 5],
2459 });
2460 }
2461
2462 #[test]
2463 fn round_trip_struct_with_large_borrowed_bytes() {
2464 #[derive(Facet, Debug, PartialEq)]
2466 struct Msg<'a> {
2467 id: u32,
2468 data: &'a [u8],
2469 }
2470
2471 let data = vec![0xABu8; 8192];
2472 let msg = Msg { id: 1, data: &data };
2473 let direct = to_vec(&msg).unwrap();
2474
2475 let peek = facet_reflect::Peek::new(&msg);
2476 let plan = peek_to_scatter_plan(peek).unwrap();
2477
2478 let has_reference = plan
2480 .segments()
2481 .iter()
2482 .any(|s| matches!(s, scatter::Segment::Reference { .. }));
2483 assert!(
2484 has_reference,
2485 "large borrowed bytes should produce a Reference segment"
2486 );
2487
2488 let mut scatter_bytes = vec![0u8; plan.total_size()];
2489 plan.write_into(&mut scatter_bytes);
2490 assert_eq!(direct, scatter_bytes);
2491
2492 let io_slices = plan.to_io_slices();
2494 let mut io_bytes = Vec::new();
2495 for slice in &io_slices {
2496 io_bytes.extend_from_slice(slice);
2497 }
2498 assert_eq!(direct, io_bytes);
2499
2500 let result: Msg = from_slice_borrowed(&direct).unwrap();
2501 assert_eq!(result.id, 1);
2502 assert_eq!(result.data, &data[..]);
2503 }
2504
2505 #[test]
2506 fn round_trip_large_string() {
2507 let text = "x".repeat(8192);
2509 round_trip(&text);
2510 }
2511
2512 #[test]
2517 fn deserialize_error_display_coverage() {
2518 use error::DeserializeError;
2519
2520 let errors: Vec<DeserializeError> = vec![
2522 DeserializeError::UnexpectedEof { pos: 42 },
2523 DeserializeError::VarintOverflow { pos: 10 },
2524 DeserializeError::InvalidBool { pos: 5, got: 0x02 },
2525 DeserializeError::InvalidUtf8 { pos: 20 },
2526 DeserializeError::InvalidOptionTag { pos: 3, got: 0xFF },
2527 DeserializeError::InvalidEnumDiscriminant {
2528 pos: 0,
2529 index: 99,
2530 variant_count: 3,
2531 },
2532 DeserializeError::UnsupportedType("SomeType".into()),
2533 DeserializeError::ReflectError("something went wrong".into()),
2534 DeserializeError::UnknownVariant { remote_index: 7 },
2535 DeserializeError::TrailingBytes { pos: 10, len: 20 },
2536 DeserializeError::Custom("custom error".into()),
2537 DeserializeError::protocol("protocol violation"),
2538 ];
2539
2540 for err in &errors {
2541 let msg = format!("{err}");
2542 assert!(!msg.is_empty(), "error display should not be empty");
2543 }
2544 }
2545
2546 #[test]
2547 fn serialize_error_display_coverage() {
2548 use error::SerializeError;
2549
2550 let errors: Vec<SerializeError> = vec![
2551 SerializeError::UnsupportedType("BadType".into()),
2552 SerializeError::ReflectError("reflect fail".into()),
2553 ];
2554
2555 for err in &errors {
2556 let msg = format!("{err}");
2557 assert!(!msg.is_empty());
2558 }
2559 }
2560
2561 #[test]
2562 fn translation_error_display_coverage() {
2563 use vox_types::{
2564 FieldSchema, Schema, SchemaHash, SchemaKind, TypeRef, VariantPayload, VariantSchema,
2565 };
2566
2567 let dummy_struct = Schema {
2568 id: SchemaHash(1),
2569 type_params: vec![],
2570 kind: SchemaKind::Struct {
2571 name: "Foo".into(),
2572 fields: vec![FieldSchema {
2573 name: "x".into(),
2574 type_ref: TypeRef::concrete(SchemaHash(2)),
2575 required: true,
2576 }],
2577 },
2578 };
2579 let dummy_enum = Schema {
2580 id: SchemaHash(3),
2581 type_params: vec![],
2582 kind: SchemaKind::Enum {
2583 name: "Bar".into(),
2584 variants: vec![],
2585 },
2586 };
2587 let dummy_prim = Schema {
2588 id: SchemaHash(4),
2589 type_params: vec![],
2590 kind: SchemaKind::Primitive {
2591 primitive_type: vox_types::PrimitiveType::U32,
2592 },
2593 };
2594
2595 let errors: Vec<TranslationError> = vec![
2596 TranslationError::new(TranslationErrorKind::NameMismatch {
2597 remote: dummy_struct.clone(),
2598 local: dummy_enum.clone(),
2599 remote_rust: "Dummy".into(),
2600 local_rust: "Dummy".into(),
2601 }),
2602 TranslationError::new(TranslationErrorKind::KindMismatch {
2603 remote: dummy_struct.clone(),
2604 local: dummy_prim.clone(),
2605 remote_rust: "Dummy".into(),
2606 local_rust: "u32".into(),
2607 }),
2608 TranslationError::new(TranslationErrorKind::MissingRequiredField {
2609 field: FieldSchema {
2610 name: "missing".into(),
2611 type_ref: TypeRef::concrete(SchemaHash(5)),
2612 required: true,
2613 },
2614 remote_struct: dummy_struct.clone(),
2615 }),
2616 TranslationError::new(TranslationErrorKind::IncompatibleVariantPayload {
2617 remote_variant: VariantSchema {
2618 name: "V".into(),
2619 index: 0,
2620 payload: VariantPayload::Unit,
2621 },
2622 local_variant: VariantSchema {
2623 name: "V".into(),
2624 index: 0,
2625 payload: VariantPayload::Newtype {
2626 type_ref: TypeRef::concrete(SchemaHash(6)),
2627 },
2628 },
2629 }),
2630 TranslationError::new(TranslationErrorKind::SchemaNotFound {
2631 type_id: SchemaHash(99),
2632 side: error::SchemaSide::Remote,
2633 }),
2634 TranslationError::new(TranslationErrorKind::TupleLengthMismatch {
2635 remote: dummy_prim.clone(),
2636 local: dummy_prim.clone(),
2637 remote_rust: "(u32, u32)".into(),
2638 local_rust: "(u32, u32, u32)".into(),
2639 remote_len: 2,
2640 local_len: 3,
2641 }),
2642 TranslationError::new(TranslationErrorKind::UnresolvedVar {
2643 name: "T".into(),
2644 side: error::SchemaSide::Local,
2645 }),
2646 ];
2647
2648 for err in &errors {
2649 let msg = format!("{err}");
2650 assert!(
2651 !msg.is_empty(),
2652 "translation error display should not be empty"
2653 );
2654 }
2655
2656 let with_path = TranslationError::new(TranslationErrorKind::SchemaNotFound {
2658 type_id: SchemaHash(1),
2659 side: error::SchemaSide::Remote,
2660 })
2661 .with_path_prefix(error::PathSegment::Field("foo".into()));
2662 let msg = format!("{with_path}");
2663 assert!(msg.contains(".foo"), "should contain path: {msg}");
2664 }
2665
2666 #[test]
2671 fn translation_through_vec_skips_extra_field() {
2672 mod remote {
2676 use facet::Facet;
2677 #[derive(Facet, Debug)]
2678 pub struct A {
2679 pub items: Vec<B>,
2680 }
2681 #[derive(Facet, Debug)]
2682 pub struct B {
2683 pub x: u32,
2684 pub extra: String,
2685 }
2686 }
2687 mod local {
2688 use facet::Facet;
2689 #[derive(Facet, Debug, PartialEq)]
2690 pub struct A {
2691 pub items: Vec<B>,
2692 }
2693 #[derive(Facet, Debug, PartialEq)]
2694 pub struct B {
2695 pub x: u32,
2696 }
2697 }
2698
2699 let r = plan_for(remote::A::SHAPE, local::A::SHAPE).unwrap();
2700 let bytes = to_vec(&remote::A {
2701 items: vec![
2702 remote::B {
2703 x: 1,
2704 extra: "a".into(),
2705 },
2706 remote::B {
2707 x: 2,
2708 extra: "bb".into(),
2709 },
2710 remote::B {
2711 x: 3,
2712 extra: "ccc".into(),
2713 },
2714 ],
2715 })
2716 .unwrap();
2717 let result: local::A = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
2718 assert_eq!(
2719 result,
2720 local::A {
2721 items: vec![local::B { x: 1 }, local::B { x: 2 }, local::B { x: 3 }],
2722 }
2723 );
2724 }
2725
2726 #[test]
2727 fn translation_through_option_skips_extra_field() {
2728 mod remote {
2731 use facet::Facet;
2732 #[derive(Facet, Debug)]
2733 pub struct A {
2734 pub maybe: Option<B>,
2735 pub id: u32,
2736 }
2737 #[derive(Facet, Debug)]
2738 pub struct B {
2739 pub x: u32,
2740 pub extra: String,
2741 }
2742 }
2743 mod local {
2744 use facet::Facet;
2745 #[derive(Facet, Debug, PartialEq)]
2746 pub struct A {
2747 pub maybe: Option<B>,
2748 pub id: u32,
2749 }
2750 #[derive(Facet, Debug, PartialEq)]
2751 pub struct B {
2752 pub x: u32,
2753 }
2754 }
2755
2756 let r = plan_for(remote::A::SHAPE, local::A::SHAPE).unwrap();
2757 let bytes = to_vec(&remote::A {
2758 maybe: Some(remote::B {
2759 x: 42,
2760 extra: "gone".into(),
2761 }),
2762 id: 99,
2763 })
2764 .unwrap();
2765 let result: local::A = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
2766 assert_eq!(
2767 result,
2768 local::A {
2769 maybe: Some(local::B { x: 42 }),
2770 id: 99,
2771 }
2772 );
2773 }
2774
2775 #[test]
2776 fn translation_through_result_skips_extra_field() {
2777 mod remote {
2780 use facet::Facet;
2781 #[derive(Facet, Debug)]
2782 pub struct A {
2783 pub result: Result<B, String>,
2784 pub id: u32,
2785 }
2786 #[derive(Facet, Debug)]
2787 pub struct B {
2788 pub x: u32,
2789 pub extra: String,
2790 }
2791 }
2792 mod local {
2793 use facet::Facet;
2794 #[derive(Facet, Debug, PartialEq)]
2795 pub struct A {
2796 pub result: Result<B, String>,
2797 pub id: u32,
2798 }
2799 #[derive(Facet, Debug, PartialEq)]
2800 pub struct B {
2801 pub x: u32,
2802 }
2803 }
2804
2805 let r = plan_for(remote::A::SHAPE, local::A::SHAPE).unwrap();
2806 let bytes = to_vec(&remote::A {
2807 result: Ok(remote::B {
2808 x: 99,
2809 extra: "dropped".into(),
2810 }),
2811 id: 77,
2812 })
2813 .unwrap();
2814 let result: local::A = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
2815 assert_eq!(
2816 result,
2817 local::A {
2818 result: Ok(local::B { x: 99 }),
2819 id: 77,
2820 }
2821 );
2822 }
2823
2824 #[test]
2825 fn translation_through_map_value_skips_extra_field() {
2826 mod remote {
2829 use facet::Facet;
2830 #[derive(Facet, Debug)]
2831 pub struct A {
2832 pub map: std::collections::HashMap<String, B>,
2833 pub id: u32,
2834 }
2835 #[derive(Facet, Debug)]
2836 pub struct B {
2837 pub x: u32,
2838 pub extra: String,
2839 }
2840 }
2841 mod local {
2842 use facet::Facet;
2843 #[derive(Facet, Debug, PartialEq)]
2844 pub struct A {
2845 pub map: std::collections::HashMap<String, B>,
2846 pub id: u32,
2847 }
2848 #[derive(Facet, Debug, PartialEq)]
2849 pub struct B {
2850 pub x: u32,
2851 }
2852 }
2853
2854 let r = plan_for(remote::A::SHAPE, local::A::SHAPE).unwrap();
2855 let mut remote_map = std::collections::HashMap::new();
2856 remote_map.insert(
2857 "key".into(),
2858 remote::B {
2859 x: 7,
2860 extra: "nope".into(),
2861 },
2862 );
2863 let bytes = to_vec(&remote::A {
2864 map: remote_map,
2865 id: 55,
2866 })
2867 .unwrap();
2868 let result: local::A = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
2869 let mut expected_map = std::collections::HashMap::new();
2870 expected_map.insert("key".into(), local::B { x: 7 });
2871 assert_eq!(
2872 result,
2873 local::A {
2874 map: expected_map,
2875 id: 55,
2876 }
2877 );
2878 }
2879
2880 #[test]
2881 fn translation_nested_three_levels_deep() {
2882 mod remote {
2884 use facet::Facet;
2885 #[derive(Facet, Debug)]
2886 pub struct A {
2887 pub items: Vec<B>,
2888 }
2889 #[derive(Facet, Debug)]
2890 pub struct B {
2891 pub inner: C,
2892 }
2893 #[derive(Facet, Debug)]
2894 pub struct C {
2895 pub x: u32,
2896 pub extra: String,
2897 }
2898 }
2899 mod local {
2900 use facet::Facet;
2901 #[derive(Facet, Debug, PartialEq)]
2902 pub struct A {
2903 pub items: Vec<B>,
2904 }
2905 #[derive(Facet, Debug, PartialEq)]
2906 pub struct B {
2907 pub inner: C,
2908 }
2909 #[derive(Facet, Debug, PartialEq)]
2910 pub struct C {
2911 pub x: u32,
2912 }
2913 }
2914
2915 let r = plan_for(remote::A::SHAPE, local::A::SHAPE).unwrap();
2916 let bytes = to_vec(&remote::A {
2917 items: vec![remote::B {
2918 inner: remote::C {
2919 x: 42,
2920 extra: "deep".into(),
2921 },
2922 }],
2923 })
2924 .unwrap();
2925 let result: local::A = from_slice_with_plan(&bytes, &r.plan, &r.remote.registry).unwrap();
2926 assert_eq!(
2927 result,
2928 local::A {
2929 items: vec![local::B {
2930 inner: local::C { x: 42 },
2931 }],
2932 }
2933 );
2934 }
2935
2936 #[test]
2941 fn non_borrowed_into_borrowed_str_must_fail() {
2942 #[derive(Facet, Debug, PartialEq)]
2946 struct Msg<'a> {
2947 name: &'a str,
2948 }
2949
2950 let bytes = to_vec(&"hello".to_string()).unwrap();
2951 let result: Result<Msg<'_>, _> = from_slice(&bytes);
2953 assert!(
2954 result.is_err(),
2955 "from_slice (non-borrowed) into &str should fail, got: {:?}",
2956 result,
2957 );
2958 }
2959
2960 #[test]
2961 fn borrowed_into_borrowed_str_must_succeed() {
2962 #[derive(Facet, Debug, PartialEq)]
2965 struct Msg<'a> {
2966 name: &'a str,
2967 }
2968
2969 let bytes = to_vec(&"hello".to_string()).unwrap();
2970 let result: Msg<'_> = from_slice_borrowed(&bytes).unwrap();
2971 assert_eq!(result.name, "hello");
2972 }
2973
2974 #[test]
2975 fn non_borrowed_cow_str_returns_owned() {
2976 use std::borrow::Cow;
2979
2980 let bytes = to_vec(&"hello".to_string()).unwrap();
2981 let result: Cow<'_, str> = from_slice(&bytes).unwrap();
2982 assert_eq!(&*result, "hello");
2983 assert!(
2984 matches!(result, Cow::Owned(_)),
2985 "from_slice into Cow<str> should return Owned, got Borrowed",
2986 );
2987 }
2988
2989 #[test]
2990 fn borrowed_cow_str_returns_borrowed() {
2991 use std::borrow::Cow;
2994
2995 let bytes = to_vec(&"hello".to_string()).unwrap();
2996 let result: Cow<'_, str> = from_slice_borrowed(&bytes).unwrap();
2997 assert_eq!(&*result, "hello");
2998 assert!(
2999 matches!(result, Cow::Borrowed(_)),
3000 "from_slice_borrowed into Cow<str> should return Borrowed, got Owned",
3001 );
3002 }
3003
3004 #[test]
3009 fn round_trip_cow_str() {
3010 use std::borrow::Cow;
3011 let val: Cow<'_, str> = Cow::Owned("hello cow".to_string());
3012 let bytes = to_vec(&val).unwrap();
3013 let result: Cow<'_, str> = from_slice_borrowed(&bytes).unwrap();
3014 assert_eq!(result, val);
3015 }
3016
3017 #[test]
3018 fn round_trip_struct_with_cow_str() {
3019 use std::borrow::Cow;
3020 #[derive(Facet, Debug, PartialEq)]
3021 struct Msg<'a> {
3022 name: Cow<'a, str>,
3023 id: u32,
3024 }
3025
3026 let msg = Msg {
3027 name: Cow::Owned("test".to_string()),
3028 id: 42,
3029 };
3030 let bytes = to_vec(&msg).unwrap();
3031 let result: Msg<'_> = from_slice_borrowed(&bytes).unwrap();
3032 assert_eq!(result, msg);
3033 }
3034
3035 #[test]
3040 fn round_trip_u8() {
3041 round_trip(&0u8);
3042 round_trip(&255u8);
3043 }
3044
3045 #[test]
3046 fn round_trip_u16() {
3047 round_trip(&0u16);
3048 round_trip(&u16::MAX);
3049 }
3050
3051 #[test]
3052 fn round_trip_u64() {
3053 round_trip(&0u64);
3054 round_trip(&u64::MAX);
3055 }
3056
3057 #[test]
3058 fn round_trip_i8() {
3059 round_trip(&0i8);
3060 round_trip(&i8::MIN);
3061 round_trip(&i8::MAX);
3062 }
3063
3064 #[test]
3065 fn round_trip_i16() {
3066 round_trip(&0i16);
3067 round_trip(&i16::MIN);
3068 round_trip(&i16::MAX);
3069 }
3070
3071 #[test]
3072 fn round_trip_i32() {
3073 round_trip(&0i32);
3074 round_trip(&i32::MIN);
3075 round_trip(&i32::MAX);
3076 }
3077
3078 #[test]
3079 fn round_trip_i64() {
3080 round_trip(&0i64);
3081 round_trip(&i64::MIN);
3082 round_trip(&i64::MAX);
3083 }
3084
3085 #[test]
3090 fn round_trip_borrowed_slice_u32() {
3091 let data: Vec<u32> = vec![10, 20, 30];
3093 let bytes = to_vec(&data).unwrap();
3094 let result: Vec<u32> = from_slice_borrowed(&bytes).unwrap();
3095 assert_eq!(result, data);
3096 }
3097
3098 #[test]
3099 fn round_trip_borrowed_slice_in_struct() {
3100 #[derive(Facet, Debug, PartialEq)]
3101 struct Msg<'a> {
3102 data: &'a [u8],
3103 id: u32,
3104 }
3105
3106 let data = [1u8, 2, 3, 4, 5];
3107 let msg = Msg {
3108 data: &data,
3109 id: 42,
3110 };
3111 let bytes = to_vec(&msg).unwrap();
3112 let result: Msg<'_> = from_slice_borrowed(&bytes).unwrap();
3113 assert_eq!(result.id, 42);
3114 assert_eq!(result.data, &data);
3115 }
3116}