seek_ext/
lib.rs

1//! Adds convenience methods to `io::Seek` types via the [`SeekExt`] extension
2//! trait.
3
4use std::io::{Result, Seek, SeekFrom};
5
6
7/// Adds convenience methods to all types that implement `io::Seek`.
8///
9/// This is an extension trait that has a blanket impl which implements this
10/// trait for all `T where T: io::Seek`. You just need to import this trait
11/// into scope and then you can use its methods on all `Seek` types.
12pub trait SeekExt: Seek {
13    /// Returns the length (in bytes) of this stream.
14    ///
15    /// This method is implemented using three seek operations. If this method
16    /// returns successfully, the seek position is unchanged (i.e. the position
17    /// before calling this method is the same as afterwards). However, if this
18    /// method returns an error, the seek position is undefined.
19    ///
20    /// If you need to obtain the length of *many* streams and you don't care
21    /// about the seek position afterwards, you can reduce the number of seek
22    /// operations by simply calling `seek(SeekFrom::End(0))` and use its
23    /// return value (it is also the stream length).
24    ///
25    ///
26    /// # Example
27    ///
28    /// ```
29    /// use std::io::{Cursor, Seek, SeekFrom};
30    /// use seek_ext::SeekExt;
31    ///
32    /// # fn main() -> Result<(), std::io::Error> {
33    /// let mut c = Cursor::new(vec![0; 6]);
34    /// let pos_before = c.seek(SeekFrom::Current(4))?;
35    ///
36    /// assert_eq!(c.stream_len()?, 6);
37    /// assert_eq!(c.current_position()?, pos_before);
38    /// # Ok(())
39    /// # }
40    /// ```
41    fn stream_len(&mut self) -> Result<u64> {
42        let old_pos = self.current_position()?;
43        let len = self.seek(SeekFrom::End(0))?;
44        self.seek(SeekFrom::Start(old_pos))?;
45        Ok(len)
46    }
47
48    /// Returns the current seek position from the start of the stream.
49    ///
50    /// This is equivalent to `self.seek(SeekFrom::Current(0))`.
51    ///
52    ///
53    /// # Example
54    ///
55    /// ```
56    /// use std::io::{Cursor, Seek, SeekFrom};
57    /// use seek_ext::SeekExt;
58    ///
59    /// # fn main() -> Result<(), std::io::Error> {
60    /// let mut c = Cursor::new(vec![0; 6]);
61    ///
62    /// c.seek(SeekFrom::Current(4))?;
63    /// assert_eq!(c.current_position()?, 4);
64    ///
65    /// c.seek(SeekFrom::Current(-3))?;
66    /// assert_eq!(c.current_position()?, 1);
67    /// # Ok(())
68    /// # }
69    /// ```
70    fn current_position(&mut self) -> Result<u64> {
71        self.seek(SeekFrom::Current(0))
72    }
73}
74
75impl<T: Seek> SeekExt for T {}
76
77
78#[cfg(test)]
79mod tests {
80    use std::io::{Cursor, Seek, SeekFrom};
81    use super::SeekExt;
82
83    #[test]
84    fn stream_len() {
85        let mut c = Cursor::new(vec![0; 15]);
86        assert_eq!(c.stream_len().unwrap(), 15);
87
88        c.seek(SeekFrom::End(0)).unwrap();
89        let old_pos = c.current_position().unwrap();
90        assert_eq!(c.stream_len().unwrap(), 15);
91        assert_eq!(c.current_position().unwrap(), old_pos);
92
93        c.seek(SeekFrom::Start(7)).unwrap();
94        c.seek(SeekFrom::Current(2)).unwrap();
95        let old_pos = c.current_position().unwrap();
96        assert_eq!(c.stream_len().unwrap(), 15);
97        assert_eq!(c.current_position().unwrap(), old_pos);
98    }
99
100    #[test]
101    fn current_position() {
102        // All `asserts` are duplicated here to make sure the method does not
103        // change anything about the seek state.
104        let mut c = Cursor::new(vec![0; 15]);
105        assert_eq!(c.current_position().unwrap(), 0);
106        assert_eq!(c.current_position().unwrap(), 0);
107
108        c.seek(SeekFrom::End(0)).unwrap();
109        assert_eq!(c.current_position().unwrap(), 15);
110        assert_eq!(c.current_position().unwrap(), 15);
111
112
113        c.seek(SeekFrom::Start(7)).unwrap();
114        c.seek(SeekFrom::Current(2)).unwrap();
115        assert_eq!(c.current_position().unwrap(), 9);
116        assert_eq!(c.current_position().unwrap(), 9);
117
118        c.seek(SeekFrom::End(-3)).unwrap();
119        c.seek(SeekFrom::Current(1)).unwrap();
120        c.seek(SeekFrom::Current(-5)).unwrap();
121        assert_eq!(c.current_position().unwrap(), 8);
122        assert_eq!(c.current_position().unwrap(), 8);
123    }
124}