robotrt-middleware-core 0.1.0-beta.2

RobotRT modular robotics runtime and middleware components.
Documentation
use core_types::{ErrorCode, ErrorDomain, RtError};
use data_model::{ControlEnvelope, DataEnvelope, PacketHeader, SchemaId, SchemaVersion};

const CONTROL_SEPARATOR: u8 = 0;

pub trait SerializationManager {
    fn encode_control(&self, envelope: &ControlEnvelope) -> Result<Vec<u8>, RtError>;
    fn decode_control(&self, bytes: &[u8]) -> Result<ControlEnvelope, RtError>;
    fn encode_data(&self, envelope: &DataEnvelope) -> Result<Vec<u8>, RtError>;
}

#[derive(Default)]
pub struct SimpleSerializationManager;

impl SerializationManager for SimpleSerializationManager {
    fn encode_control(&self, envelope: &ControlEnvelope) -> Result<Vec<u8>, RtError> {
        let mut bytes = envelope.label.as_bytes().to_vec();
        bytes.push(CONTROL_SEPARATOR);
        bytes.extend_from_slice(&envelope.payload);
        Ok(bytes)
    }

    fn decode_control(&self, bytes: &[u8]) -> Result<ControlEnvelope, RtError> {
        let Some(separator) = bytes.iter().position(|b| *b == CONTROL_SEPARATOR) else {
            return Err(RtError::new(
                ErrorCode::CodecError,
                ErrorDomain::DataModel,
                false,
                "invalid control payload: missing separator",
            ));
        };

        let label = String::from_utf8(bytes[..separator].to_vec()).map_err(|_| {
            RtError::new(
                ErrorCode::CodecError,
                ErrorDomain::DataModel,
                false,
                "invalid control payload: label is not utf8",
            )
        })?;

        let payload = bytes[separator + 1..].to_vec();

        Ok(ControlEnvelope {
            header: PacketHeader {
                version: 1,
                domain: core_types::TransportDomain::Local,
                session_id: None,
                stream_id: None,
                sequence: 0,
                ack: None,
                timestamp: core_types::Timestamp::now(),
                schema_id: SchemaId::new("decoded.control"),
                schema_version: SchemaVersion(1),
            },
            label,
            payload,
        })
    }

    fn encode_data(&self, envelope: &DataEnvelope) -> Result<Vec<u8>, RtError> {
        match &envelope.payload {
            data_model::DataPayload::Inline(buf) => Ok(buf.clone()),
            data_model::DataPayload::External(external) => {
                let marker = format!(
                    "ext:{}:{}:{}",
                    external.buffer_id.0, external.offset, external.len
                );
                Ok(marker.into_bytes())
            }
        }
    }
}