use alloc::vec::Vec;
use core::sync::atomic::{AtomicU16, Ordering};
use crate::{
protocol::r#struct::Writable,
tcp,
tcp::{Header, UnitId},
};
#[must_use]
#[derive(Default)]
pub struct Encoder(AtomicU16);
impl Encoder {
pub const fn with_next_transaction_id(transaction_id: u16) -> Self {
Self(AtomicU16::new(transaction_id))
}
pub fn prepare(
&self,
unit_id: UnitId,
request: &impl Writable,
) -> Result<(Vec<u8>, u16), tcp::Error> {
let transaction_id = self.0.fetch_add(1, Ordering::Relaxed);
let frame_bytes = {
let payload_bytes = request.to_bytes()?;
let mut frame_bytes = {
let length = u16::try_from(payload_bytes.len() + 1)
.map_err(|_| tcp::Error::PayloadSizeExceeded(payload_bytes.len()))?;
Header::builder()
.unit_id(unit_id)
.transaction_id(transaction_id)
.length(length)
.build()
.to_bytes()?
};
frame_bytes.extend(payload_bytes);
frame_bytes
};
Ok((frame_bytes, transaction_id))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn send_example_ok() {
let encoder = Encoder::with_next_transaction_id(0x1501);
let (frame, transaction_id) =
encoder.prepare(UnitId::NonSignificant, &[0x03_u8, 0x00, 0x04, 0x00, 0x01]).unwrap();
assert_eq!(transaction_id, 0x1501);
assert_eq!(encoder.0.into_inner(), 0x1502);
assert_eq!(
frame,
[
0x15, 0x01, 0x00, 0x00, 0x00, 0x06, 0xFF, 0x03, 0x00, 0x04, 0x00, 0x01, ]
);
}
}