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`]. The only conduit shape is [`BareConduit`]: wraps a raw
5//! `Link` with postcard serialization. No reconnect, no reliability —
6//! reconnect was removed (StableConduit deleted) because the abstraction
7//! had no real users.
8
9mod bare_conduit;
10pub use bare_conduit::*;
11pub use vox_types::TransportMode;
12
13mod handshake;
14pub use handshake::*;
15
16mod into_conduit;
17pub use into_conduit::*;
18
19mod operation_store;
20pub use operation_store::*;
21
22mod transport_prologue;
23pub use transport_prologue::*;
24
25mod link_source;
26pub use link_source::*;
27
28#[cfg(not(target_arch = "wasm32"))]
29mod memory_link;
30#[cfg(not(target_arch = "wasm32"))]
31pub use memory_link::*;
32
33mod session;
34pub use session::*;
35
36mod driver;
37pub use driver::*;
38
39use vox_types::{Backing, SelfRef};
40
41/// Pre-built translation plan for deserializing the `Message` wire type.
42///
43/// Built once from the peer's schema (received during handshake) and our
44/// local schema. Stored in the conduit's Rx half and used for every
45/// incoming message.
46pub struct MessagePlan {
47    pub remote_schema_id: u64,
48    pub plan: vox_postcard::plan::TranslationPlan,
49    pub registry: vox_types::SchemaRegistry,
50}
51
52impl MessagePlan {
53    /// Build a message plan from the handshake result's schema exchange.
54    pub fn from_handshake(result: &vox_types::HandshakeResult) -> Result<Self, String> {
55        use vox_postcard::plan::{PlanInput, SchemaSet, build_plan};
56
57        if result.peer_schema.is_empty() || result.our_schema.is_empty() {
58            // No schemas exchanged — fall back to identity plan
59            let plan = vox_postcard::build_identity_plan(
60                <vox_types::Message<'static> as facet::Facet<'static>>::SHAPE,
61            );
62            return Ok(MessagePlan {
63                remote_schema_id: 0,
64                plan,
65                registry: vox_types::SchemaRegistry::new(),
66            });
67        }
68
69        let remote = SchemaSet::from_schemas(result.peer_schema.clone());
70        let local = SchemaSet::from_schemas(result.our_schema.clone());
71
72        let plan = build_plan(&PlanInput {
73            remote: &remote,
74            local: &local,
75        })
76        .map_err(|e| format!("failed to build message translation plan: {e}"))?;
77
78        Ok(MessagePlan {
79            remote_schema_id: remote.root.id.0,
80            plan,
81            registry: remote.registry,
82        })
83    }
84}
85
86/// Deserialize postcard-encoded `backing` bytes into `T` in place, returning
87/// a [`vox_types::SelfRef`] that keeps the backing storage alive for the
88/// value. Uses the identity plan; for plan-aware decoding, use
89/// [`deserialize_postcard_with_plan`].
90// r[impl zerocopy.framing.value]
91#[allow(dead_code)]
92pub(crate) fn deserialize_postcard<T: facet::Facet<'static>>(
93    backing: Backing,
94) -> Result<SelfRef<T>, vox_postcard::DeserializeError> {
95    let plan = vox_postcard::build_identity_plan(T::SHAPE);
96    let registry = vox_types::SchemaRegistry::new();
97    deserialize_postcard_with_plan(backing, &plan, &registry)
98}
99
100/// Deserialize postcard-encoded `backing` bytes into `T` using a pre-built
101/// translation plan and schema registry for the remote peer's type layout.
102// r[impl zerocopy.framing.value]
103#[allow(dead_code)]
104pub(crate) fn deserialize_postcard_with_plan<T: facet::Facet<'static>>(
105    backing: Backing,
106    plan: &vox_postcard::plan::TranslationPlan,
107    registry: &vox_types::SchemaRegistry,
108) -> Result<SelfRef<T>, vox_postcard::DeserializeError> {
109    #[cfg(not(target_arch = "wasm32"))]
110    {
111        SelfRef::try_new(backing, |bytes| {
112            vox_jit::global_runtime()
113                .try_decode_owned::<T>(bytes, 0, plan, registry)
114                .expect("JIT decode unavailable for type")
115        })
116    }
117    #[cfg(target_arch = "wasm32")]
118    {
119        SelfRef::try_new(backing, |bytes| {
120            vox_postcard::from_slice_with_plan::<T>(bytes, plan, registry)
121        })
122    }
123}
124
125/// Like [`deserialize_postcard`] but uses an already-resolved JIT decoder,
126/// skipping the global cache lookup. Used by conduits that resolved their
127/// decoder at construction.
128#[cfg(not(target_arch = "wasm32"))]
129pub(crate) fn deserialize_postcard_with_decoder<T: facet::Facet<'static>>(
130    backing: Backing,
131    decoder: &'static vox_jit::cache::CompiledDecoder,
132) -> Result<SelfRef<T>, vox_postcard::DeserializeError> {
133    let decode_fn = decoder
134        .owned_fn_ptr()
135        .expect("owned decode_fn missing on owned decoder");
136    SelfRef::try_new(backing, |bytes| {
137        vox_jit::decode_owned_with::<T>(decode_fn, bytes)
138    })
139}
140
141pub mod testing;
142
143#[cfg(test)]
144mod tests;