msrt 0.1.5

Portable MSRT protocol implementation.
Documentation
use msrt::{
    Engine,
    core::PacketType,
    engine::{EnginePoll, MessageEvent, ReceiveReport},
};

const TX_BUF_BYTES: usize = 128;

#[test]
fn default_message_is_acked_without_adapter() {
    let mut host = Engine::default();
    let mut mcu = Engine::default();

    host.send(b"host ping").expect("queue host message");

    let host_write = next_write(&mut host);
    assert!(matches!(
        mcu.receive(host_write.as_bytes()),
        ReceiveReport::Packet { .. }
    ));

    let ack = next_write(&mut mcu);
    assert!(matches!(
        host.receive(ack.as_bytes()),
        ReceiveReport::Ack { .. }
    ));

    let mcu_message = next_message(&mut mcu);
    assert_eq!(mcu_message.packet_type, PacketType::Data);
    assert_eq!(mcu_message.as_bytes(), b"host ping");

    assert_no_send_failed(&mut host);
}

#[test]
fn mcu_can_debug_after_receiving_default_message_without_adapter() {
    let mut host = Engine::default();
    let mut mcu = Engine::default();

    host.send(b"host ping").expect("queue host message");
    deliver_next_write(&mut host, &mut mcu);

    deliver_next_write(&mut mcu, &mut host);

    let mcu_message = next_message(&mut mcu);
    assert_eq!(mcu_message.packet_type, PacketType::Data);
    assert_eq!(mcu_message.as_bytes(), b"host ping");

    mcu.send_log(b"mcu received host message")
        .expect("queue mcu debug");

    deliver_next_write(&mut mcu, &mut host);

    let debug = next_message(&mut host);
    assert_eq!(debug.packet_type, PacketType::Log);
    assert_eq!(debug.as_bytes(), b"mcu received host message");

    assert_no_send_failed(&mut host);
    assert_no_send_failed(&mut mcu);
}

#[test]
fn split_serial_reads_still_ack_and_debug_without_adapter() {
    let mut host = Engine::default();
    let mut mcu = Engine::default();

    host.send(b"host ping").expect("queue host message");

    let host_write = next_write(&mut host);
    for byte in host_write.as_bytes() {
        let _ = mcu.receive(core::slice::from_ref(byte));
    }

    let ack = next_write(&mut mcu);
    for byte in ack.as_bytes() {
        let _ = host.receive(core::slice::from_ref(byte));
    }

    let mcu_message = next_message(&mut mcu);
    assert_eq!(mcu_message.as_bytes(), b"host ping");

    mcu.send_log(b"mcu received host message")
        .expect("queue mcu debug");
    let debug_write = next_write(&mut mcu);
    for byte in debug_write.as_bytes() {
        let _ = host.receive(core::slice::from_ref(byte));
    }

    let debug = next_message(&mut host);
    assert_eq!(debug.packet_type, PacketType::Log);
    assert_eq!(debug.as_bytes(), b"mcu received host message");

    assert_no_send_failed(&mut host);
    assert_no_send_failed(&mut mcu);
}

#[test]
fn poll_copies_transmit_bytes_into_external_buffer() {
    let mut host = Engine::default();
    let mut mcu = Engine::default();
    let mut host_tx = [0; TX_BUF_BYTES];
    let mut mcu_tx = [0; TX_BUF_BYTES];

    host.send(b"host ping").expect("queue host message");

    let bytes = next_transmit(&mut host, &mut host_tx);
    assert!(matches!(mcu.receive(bytes), ReceiveReport::Packet { .. }));

    let bytes = next_transmit(&mut mcu, &mut mcu_tx);
    assert!(matches!(host.receive(bytes), ReceiveReport::Ack { .. }));

    let message = next_polled_message(&mut mcu, &mut mcu_tx);
    assert_eq!(message.packet_type, PacketType::Data);
    assert_eq!(message.as_bytes(), b"host ping");

    mcu.send_log(b"mcu received host message")
        .expect("queue mcu debug");

    let bytes = next_transmit(&mut mcu, &mut mcu_tx);
    assert!(matches!(host.receive(bytes), ReceiveReport::Packet { .. }));

    let message = next_polled_message(&mut host, &mut host_tx);
    assert_eq!(message.packet_type, PacketType::Log);
    assert_eq!(message.as_bytes(), b"mcu received host message");
}

fn deliver_next_write(src: &mut Engine, dst: &mut Engine) {
    let write = next_write(src);
    match dst.receive(write.as_bytes()) {
        ReceiveReport::Packet { .. }
        | ReceiveReport::Ack { .. }
        | ReceiveReport::Ping
        | ReceiveReport::Pong => {}
        other => panic!("unexpected receive report: {other:?}"),
    }
}

fn next_write(engine: &mut Engine) -> TestWrite {
    let mut tx_buf = [0; TX_BUF_BYTES];
    let bytes = next_transmit(engine, &mut tx_buf);
    TestWrite::from_bytes(bytes)
}

fn next_message(engine: &mut Engine) -> MessageEvent {
    let mut tx_buf = [0; TX_BUF_BYTES];
    next_polled_message(engine, &mut tx_buf)
}

fn assert_no_send_failed(engine: &mut Engine) {
    let mut tx_buf = [0; TX_BUF_BYTES];

    loop {
        match engine.poll(0, &mut tx_buf).expect("poll engine") {
            EnginePoll::SendFailed(failed) => panic!("unexpected send failure: {failed:?}"),
            EnginePoll::Idle => break,
            EnginePoll::Transmit { .. } | EnginePoll::Message(_) => {}
        }
    }
}

fn next_transmit<'a>(engine: &mut Engine, tx_buf: &'a mut [u8]) -> &'a [u8] {
    match engine.poll(0, tx_buf).expect("poll engine") {
        EnginePoll::Transmit { bytes, .. } => bytes,
        other => panic!("engine should produce transmit bytes, got {other:?}"),
    }
}

fn next_polled_message(engine: &mut Engine, tx_buf: &mut [u8]) -> MessageEvent {
    loop {
        match engine.poll(0, tx_buf).expect("poll engine") {
            EnginePoll::Message(message) => return message,
            EnginePoll::Transmit { .. } => {}
            EnginePoll::SendFailed(failed) => panic!("unexpected send failure: {failed:?}"),
            EnginePoll::Idle => panic!("engine should produce a message event"),
        }
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct TestWrite {
    bytes: [u8; TX_BUF_BYTES],
    len: usize,
}

impl TestWrite {
    fn from_bytes(bytes: &[u8]) -> Self {
        let mut stored = [0; TX_BUF_BYTES];
        stored[..bytes.len()].copy_from_slice(bytes);

        Self {
            bytes: stored,
            len: bytes.len(),
        }
    }

    fn as_bytes(&self) -> &[u8] {
        &self.bytes[..self.len]
    }
}