byteable/
io.rs

1//! Synchronous I/O traits for reading and writing byteable types.
2//!
3//! This module provides extension traits for `std::io::Read` and `std::io::Write`
4//! that allow reading and writing types implementing the `Byteable` trait.
5
6use crate::byte_array::ByteableByteArray;
7use crate::byteable::Byteable;
8use std::io::{Read, Write};
9
10/// Extends `std::io::Read` with a method to read a `Byteable` type.
11pub trait ReadByteable: Read {
12    /// Reads one `Byteable` element from the reader.
13    ///
14    /// This method will create a zero-filled byte array, read enough bytes
15    /// from the underlying reader to fill it, and then convert the byte
16    /// array into the specified `Byteable` type.
17    fn read_one<T: Byteable>(&mut self) -> std::io::Result<T> {
18        let mut e = T::ByteArray::create_zeroed();
19        self.read_exact(e.as_byteslice_mut())?;
20        Ok(T::from_bytearray(e))
21    }
22}
23
24/// Implements `ReadByteable` for all types that implement `std::io::Read`.
25impl<T: Read> ReadByteable for T {}
26
27/// Extends `std::io::Write` with a method to write a `Byteable` type.
28pub trait WriteByteable: Write {
29    /// Writes one `Byteable` element to the writer.
30    ///
31    /// This method will convert the `Byteable` data into its byte array
32    /// representation and then write all those bytes to the underlying writer.
33    fn write_one<T: Byteable>(&mut self, data: T) -> std::io::Result<()> {
34        let e = data.as_bytearray();
35        self.write_all(e.as_byteslice())
36    }
37}
38
39/// Implements `WriteByteable` for all types that implement `std::io::Write`.
40impl<T: Write> WriteByteable for T {}
41
42#[cfg(test)]
43mod tests {
44    use super::{ReadByteable, WriteByteable};
45    use crate::{BigEndian, Byteable, LittleEndian, impl_byteable};
46    use std::io::Cursor;
47
48    #[derive(Clone, Copy, PartialEq, Debug)]
49    #[repr(C, packed)]
50    struct TestPacket {
51        id: u16,
52        value: LittleEndian<u32>,
53    }
54    impl_byteable!(TestPacket);
55
56    #[test]
57    fn test_write_one() {
58        let packet = TestPacket {
59            id: 123,
60            value: LittleEndian::new(0x01020304),
61        };
62
63        let mut buffer = Cursor::new(vec![]);
64        buffer.write_one(packet).unwrap();
65        assert_eq!(buffer.into_inner(), vec![123, 0, 4, 3, 2, 1]);
66    }
67
68    #[test]
69    fn test_read_one() {
70        let data = vec![123, 0, 4, 3, 2, 1];
71        let mut reader = Cursor::new(data);
72        let packet: TestPacket = reader.read_one().unwrap();
73
74        // Copy values to avoid packed field reference issues
75        let id = packet.id;
76        let value = packet.value.get();
77        assert_eq!(id, 123);
78        assert_eq!(value, 0x01020304);
79    }
80
81    #[test]
82    fn test_write_read_roundtrip() {
83        let original = TestPacket {
84            id: 42,
85            value: LittleEndian::new(0xAABBCCDD),
86        };
87
88        let mut buffer = Cursor::new(vec![]);
89        buffer.write_one(original).unwrap();
90
91        let mut reader = Cursor::new(buffer.into_inner());
92        let read_packet: TestPacket = reader.read_one().unwrap();
93
94        assert_eq!(read_packet, original);
95    }
96
97    #[test]
98    fn test_write_multiple() {
99        let mut buffer = Cursor::new(vec![]);
100
101        buffer.write_one(BigEndian::new(0x0102u16)).unwrap();
102        buffer.write_one(LittleEndian::new(0x0304u16)).unwrap();
103
104        assert_eq!(buffer.into_inner(), vec![1, 2, 4, 3]);
105    }
106}