ts_io/
cursor.rs

1//! A simple cursor implementation to have sane error cases.
2
3#[derive(Clone, Debug)]
4/// A simple cursor over a slice.
5pub struct Cursor<'a, T: Copy + Default> {
6    /// The current index of the collection.
7    index: usize,
8    /// The collection.
9    collection: &'a [T],
10}
11
12impl<T: Copy + Default> core::fmt::Display for Cursor<'_, T> {
13    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
14        write!(f, "index {} of {}", self.index, self.collection.len())
15    }
16}
17
18impl<'a, T: Copy + Default> Cursor<'a, T> {
19    /// Create a new cursor over a collection.
20    pub fn new(collection: &'a [T]) -> Self {
21        Self {
22            index: 0,
23            collection,
24        }
25    }
26
27    /// Return the remaining slice of the collection.
28    ///
29    /// # Panics
30    /// * If index is out of bounds, this _should_ never happen.
31    pub fn remaining(&self) -> &[T] {
32        self.collection
33            .get(self.index..)
34            .expect("index should never be out of bounds")
35    }
36
37    /// Pull some items from this source into the specified buffer, returning how many items were
38    /// read.
39    ///
40    /// If this function returns `0` then either:
41    /// 1. The `buffer` is of length zero.
42    /// 2. All items have been read from the source.
43    ///
44    /// # Panics
45    /// * If it attempts to read out of bounds, which should only happen if the implementation is
46    ///   incorrect.
47    pub fn read(&mut self, buffer: &mut [T]) -> usize {
48        let item_count = buffer.len().min(self.collection.len() - self.index);
49        if item_count == 0 {
50            return 0;
51        }
52        let data = self
53            .read_count(item_count)
54            .expect("read should never read out of bounds");
55        buffer
56            .get_mut(..item_count)
57            .expect("read should never read out of bounds")
58            .copy_from_slice(data);
59        item_count
60    }
61
62    /// Pull exactly `N` items from the source into an array.
63    pub fn read_array<const N: usize>(&mut self) -> Result<[T; N], OutOfBounds> {
64        let mut output = [T::default(); N];
65        let data = self.read_count(N)?;
66        output.copy_from_slice(data);
67        Ok(output)
68    }
69
70    /// Pull the next `count` items from the source.
71    pub fn read_count<N: Into<usize>>(&mut self, count: N) -> Result<&[T], OutOfBounds> {
72        let count = count.into();
73        let data = self
74            .collection
75            .get(self.index..self.index + count)
76            .ok_or_else(|| OutOfBounds::new(count))?;
77        self.index += count;
78
79        Ok(data)
80    }
81}
82
83impl std::io::Read for Cursor<'_, u8> {
84    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
85        Ok(self.read(buf))
86    }
87}
88
89/// A read would take the cursor out of bounds.
90#[derive(Clone, Copy, Debug)]
91#[non_exhaustive]
92pub struct OutOfBounds {
93    /// The number of items requested.
94    requested: usize,
95}
96impl OutOfBounds {
97    /// Create a new instance of an out of bounds error.
98    fn new(requested: usize) -> Self {
99        Self { requested }
100    }
101}
102impl core::fmt::Display for OutOfBounds {
103    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
104        write!(
105            f,
106            "reading {} items would take the cursor out of bounds",
107            self.requested,
108        )
109    }
110}
111impl core::error::Error for OutOfBounds {}