maybe_fut/api/io/
seek.rs

1use std::io::SeekFrom;
2
3/// The [`Seek`] trait provides a cursor which can be moved within a stream of bytes.
4/// The stream typically has a fixed size, allowing seeking relative to either end or the current offset.
5pub trait Seek {
6    /// Moves the cursor to a new position within the stream.
7    fn seek(&mut self, pos: SeekFrom) -> impl Future<Output = std::io::Result<u64>>;
8
9    /// Rewind to the beginning of a stream.
10    ///
11    /// This is a convenience method, equivalent to `self.seek(SeekFrom::Start(0))`.
12    fn rewind(&mut self) -> impl Future<Output = std::io::Result<u64>> {
13        self.seek(SeekFrom::Start(0))
14    }
15
16    /// Returns the current seek position from the start of the stream.
17    ///
18    /// This is equivalent to `self.seek(SeekFrom::Current(0))`.
19    fn stream_position(&mut self) -> impl Future<Output = std::io::Result<u64>> {
20        self.seek(SeekFrom::Current(0))
21    }
22
23    /// Seeks relative to the current position.
24    ///
25    /// This is equivalent to `self.seek(SeekFrom::Current(offset))` but doesn’t return the new position which can allow some implementations such as [`std::io::BufReader`] to perform more efficient seeks.
26    fn seek_relative(&mut self, offset: i64) -> impl Future<Output = std::io::Result<u64>> {
27        self.seek(SeekFrom::Current(offset))
28    }
29}
30
31#[cfg(test)]
32mod test {
33    use super::*;
34
35    struct MockSeek {
36        position: u64,
37        max_size: i64,
38    }
39
40    impl MockSeek {
41        fn new(max_size: i64) -> Self {
42            Self {
43                position: 0,
44                max_size,
45            }
46        }
47    }
48
49    impl Seek for MockSeek {
50        async fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
51            match pos {
52                SeekFrom::Start(offset) => {
53                    self.position = offset;
54                }
55                SeekFrom::Current(offset) => {
56                    self.position = self.position.saturating_add(offset as u64);
57                }
58                SeekFrom::End(offset) => {
59                    self.position = self.max_size.saturating_add(offset) as u64;
60                }
61            }
62            Ok(self.position)
63        }
64    }
65
66    #[tokio::test]
67    async fn test_seek() {
68        let mut seek = MockSeek::new(50);
69        assert_eq!(seek.seek(SeekFrom::Start(10)).await.unwrap(), 10);
70        assert_eq!(seek.seek(SeekFrom::Current(5)).await.unwrap(), 15);
71        assert_eq!(seek.seek(SeekFrom::End(-5)).await.unwrap(), 45);
72    }
73}