ts-rust-helper 0.11.0

Various helper functions, structures, and traits for working on my Rust projects.
Documentation
//! A simple way to read some bytes.

use crate::integer::Integer;

#[derive(Clone, Debug)]
/// A simple cursor over a slice.
pub struct SimpleCursor<'a> {
    index: usize,
    collection: &'a [u8],
}

impl core::fmt::Display for SimpleCursor<'_> {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "byte {} of {}", self.index, self.collection.len())
    }
}

impl<'a> SimpleCursor<'a> {
    /// Create a new cursor over a collection.
    pub fn new(collection: &'a [u8]) -> Self {
        Self {
            index: 0,
            collection,
        }
    }

    /// Pull some bytes from this source into the specified buffer, returning how many bytes were
    /// read.
    ///
    /// If this function returns `0` then either:
    /// 1. The `buffer` is of length zero.
    /// 2. All bytes have been read from the source.
    pub fn read(&mut self, buffer: &mut [u8]) -> usize {
        let byte_count = buffer.len().min(self.collection.len() - self.index);
        if byte_count == 0 {
            return 0;
        }
        let data = self.read_count(byte_count).unwrap();
        buffer[..byte_count].copy_from_slice(data);
        byte_count
    }

    /// Pull exactly `N` bytes from the source into an array.
    pub fn read_to_array<const N: usize>(&mut self) -> Result<[u8; N], OutOfBounds> {
        let mut output = [0u8; N];
        let data = self.read_count(N)?;
        output.copy_from_slice(data);
        Ok(output)
    }

    /// Pull the next `count` bytes from the source.
    pub fn read_count<N: Into<usize>>(&mut self, count: N) -> Result<&[u8], OutOfBounds> {
        let count = count.into();
        let data = self
            .collection
            .get(self.index..self.index + count)
            .ok_or_else(|| OutOfBounds::new(count))?;
        self.index += count;

        Ok(data)
    }

    /// Pull `N` bytes from the source and create a native endian integer value from its
    /// representation as a byte array in big endian.
    pub fn read_be_integer<const N: usize, I: Integer<N>>(&mut self) -> Result<I, OutOfBounds> {
        let data = self.read_to_array::<N>()?;
        Ok(I::from_be_bytes(data))
    }

    /// Pull `N` bytes from the source and create a native endian integer value from its
    /// representation as a byte array in little endian.
    pub fn read_le_integer<const N: usize, I: Integer<N>>(&mut self) -> Result<I, OutOfBounds> {
        let data = self.read_to_array::<N>()?;
        Ok(I::from_le_bytes(data))
    }

    /// Pull `N` bytes from the source and create a native endian integer value from its
    /// representation as a byte array in native endian.
    pub fn read_ne_integer<const N: usize, I: Integer<N>>(&mut self) -> Result<I, OutOfBounds> {
        let data = self.read_to_array::<N>()?;
        Ok(I::from_ne_bytes(data))
    }
}

impl std::io::Read for SimpleCursor<'_> {
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        Ok(self.read(buf))
    }
}

/// A read would take the cursor out of bounds.
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub struct OutOfBounds {
    requested: usize,
}
impl OutOfBounds {
    fn new(requested: usize) -> Self {
        Self { requested }
    }
}
impl core::fmt::Display for OutOfBounds {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(
            f,
            "reading {} bytes would take the cursor out of bounds",
            self.requested,
        )
    }
}
impl core::error::Error for OutOfBounds {}

#[cfg(test)]
mod test {
    use crate::simple_cursor::SimpleCursor;

    #[test]
    fn simple_cursor() {
        let collection: Vec<u8> = (0..32).collect();
        let mut cursor = SimpleCursor::new(&collection);

        let result = cursor.read_count(2usize).unwrap();
        assert_eq!(&[0, 1], result);

        let mut buffer = vec![0u8; 0];
        let result = cursor.read(&mut buffer);
        assert_eq!(0, result);

        let mut buffer = vec![0u8; 4];
        let result = cursor.read(&mut buffer);
        assert_eq!(4, result);
        assert_eq!(vec![2, 3, 4, 5], buffer);

        let result = cursor.read_to_array::<3>().unwrap();
        assert_eq!([6, 7, 8], result);

        let result: u8 = cursor.read_ne_integer().unwrap();
        assert_eq!(9, result);

        let result: i8 = cursor.read_ne_integer().unwrap();
        assert_eq!(10, result);

        let result: i64 = cursor.read_be_integer().unwrap();
        assert_eq!(i64::from_be_bytes([11, 12, 13, 14, 15, 16, 17, 18]), result);

        let result: u32 = cursor.read_le_integer().unwrap();
        assert_eq!(u32::from_le_bytes([19, 20, 21, 22]), result);

        let mut buffer = vec![0u8; 32];
        let result = cursor.read(&mut buffer);
        assert_eq!(result, 9);
        assert_eq!(&[23, 24, 25, 26, 27, 28, 29, 30, 31], &buffer[0..9]);

        let result = cursor.read(&mut buffer);
        assert_eq!(0, result);
    }
}