1mod 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
33#[cfg(not(target_arch = "wasm32"))]
34use std::panic::AssertUnwindSafe;
35
36mod session;
37pub use session::*;
38
39mod driver;
40pub use driver::*;
41
42use vox_types::{Backing, SelfRef};
43
44pub struct MessagePlan {
50 pub remote_schema_id: u64,
51 pub plan: vox_postcard::plan::TranslationPlan,
52 pub registry: vox_types::SchemaRegistry,
53}
54
55impl MessagePlan {
56 pub fn from_handshake(result: &vox_types::HandshakeResult) -> Result<Self, String> {
58 use vox_postcard::plan::{PlanInput, SchemaSet, build_plan};
59
60 if result.peer_schema.is_empty() || result.our_schema.is_empty() {
61 let plan = vox_postcard::build_identity_plan(
63 <vox_types::Message<'static> as facet::Facet<'static>>::SHAPE,
64 );
65 return Ok(MessagePlan {
66 remote_schema_id: 0,
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 remote_schema_id: remote.root.id.0,
83 plan,
84 registry: remote.registry,
85 })
86 }
87}
88
89#[allow(dead_code)]
95pub(crate) fn deserialize_postcard<T: facet::Facet<'static>>(
96 backing: Backing,
97) -> Result<SelfRef<T>, vox_postcard::DeserializeError> {
98 let plan = vox_postcard::build_identity_plan(T::SHAPE);
99 let registry = vox_types::SchemaRegistry::new();
100 deserialize_postcard_with_plan(backing, &plan, ®istry)
101}
102
103#[allow(dead_code)]
107pub(crate) fn deserialize_postcard_with_plan<T: facet::Facet<'static>>(
108 backing: Backing,
109 plan: &vox_postcard::plan::TranslationPlan,
110 registry: &vox_types::SchemaRegistry,
111) -> Result<SelfRef<T>, vox_postcard::DeserializeError> {
112 #[cfg(not(target_arch = "wasm32"))]
113 {
114 SelfRef::try_new(backing, |bytes| {
115 match std::panic::catch_unwind(AssertUnwindSafe(|| {
116 vox_jit::global_runtime().try_decode_owned::<T>(bytes, 0, plan, registry)
117 })) {
118 Ok(Some(result)) => result,
119 Ok(None) => vox_postcard::from_slice_with_plan::<T>(bytes, plan, registry),
120 Err(payload) => {
121 tracing::warn!(
122 shape = %T::SHAPE,
123 panic = %panic_payload_message(&payload),
124 "vox message JIT decode panicked; falling back"
125 );
126 vox_postcard::from_slice_with_plan::<T>(bytes, plan, registry)
127 }
128 }
129 })
130 }
131 #[cfg(target_arch = "wasm32")]
132 {
133 SelfRef::try_new(backing, |bytes| {
134 vox_postcard::from_slice_with_plan::<T>(bytes, plan, registry)
135 })
136 }
137}
138
139#[cfg(not(target_arch = "wasm32"))]
143pub(crate) fn deserialize_postcard_with_decoder<T: facet::Facet<'static>>(
144 backing: Backing,
145 decoder: Option<&'static vox_jit::cache::CompiledDecoder>,
146 plan: &vox_postcard::plan::TranslationPlan,
147 registry: &vox_types::SchemaRegistry,
148) -> Result<SelfRef<T>, vox_postcard::DeserializeError> {
149 SelfRef::try_new(backing, |bytes| {
150 let Some(decoder) = decoder else {
151 return vox_postcard::from_slice_with_plan::<T>(bytes, plan, registry);
152 };
153 let Some(decode_fn) = decoder.owned_fn_ptr() else {
154 tracing::warn!(
155 shape = %T::SHAPE,
156 "vox message JIT decoder missing function pointer; falling back"
157 );
158 return vox_postcard::from_slice_with_plan::<T>(bytes, plan, registry);
159 };
160 match std::panic::catch_unwind(AssertUnwindSafe(|| {
161 vox_jit::decode_owned_with::<T>(decode_fn, bytes)
162 })) {
163 Ok(result) => result,
164 Err(payload) => {
165 tracing::warn!(
166 shape = %T::SHAPE,
167 panic = %panic_payload_message(&payload),
168 "vox message JIT decode panicked; falling back"
169 );
170 vox_postcard::from_slice_with_plan::<T>(bytes, plan, registry)
171 }
172 }
173 })
174}
175
176#[cfg(not(target_arch = "wasm32"))]
177fn panic_payload_message(payload: &(dyn std::any::Any + Send)) -> String {
178 if let Some(message) = payload.downcast_ref::<&'static str>() {
179 (*message).to_owned()
180 } else if let Some(message) = payload.downcast_ref::<String>() {
181 message.clone()
182 } else {
183 "non-string panic payload".to_owned()
184 }
185}
186
187pub mod testing;
188
189#[cfg(test)]
190mod tests;