Skip to main content

recon_sim/
codec.rs

1//! The wire boundary, exercised on demand.
2//!
3//! The simulator moves typed values between processes, so runs are fast and a codec defect
4//! cannot masquerade as a protocol defect. Encoding is still worth checking — just not on every
5//! delivery of every run — so it is available as an opt-in mode.
6
7use recon_core::error::CodecError;
8use serde::{Serialize, de::DeserializeOwned};
9
10/// Encode a value the way a real driver would: once, at the boundary, with no intermediate
11/// representation.
12pub fn encode<T: Serialize>(value: &T) -> Result<Vec<u8>, CodecError> {
13    bincode::serde::encode_to_vec(value, bincode::config::standard()).map_err(|e| {
14        CodecError::Encode { type_name: core::any::type_name::<T>(), source: Box::new(e) }
15    })
16}
17
18/// Decode a value produced by [`encode`].
19pub fn decode<T: DeserializeOwned>(bytes: &[u8]) -> Result<T, CodecError> {
20    bincode::serde::decode_from_slice(bytes, bincode::config::standard()).map(|(v, _)| v).map_err(
21        |e| CodecError::Decode { type_name: core::any::type_name::<T>(), source: Box::new(e) },
22    )
23}
24
25/// Encode then decode, confirming the value survives unchanged.
26pub fn round_trip<T>(value: &T) -> Result<T, CodecError>
27where
28    T: Serialize + DeserializeOwned + PartialEq,
29{
30    let bytes = encode(value)?;
31    let back: T = decode(&bytes)?;
32    if back != *value {
33        return Err(CodecError::RoundTripMismatch { type_name: core::any::type_name::<T>() });
34    }
35    Ok(back)
36}