From e8ee00a6493b8cde09f5497f430d46a978ae865f Mon Sep 17 00:00:00 2001 From: Lukas Kalbertodt Date: Wed, 13 Feb 2019 12:19:58 +0100 Subject: [PATCH] Add provided methods `Seek::{stream_len, stream_position}` These two methods are defined in terms of `Seek::seek` and are added for convenience. Tests are included. --- src/libstd/io/mod.rs | 121 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 2 deletions(-) diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index e3e2754a7aa09..99bb24f54dd41 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -1329,6 +1329,78 @@ pub trait Seek { /// [`SeekFrom::Start`]: enum.SeekFrom.html#variant.Start #[stable(feature = "rust1", since = "1.0.0")] fn seek(&mut self, pos: SeekFrom) -> Result; + + /// Returns the length of this stream (in bytes). + /// + /// This method is implemented using three seek operations. If this method + /// returns successfully, the seek position is unchanged (i.e. the position + /// before calling this method is the same as afterwards). However, if this + /// method returns an error, the seek position is undefined. + /// + /// If you need to obtain the length of *many* streams and you don't care + /// about the seek position afterwards, you can reduce the number of seek + /// operations by simply calling `seek(SeekFrom::End(0))` and use its + /// return value (it is also the stream length). + /// + /// Note that length of a stream can change over time (for example, when + /// data is appended to a file). So calling this method multiply times does + /// not necessarily return the same length each time. + /// + /// + /// # Example + /// + /// ```no_run + /// #![feature(seek_convenience)] + /// use std::{ + /// io::{self, Seek}, + /// fs::File, + /// }; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// + /// let len = f.stream_len()?; + /// println!("The file is currently {} bytes long", len); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "seek_convenience", issue = "0")] + fn stream_len(&mut self) -> Result { + let old_pos = self.stream_position()?; + let len = self.seek(SeekFrom::End(0))?; + self.seek(SeekFrom::Start(old_pos))?; + Ok(len) + } + + /// Returns the current seek position from the start of the stream. + /// + /// This is equivalent to `self.seek(SeekFrom::Current(0))`. + /// + /// + /// # Example + /// + /// ```no_run + /// #![feature(seek_convenience)] + /// use std::{ + /// io::{self, BufRead, BufReader, Seek}, + /// fs::File, + /// }; + /// + /// fn main() -> io::Result<()> { + /// let mut f = BufReader::new(File::open("foo.txt")?); + /// + /// let before = f.stream_position()?; + /// f.read_line(&mut String::new())?; + /// let after = f.stream_position()?; + /// + /// println!("The first line was {} bytes long", after - before); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "seek_convenience", issue = "0")] + fn stream_position(&mut self) -> Result { + self.seek(SeekFrom::Current(0)) + } } /// Enumeration of possible methods to seek within an I/O object. @@ -2157,8 +2229,7 @@ impl Iterator for Lines { mod tests { use crate::io::prelude::*; use crate::io; - use super::Cursor; - use super::repeat; + use super::{Cursor, SeekFrom, repeat}; #[test] #[cfg_attr(target_os = "emscripten", ignore)] @@ -2380,4 +2451,50 @@ mod tests { super::read_to_end(&mut lr, &mut vec) }); } + + #[test] + fn seek_len() -> io::Result<()> { + let mut c = Cursor::new(vec![0; 15]); + assert_eq!(c.stream_len()?, 15); + + c.seek(SeekFrom::End(0))?; + let old_pos = c.stream_position()?; + assert_eq!(c.stream_len()?, 15); + assert_eq!(c.stream_position()?, old_pos); + + c.seek(SeekFrom::Start(7))?; + c.seek(SeekFrom::Current(2))?; + let old_pos = c.stream_position()?; + assert_eq!(c.stream_len()?, 15); + assert_eq!(c.stream_position()?, old_pos); + + Ok(()) + } + + #[test] + fn seek_position() -> io::Result<()> { + // All `asserts` are duplicated here to make sure the method does not + // change anything about the seek state. + let mut c = Cursor::new(vec![0; 15]); + assert_eq!(c.stream_position()?, 0); + assert_eq!(c.stream_position()?, 0); + + c.seek(SeekFrom::End(0))?; + assert_eq!(c.stream_position()?, 15); + assert_eq!(c.stream_position()?, 15); + + + c.seek(SeekFrom::Start(7))?; + c.seek(SeekFrom::Current(2))?; + assert_eq!(c.stream_position()?, 9); + assert_eq!(c.stream_position()?, 9); + + c.seek(SeekFrom::End(-3))?; + c.seek(SeekFrom::Current(1))?; + c.seek(SeekFrom::Current(-5))?; + assert_eq!(c.stream_position()?, 8); + assert_eq!(c.stream_position()?, 8); + + Ok(()) + } }