diff --git a/chapter3/Cargo.toml b/chapter3/Cargo.toml index 2506c69..62e63c4 100644 --- a/chapter3/Cargo.toml +++ b/chapter3/Cargo.toml @@ -86,3 +86,9 @@ path = "src/3_6_3/main.rs" [[bin]] name = "3_6_2" path = "src/3_6_2/main.rs" +[[bin]] +name = "3_7_multi" +path = "src/3_7/multi_reader.rs" +[[bin]] +name = "3_7_tee" +path = "src/3_7/tee_reader.rs" diff --git a/chapter3/src/3_7/multi_reader.rs b/chapter3/src/3_7/multi_reader.rs new file mode 100644 index 0000000..322883b --- /dev/null +++ b/chapter3/src/3_7/multi_reader.rs @@ -0,0 +1,14 @@ +use std::io::{copy, stdout}; + +use lib::io::MultiReader; + +fn main() -> std::io::Result<()> { + let header = "---- HEADER ----\n".as_bytes(); + let content = "Example of MultiReader\n".as_bytes(); + let footer = "---- FOOTER ----\n".as_bytes(); + let mut multi_reader = + MultiReader::new(vec![Box::new(header), Box::new(content), Box::new(footer)]); + copy(&mut multi_reader, &mut stdout())?; + + Ok(()) +} diff --git a/chapter3/src/3_7/tee_reader.rs b/chapter3/src/3_7/tee_reader.rs new file mode 100644 index 0000000..f2a6fae --- /dev/null +++ b/chapter3/src/3_7/tee_reader.rs @@ -0,0 +1,20 @@ +use lib::io::TeeReader; +use std::io::Read; + +fn main() -> std::io::Result<()> { + // TeeReader を使用した場合の例。 + let mut buf = Vec::new(); + let reader = "Example of TeeReader".as_bytes(); + let mut tee_reader = TeeReader::new(reader, &mut buf); + // データを読み捨てる。 + let _ = tee_reader.read_to_end(&mut Vec::new())?; + + println!( + "{}", + // けどバッファには残っている。 + String::from_utf8(buf) + .expect("UTF-8 形式でない文字列の可能性があります。UTF-8 にしてください。") + ); + + Ok(()) +} diff --git a/lib/src/io.rs b/lib/src/io.rs index 7c9ea0a..1e53116 100644 --- a/lib/src/io.rs +++ b/lib/src/io.rs @@ -1,7 +1,11 @@ mod limited_reader; +mod multi_reader; mod multi_writer; mod section_reader; +mod tee_reader; pub use limited_reader::LimitedReader; +pub use multi_reader::MultiReader; pub use multi_writer::MultiWriter; pub use section_reader::SectionReader; +pub use tee_reader::TeeReader; diff --git a/lib/src/io/multi_reader.rs b/lib/src/io/multi_reader.rs new file mode 100644 index 0000000..c355f1b --- /dev/null +++ b/lib/src/io/multi_reader.rs @@ -0,0 +1,58 @@ +use std::{collections::VecDeque, io::Read}; + +/// Takes multiple `std::io::Read` at once. +/// This is inspired by `io.MultiReader` in Go. +/// +/// # Example +/// +/// ``` +/// use std::io::{copy, stdout}; +/// use lib::io::MultiReader; +/// +/// fn main() -> std::io::Result<()> { +/// let header = "---- HEADER ----\n".as_bytes(); +/// let content = "Example of MultiReader\n".as_bytes(); +/// let footer = "---- FOOTER ----\n".as_bytes(); +/// let mut multi_reader = +/// MultiReader::new(vec![Box::new(header), Box::new(content), Box::new(footer)]); +/// copy(&mut multi_reader, &mut stdout())?; +/// Ok(()) +/// } +/// ``` +pub struct MultiReader { + readers: VecDeque>, + /// Points to current element while reading. + current: Option>, +} + +impl MultiReader { + /// Constructs `MultiReader`. `current` is set to the first element that is popped out from `VecDeque`. + pub fn new(readers: Vec>) -> Self { + let mut deque: VecDeque> = readers.into(); + let current = deque.pop_front(); + Self { + readers: deque, + current, + } + } +} + +impl Read for MultiReader { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + // Doesn't move to the next one until the first one is completely read. + // If the processing ends in the middle of reading the first one, + // it returns the control at that point. + loop { + match self.current { + Some(ref mut r) => { + let n = r.read(buf)?; + if n > 0 { + return Ok(n); + } + } + None => return Ok(0), + } + self.current = self.readers.pop_front(); + } + } +} diff --git a/lib/src/io/tee_reader.rs b/lib/src/io/tee_reader.rs new file mode 100644 index 0000000..3341976 --- /dev/null +++ b/lib/src/io/tee_reader.rs @@ -0,0 +1,32 @@ +use std::io::{Read, Write}; + +pub struct TeeReader +where + R: Read, + W: Write, +{ + reader: R, + writer: W, +} + +impl TeeReader +where + R: Read, + W: Write, +{ + pub fn new(reader: R, writer: W) -> Self { + Self { reader, writer } + } +} + +impl Read for TeeReader +where + R: Read, + W: Write, +{ + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + let n = self.reader.read(buf)?; + self.writer.write_all(&buf[..n])?; + Ok(n) + } +}