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}