Skip to main content

vox_core/
lib.rs

1//! Core implementations for the vox connectivity layer.
2//!
3//! This crate provides concrete implementations of the traits defined in
4//! [`vox_types`]:
5//!
6//! - [`BareConduit`]: wraps a raw `Link` with postcard serialization.
7//!   No reconnect, no reliability. For localhost, SHM, testing.
8//! - `StableConduit` (TODO): wraps a Link + seq/ack/replay with
9//!   bytes-based replay buffer. Handles reconnect transparently.
10
11mod bare_conduit;
12pub use bare_conduit::*;
13pub use vox_types::TransportMode;
14
15mod handshake;
16pub use handshake::*;
17
18mod into_conduit;
19pub use into_conduit::*;
20
21mod operation_store;
22pub use operation_store::*;
23
24mod transport_prologue;
25pub use transport_prologue::*;
26
27#[cfg(not(target_arch = "wasm32"))]
28mod stable_conduit;
29#[cfg(not(target_arch = "wasm32"))]
30pub use stable_conduit::*;
31
32#[cfg(not(target_arch = "wasm32"))]
33mod memory_link;
34#[cfg(not(target_arch = "wasm32"))]
35pub use memory_link::*;
36
37mod session;
38pub use session::*;
39
40mod driver;
41pub use driver::*;
42
43use facet_reflect::Partial;
44use vox_types::{Backing, SelfRef};
45
46/// Pre-built translation plan for deserializing the `Message` wire type.
47///
48/// Built once from the peer's schema (received during handshake) and our
49/// local schema. Stored in the conduit's Rx half and used for every
50/// incoming message.
51pub struct MessagePlan {
52    pub plan: vox_postcard::plan::TranslationPlan,
53    pub registry: vox_types::SchemaRegistry,
54}
55
56impl MessagePlan {
57    /// Build a message plan from the handshake result's schema exchange.
58    pub fn from_handshake(result: &vox_types::HandshakeResult) -> Result<Self, String> {
59        use vox_postcard::plan::{PlanInput, SchemaSet, build_plan};
60
61        if result.peer_schema.is_empty() || result.our_schema.is_empty() {
62            // No schemas exchanged — fall back to identity plan
63            let plan = vox_postcard::build_identity_plan(
64                <vox_types::Message<'static> as facet::Facet<'static>>::SHAPE,
65            );
66            return Ok(MessagePlan {
67                plan,
68                registry: vox_types::SchemaRegistry::new(),
69            });
70        }
71
72        let remote = SchemaSet::from_schemas(result.peer_schema.clone());
73        let local = SchemaSet::from_schemas(result.our_schema.clone());
74
75        let plan = build_plan(&PlanInput {
76            remote: &remote,
77            local: &local,
78        })
79        .map_err(|e| format!("failed to build message translation plan: {e}"))?;
80
81        Ok(MessagePlan {
82            plan,
83            registry: remote.registry,
84        })
85    }
86}
87
88/// Deserialize postcard-encoded `backing` bytes into `T` in place, returning a
89/// [`vox_types::SelfRef`] that keeps the backing storage alive for the value.
90// r[impl zerocopy.framing.value]
91pub(crate) fn deserialize_postcard<T: facet::Facet<'static>>(
92    backing: Backing,
93) -> Result<SelfRef<T>, vox_postcard::DeserializeError> {
94    let plan = vox_postcard::build_identity_plan(T::SHAPE);
95    let registry = vox_types::SchemaRegistry::new();
96    deserialize_postcard_with_plan(backing, &plan, &registry)
97}
98
99/// Deserialize postcard-encoded `backing` bytes into `T` using a pre-built
100/// translation plan and schema registry for the remote peer's type layout.
101// r[impl zerocopy.framing.value]
102pub(crate) fn deserialize_postcard_with_plan<T: facet::Facet<'static>>(
103    backing: Backing,
104    plan: &vox_postcard::plan::TranslationPlan,
105    registry: &vox_types::SchemaRegistry,
106) -> Result<SelfRef<T>, vox_postcard::DeserializeError> {
107    // SAFETY: backing is heap-allocated with a stable address.
108    // The SelfRef::try_new contract guarantees value is dropped before backing.
109    SelfRef::try_new(backing, |bytes| {
110        let mut value = std::mem::MaybeUninit::<T>::uninit();
111        let ptr = facet_core::PtrUninit::from_maybe_uninit(&mut value);
112
113        // SAFETY: ptr points to valid, aligned, properly-sized memory for T.
114        #[allow(unsafe_code)]
115        let partial: Partial<'_, true> = unsafe { Partial::from_raw_with_shape(ptr, T::SHAPE) }
116            .map_err(|e| vox_postcard::DeserializeError::ReflectError(e.to_string()))?;
117
118        let partial = vox_postcard::deserialize_into(partial, bytes, plan, registry)?;
119
120        partial
121            .finish_in_place()
122            .map_err(|e| vox_postcard::DeserializeError::ReflectError(e.to_string()))?;
123
124        // SAFETY: finish_in_place succeeded, so value is fully initialized.
125        #[allow(unsafe_code)]
126        Ok(unsafe { value.assume_init() })
127    })
128}
129
130pub mod testing;
131
132#[cfg(test)]
133mod tests;