From f0c9ce27b3726722155c71b2b1e7541e0eed9960 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 16 Jan 2015 14:40:16 +0100 Subject: [PATCH] read_until reworked so returned delimiter is explicit read_line never returns '\n' character read_until returns enum signifying whether delimiter is returned or not --- src/libstd/io/buffered.rs | 20 ++++++++++++---- src/libstd/io/mod.rs | 40 +++++++++++++++++++++++++------- src/libstd/io/stdio.rs | 2 +- src/test/run-pass/issue-13304.rs | 2 +- 4 files changed, 50 insertions(+), 14 deletions(-) diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs index 73c73209f00fc..1bb5758cead08 100644 --- a/src/libstd/io/buffered.rs +++ b/src/libstd/io/buffered.rs @@ -574,12 +574,24 @@ mod test { #[test] fn test_read_until() { + use io::ReadUntilHelper; let inner = MemReader::new(vec!(0, 1, 2, 1, 0)); let mut reader = BufferedReader::with_capacity(2, inner); - assert_eq!(reader.read_until(0), Ok(vec!(0))); - assert_eq!(reader.read_until(2), Ok(vec!(1, 2))); - assert_eq!(reader.read_until(1), Ok(vec!(1))); - assert_eq!(reader.read_until(8), Ok(vec!(0))); + assert_eq!(reader.read_until(0), Ok(ReadUntilHelper::WithDelimiter(vec!(0)))); + assert_eq!(reader.read_until(2), Ok(ReadUntilHelper::WithDelimiter(vec!(1, 2)))); + assert_eq!(reader.read_until(1), Ok(ReadUntilHelper::WithDelimiter(vec!(1)))); + assert_eq!(reader.read_until(8), Ok(ReadUntilHelper::WithoutDelimiter(vec!(0)))); + assert!(reader.read_until(9).is_err()); + } + + #[test] + fn test_read_until_without_delimiter() { + let inner = MemReader::new(vec!(0, 1, 2, 1, 0)); + let mut reader = BufferedReader::with_capacity(2, inner); + assert_eq!(reader.read_until(0).unwrap().without_delimiter(), vec![]); + assert_eq!(reader.read_until(2).unwrap().without_delimiter(), vec![1]); + assert_eq!(reader.read_until(1).unwrap().without_delimiter(), vec![]); + assert_eq!(reader.read_until(8).unwrap().without_delimiter(), vec![0]); assert!(reader.read_until(9).is_err()); } diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index f106e9464c52a..51548d7ad76da 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -1348,6 +1348,32 @@ impl<'r, T: Buffer> Iterator for Chars<'r, T> { } } +/// Makes the possibility of `read_until` returning a sequence with a delimiter explicit +/// Use `without_delimiter` if you always want the same type of result +pub enum ReadUntilHelper { + /// If you run `read_until` again after receiving `WithoutDelimiter`, you will get an EOF + /// Does not contain the delimiter. + WithoutDelimiter(Vec), + /// Always contains the delimiter. If the delimiter was just before the end of the Buffer, + /// the next call to `read_until` will also give you an EOF. + WithDelimiter(Vec), +} + +impl ReadUntilHelper { + /// Destructures the ReadUntilHelper by either removing the delimiter (if present) or + /// directly returning the read block (if no delimiter was present) + pub fn without_delimiter(self) -> Vec { + match self { + ReadUntilHelper::WithoutDelimiter(x) => x, + ReadUntilHelper::WithDelimiter(mut x) => { + let len = x.len() - 1; + x.truncate(len); + x + }, + } + } +} + /// A Buffer is a type of reader which has some form of internal buffering to /// allow certain kinds of reading operations to be more optimized than others. /// This type extends the `Reader` trait with a few methods that are not @@ -1374,7 +1400,7 @@ pub trait Buffer: Reader { /// Reads the next line of input, interpreted as a sequence of UTF-8 /// encoded Unicode codepoints. If a newline is encountered, then the - /// newline is contained in the returned string. + /// newline is NOT contained in the returned string. /// /// # Example /// @@ -1382,7 +1408,7 @@ pub trait Buffer: Reader { /// use std::io::BufReader; /// /// let mut reader = BufReader::new(b"hello\nworld"); - /// assert_eq!("hello\n", &*reader.read_line().unwrap()); + /// assert_eq!("hello", &*reader.read_line().unwrap()); /// ``` /// /// # Error @@ -1392,13 +1418,11 @@ pub trait Buffer: Reader { /// * All non-EOF errors will be returned immediately /// * If an error is returned previously consumed bytes are lost /// * EOF is only returned if no bytes have been read - /// * Reach EOF may mean that the delimiter is not present in the return - /// value /// /// Additionally, this function can fail if the line of input read is not a /// valid UTF-8 sequence of bytes. fn read_line(&mut self) -> IoResult { - self.read_until(b'\n').and_then(|line| + self.read_until(b'\n').and_then(|ruh| Ok(ruh.without_delimiter())).and_then(|line| match String::from_utf8(line) { Ok(s) => Ok(s), Err(_) => Err(standard_error(InvalidInput)), @@ -1421,7 +1445,7 @@ pub trait Buffer: Reader { /// have been read, otherwise the pending byte buffer is returned. This /// is the reason that the byte buffer returned may not always contain the /// delimiter. - fn read_until(&mut self, byte: u8) -> IoResult> { + fn read_until(&mut self, byte: u8) -> IoResult { let mut res = Vec::new(); loop { @@ -1429,7 +1453,7 @@ pub trait Buffer: Reader { let available = match self.fill_buf() { Ok(n) => n, Err(ref e) if res.len() > 0 && e.kind == EndOfFile => { - return Ok(res); + return Ok(ReadUntilHelper::WithoutDelimiter(res)); } Err(e) => return Err(e) }; @@ -1446,7 +1470,7 @@ pub trait Buffer: Reader { }; self.consume(used); if done { - return Ok(res); + return Ok(ReadUntilHelper::WithDelimiter(res)); } } } diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index a5664b9f01377..a6154e0107faa 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -166,7 +166,7 @@ impl StdinReader { /// The read is performed atomically - concurrent read calls in other /// threads will not interleave with this one. pub fn read_until(&mut self, byte: u8) -> IoResult> { - self.inner.lock().unwrap().0.read_until(byte) + self.inner.lock().unwrap().0.read_until(byte).and_then(|x| Ok(x.without_delimiter())) } /// Like `Buffer::read_char`. diff --git a/src/test/run-pass/issue-13304.rs b/src/test/run-pass/issue-13304.rs index 11003c6fc524d..e8e6f5f8eba23 100644 --- a/src/test/run-pass/issue-13304.rs +++ b/src/test/run-pass/issue-13304.rs @@ -33,7 +33,7 @@ fn parent() { let out = p.wait_with_output().unwrap(); assert!(out.status.success()); let s = str::from_utf8(out.output.as_slice()).unwrap(); - assert_eq!(s, "test1\n\ntest2\n\ntest3\n"); + assert_eq!(s, "test1\ntest2\ntest3\n"); } fn child() {