volli-core 0.1.11

Shared types for volli
Documentation
use crate::Message;

/// Error returned when decoding a [`Message`] fails.
#[derive(Debug)]
pub enum DecodeError {
    Bincode(bincode::Error),
    Json(serde_json::Error),
}

impl core::fmt::Display for DecodeError {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            DecodeError::Bincode(e) => write!(f, "bincode decode error: {e}"),
            DecodeError::Json(e) => write!(f, "json decode error: {e}"),
        }
    }
}

impl std::error::Error for DecodeError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            DecodeError::Bincode(e) => Some(e),
            DecodeError::Json(e) => Some(e),
        }
    }
}

/// Codec trait for serialising and deserialising [`Message`]s.
pub trait Codec {
    /// Encode a high-level [`Message`] into a byte-vector.
    fn encode(msg: &Message) -> Vec<u8>;

    /// Decode a byte-slice into a [`Message`].
    ///
    /// # Errors
    /// Returns [`DecodeError`] if the bytes are invalid for this codec.
    fn decode(buf: &[u8]) -> Result<Message, DecodeError>;
}

/// Binary [`Codec`] based on `bincode`.
pub struct BincodeCodec;

impl Codec for BincodeCodec {
    fn encode(msg: &Message) -> Vec<u8> {
        bincode::serialize(msg).expect("bincode serialization should succeed")
    }

    fn decode(buf: &[u8]) -> Result<Message, DecodeError> {
        bincode::deserialize(buf).map_err(DecodeError::Bincode)
    }
}

/// JSON [`Codec`] useful for debugging and testing.
pub struct JsonCodec;

impl Codec for JsonCodec {
    fn encode(msg: &Message) -> Vec<u8> {
        serde_json::to_vec(msg).expect("json serialization should succeed")
    }

    fn decode(buf: &[u8]) -> Result<Message, DecodeError> {
        serde_json::from_slice(buf).map_err(DecodeError::Json)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn bincode_roundtrip() {
        let msg = Message::Ping { version: 1 };
        let bytes = BincodeCodec::encode(&msg);
        let decoded = BincodeCodec::decode(&bytes).unwrap();
        assert!(matches!(decoded, Message::Ping { .. }));
    }

    #[test]
    fn json_roundtrip() {
        let msg = Message::Pong {
            mac: "aa".into(),
            version: 1,
        };
        let bytes = JsonCodec::encode(&msg);
        let decoded = JsonCodec::decode(&bytes).unwrap();
        assert!(matches!(decoded, Message::Pong { .. }));
    }
}