Skip to main content

vox_postcard/
lib.rs

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    /// Serialize `value` via both `to_vec` and `peek_to_scatter_plan`,
30    /// assert the bytes are identical, then deserialize via `from_slice_borrowed`
31    /// and assert the result equals the original.
32    fn round_trip<T>(value: &T)
33    where
34        for<'de> T: Facet<'de> + std::fmt::Debug + PartialEq,
35    {
36        // Path 1: direct serialization
37        let direct_bytes = to_vec(value).unwrap();
38
39        // Path 2: scatter plan
40        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        // Both paths must produce identical bytes
46        assert_eq!(
47            direct_bytes, scatter_bytes,
48            "to_vec and scatter plan produced different bytes for {:?}",
49            value
50        );
51
52        // Also test to_io_slices produces the same content
53        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        // Also verify staging() is accessible
65        let _ = plan.staging();
66
67        // Deserialize and assert equality
68        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        // Verify our encoding matches facet-postcard exactly
189        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        // Deserialize theirs with ours
244        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        // Verify output matches to_vec
310        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        // Small borrowed fields (< 4K) are copied into staging, not kept as references.
316        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        // Everything merges into one staged segment.
326        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        // Verify output matches to_vec
351        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        // Large blob (>= 4K) should be a Reference segment (zero-copy).
357        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        // staged(id + data len), ref(data) = 2 segments total.
364        assert_eq!(segments.len(), 2);
365    }
366
367    #[test]
368    fn scatter_staged_segments_coalesce() {
369        // A struct with no borrowed fields — all structural bytes should merge
370        // into a single staged segment.
371        #[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        // Verify output still matches to_vec
401        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    // ---- Translation plan tests ----
425
426    #[derive(Debug)]
427    struct PlanResult {
428        plan: TranslationPlan,
429        remote: SchemaSet,
430        #[allow(dead_code)]
431        local: SchemaSet,
432    }
433
434    /// Helper: build a translation plan from remote and local shapes.
435    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    // r[verify schema.translation.skip-unknown]
504    #[test]
505    fn translation_remote_has_extra_field() {
506        // Remote has fields [x, y, z], local only has [x, z]
507        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        // Serialize with remote type
529        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        // Deserialize with plan into local type — y should be skipped
537        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    // r[verify schema.translation.fill-defaults]
543    #[test]
544    fn translation_remote_missing_field_with_default() {
545        // Remote has [x], local has [x, y] where y has a default
546        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    // r[verify schema.translation.field-matching]
575    // r[verify schema.errors.early-detection]
576    // r[verify schema.errors.missing-required]
577    #[test]
578    fn translation_missing_required_field_errors() {
579        // Remote has [x], local has [x, y] where y is required (no default)
580        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        // Verify error message includes useful context
608        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    // r[verify schema.translation.reorder]
617    #[test]
618    fn translation_field_reorder() {
619        // Remote has [b, a], local has [a, b] — different field order
620        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    // r[verify schema.translation.skip-unknown]
658    #[test]
659    fn translation_skip_complex_field() {
660        // Remote has an extra Vec<String> field that local doesn't have
661        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    // r[verify schema.translation.serialization-unchanged]
701    // r[verify schema.translation.type-compat]
702    #[test]
703    fn translation_identity_plan_matches_direct() {
704        // Identity plan should produce the same result as from_slice
705        #[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        // Remote: [a, b, c] → Local: [c, d, a] (b removed, d added with default, c/a reordered)
725        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, // default
762                a: 42,
763            }
764        );
765    }
766
767    // r[verify schema.errors.content]
768    #[test]
769    fn translation_error_includes_context() {
770        // Verify errors include remote type ID, local type name, field name
771        mod remote {
772            use facet::Facet;
773            #[derive(Facet, Debug)]
774            pub struct Outer {
775                pub inner: u32,
776            }
777        }
778
779        // local::Outer has a required field 'missing' that remote doesn't have
780        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        // Should include local type name
793        assert!(
794            msg.contains("Outer"),
795            "error should include local type name: {msg}"
796        );
797        // Should include missing field name
798        assert!(
799            msg.contains("missing"),
800            "error should include field name: {msg}"
801        );
802        // Should include "required field" and "missing"
803        assert!(
804            msg.contains("required field") && msg.contains("missing"),
805            "error should describe the incompatibility: {msg}"
806        );
807        // Error should have useful context
808        assert!(
809            !format!("{err}").is_empty(),
810            "error Display should produce output"
811        );
812    }
813
814    // r[verify schema.errors.type-mismatch]
815    #[test]
816    fn translation_error_kind_mismatch() {
817        // Remote is a struct, but we try to translate into a primitive shape
818        #[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    // r[verify schema.translation.enum]
830    // r[verify schema.translation.enum.missing-variant]
831    #[test]
832    fn translation_enum_variant_added() {
833        // Remote has [A, B], local has [A, B, C] — C never sent, that's fine
834        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    // r[verify schema.translation.enum.unknown-variant]
863    // r[verify schema.errors.unknown-variant-runtime]
864    #[test]
865    fn translation_enum_unknown_variant_errors_at_runtime() {
866        // Remote has [A, B, C], local has [A, B] — receiving C should error
867        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        // Sending Start/Stop works
891        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        // Sending Restart (index 2) should fail at runtime
896        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        // A minimal opaque adapter for testing.
1035        #[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        // A struct with an opaque field, mimicking RequestCall.
1074        #[derive(Debug, Facet)]
1075        struct TestCall<'a> {
1076            id: u32,
1077            payload: TestPayload<'a>,
1078        }
1079
1080        // 1. Serialize with Outgoing payload (non-passthrough)
1081        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        // Opaque values use u32le length prefix (not varint), so our encoding
1094        // intentionally differs from facet-postcard here.
1095
1096        // 2. Deserialize back (payload becomes Incoming)
1097        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        // 3. Re-serialize with Incoming payload (passthrough)
1106        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        // Test with Vec<u8> payload (like the blob stress test)
1166        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        // Step 1: Serialize
1177        let encoded = to_vec(&call).unwrap();
1178        eprintln!(
1179            "encoded ({} bytes): {:02x?}",
1180            encoded.len(),
1181            &encoded[..encoded.len().min(40)]
1182        );
1183
1184        // Step 2: Deserialize back (payload becomes Incoming)
1185        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        // Step 3: Deserialize the payload as Vec<u8>
1197        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        // Step 4: Re-serialize with Incoming payload (passthrough)
1202        let reserialized = to_vec(&round_tripped).unwrap();
1203        assert_eq!(reserialized, encoded, "re-serialized must match original");
1204
1205        // Step 5: Full round-trip again
1206        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    // ========================================================================
1219    // skip_value tests — exercise decode::skip_value for every SchemaKind
1220    // ========================================================================
1221
1222    #[test]
1223    fn skip_struct_field() {
1224        // Remote: { id: u32, extra: Point, name: String }
1225        // Local:  { id: u32, name: String }
1226        // The Point struct must be skipped entirely.
1227        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        // Test with Some
1335        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        // Test with None
1352        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        // Skip a newtype variant
1513        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        // Skip a struct variant
1529        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        // Skip a unit variant
1545        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    // ========================================================================
1562    // Tuple plan tests
1563    // ========================================================================
1564
1565    #[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    // ========================================================================
1606    // Name mismatch test
1607    // ========================================================================
1608
1609    #[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    // ========================================================================
1636    // from_schemas constructor test
1637    // ========================================================================
1638
1639    #[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    // ========================================================================
1652    // Decode error path tests
1653    // ========================================================================
1654
1655    #[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        // Option is encoded as 0x00 (None) or 0x01 (Some). 0x02 is invalid.
1664        // Encode: struct with one Option<u32> field
1665        // Manually craft bytes: option tag = 0x02
1666        let result: Result<Option<u32>, _> = from_slice(&[0x02]);
1667        assert!(result.is_err());
1668    }
1669
1670    #[test]
1671    fn decode_invalid_bool() {
1672        // Bool is 0x00 or 0x01. 0x02 is invalid.
1673        let result: Result<bool, _> = from_slice(&[0x02]);
1674        assert!(result.is_err());
1675    }
1676
1677    // ========================================================================
1678    // deserialize_into with planned deserialization
1679    // ========================================================================
1680
1681    #[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        // Use the deserialize_into entry point directly with a Partial
1704        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    // ========================================================================
1713    // Enum variant mapping deserialization
1714    // ========================================================================
1715
1716    #[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    // ========================================================================
1776    // Round-trip tests for structural types (Array, Map, Set)
1777    // ========================================================================
1778
1779    #[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    // ========================================================================
1809    // Planned deserialization with container-type fields
1810    // ========================================================================
1811
1812    #[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        // Box<T> is a pointer type — tests the Pointer deserialization branch
1883        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    // ========================================================================
1920    // Transparent wrapper deserialization
1921    // ========================================================================
1922
1923    #[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        // Tests that planned deserialization handles nested structs
1935        // through the transparent wrapper / inner deserialization path
1936        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    // ========================================================================
1983    // 128-bit integer round trips
1984    // ========================================================================
1985
1986    #[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    // ========================================================================
2002    // Skip enum with tuple variant payload
2003    // ========================================================================
2004
2005    #[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        // Skip a tuple variant
2035        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        // Skip a newtype variant
2051        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    // ========================================================================
2068    // Skip unit and char primitive types
2069    // ========================================================================
2070
2071    #[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    // ========================================================================
2156    // Skip u128/i128 fields
2157    // ========================================================================
2158
2159    #[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    // ========================================================================
2197    // Borrowed deserialization with plan
2198    // ========================================================================
2199
2200    #[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    // ========================================================================
2230    // Deserialize Set type
2231    // ========================================================================
2232
2233    #[test]
2234    fn round_trip_hashset() {
2235        use std::collections::HashSet;
2236        // Single element to avoid non-deterministic iteration order
2237        let mut val = HashSet::new();
2238        val.insert(42u32);
2239        round_trip(&val);
2240    }
2241
2242    // ========================================================================
2243    // Skip f32 and i128 fields (exercises specific skip_primitive branches)
2244    // ========================================================================
2245
2246    #[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    // ========================================================================
2322    // Truncated input (EOF errors)
2323    // ========================================================================
2324
2325    #[test]
2326    fn truncated_struct_input_errors() {
2327        #[derive(Facet, Debug)]
2328        struct Msg {
2329            id: u32,
2330            name: String,
2331        }
2332
2333        // Serialize a valid struct, then truncate the bytes
2334        let bytes = to_vec(&Msg {
2335            id: 42,
2336            name: "hello world".into(),
2337        })
2338        .unwrap();
2339        // Truncate to just the first byte (the varint for id)
2340        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        // Serialize a Vec, truncate mid-stream
2348        let bytes = to_vec(&vec![1u32, 2, 3, 4, 5]).unwrap();
2349        let truncated = &bytes[..2]; // length prefix + partial data
2350        let result: Result<Vec<u32>, _> = from_slice(truncated);
2351        assert!(result.is_err(), "truncated list should error");
2352    }
2353
2354    // ========================================================================
2355    // Round trip f32 (exercises f32-specific serialization)
2356    // ========================================================================
2357
2358    #[test]
2359    fn round_trip_f32() {
2360        round_trip(
2361            #[allow(clippy::approx_constant)]
2362            &3.14f32,
2363        );
2364    }
2365
2366    // ========================================================================
2367    // Slice-like types
2368    // ========================================================================
2369
2370    #[test]
2371    fn round_trip_boxed_str() {
2372        round_trip(&Box::<str>::from("hello"));
2373    }
2374
2375    // ========================================================================
2376    // Proxy type deserialization
2377    // ========================================================================
2378
2379    #[test]
2380    fn proxy_type_round_trip() {
2381        // Proxied<T> is serialized as its proxy type (u32), then convert_in
2382        // builds the actual value. This exercises the proxy deserialization path.
2383        #[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        // Serialize the proxy value (u32)
2402        let bytes = to_vec(&42u32).unwrap();
2403        // Deserialize as Proxied — should go through proxy path
2404        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        // Test that a struct containing a proxied field round-trips correctly.
2411        #[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    // ========================================================================
2445    // Borrowed slice round-trips (exercises scatter Reference segments)
2446    // ========================================================================
2447
2448    #[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        // Large &[u8] (>4096) — should become a Reference segment in scatter
2465        #[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        // With large data, we should have a Reference segment
2479        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        // Also verify io_slices
2493        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        // Large string (>4096) to hit the Reference threshold in scatter
2508        let text = "x".repeat(8192);
2509        round_trip(&text);
2510    }
2511
2512    // ========================================================================
2513    // Error Display coverage
2514    // ========================================================================
2515
2516    #[test]
2517    fn deserialize_error_display_coverage() {
2518        use error::DeserializeError;
2519
2520        // Exercise Display for each DeserializeError variant
2521        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        // Also test with path prefix
2657        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    // ========================================================================
2667    // Failing tests: translation plans don't recurse into containers
2668    // ========================================================================
2669
2670    #[test]
2671    fn translation_through_vec_skips_extra_field() {
2672        // Remote B has an extra field that local B doesn't have.
2673        // The translation is inside a Vec — the plan must recurse into
2674        // the Vec's element type to produce the skip.
2675        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        // Translation change is inside an Option<B>.
2729        // A trailing field `id` after the Option exposes cursor misalignment.
2730        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        // Translation change is inside a Result<B, String>.
2778        // Trailing field `id` exposes cursor misalignment.
2779        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        // Translation change is in the value type of a HashMap.
2827        // Trailing field `id` exposes cursor misalignment.
2828        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        // Change is 3 levels deep: A -> Vec<B> -> C (C has extra field).
2883        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    // ========================================================================
2937    // Memory safety: borrowed vs owned deserialization
2938    // ========================================================================
2939
2940    #[test]
2941    fn non_borrowed_into_borrowed_str_must_fail() {
2942        // Deserializing into a type with &str using from_slice (BORROW=false)
2943        // must fail — the returned value would contain a dangling reference
2944        // to the input bytes which are not guaranteed to outlive the value.
2945        #[derive(Facet, Debug, PartialEq)]
2946        struct Msg<'a> {
2947            name: &'a str,
2948        }
2949
2950        let bytes = to_vec(&"hello".to_string()).unwrap();
2951        // from_slice uses BORROW=false — it cannot produce borrowed references
2952        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        // from_slice_borrowed (BORROW=true) into &str must succeed —
2963        // the returned value borrows directly from the input.
2964        #[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        // from_slice (BORROW=false) into Cow<str> must succeed and return
2977        // Cow::Owned — it can't borrow, so it clones into an owned String.
2978        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        // from_slice_borrowed (BORROW=true) into Cow<str> must succeed and
2992        // return Cow::Borrowed — it can borrow directly from the input.
2993        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    // ========================================================================
3005    // Cow<str> round-trip
3006    // ========================================================================
3007
3008    #[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    // ========================================================================
3036    // All integer types round-trip
3037    // ========================================================================
3038
3039    #[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    // ========================================================================
3086    // Slice round-trip
3087    // ========================================================================
3088
3089    #[test]
3090    fn round_trip_borrowed_slice_u32() {
3091        // &[u32] should serialize like Vec<u32> and deserialize back
3092        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}