concat_reader/
lib.rs

1//! concat-reader is a library for Rust that contains utility functions and traits to create
2//! concatenated [`Read`] objects from any thing that implements [`IntoIterator`].
3//!
4//! ```no_run
5//! use concat_reader::{FileConcatRead, concat_path};
6//! use std::io::{self, Read, BufRead, BufReader, Write};
7//! fn main() -> io::Result<()>{
8//!     let files = vec!["/path/to/file_1", "/path/to/file_2", "/path/to/file_3"];
9//!     let mut f = concat_path(files);
10//!     let mut buffered = BufReader::new(f);
11//!     let stdout = io::stdout();
12//!     let mut handle = stdout.lock();
13//!     loop {
14//!         let mut line = String::new();
15//!         let r = buffered.read_line(&mut line)?;
16//!         if r == 0 {
17//!             return Ok(())
18//!         }
19//!         let f = buffered.get_ref().file_path();
20//!         eprintln!("read from {:?}", f);
21//!         handle.write(line.as_bytes())?;
22//!     }
23//! }
24//! ```
25//! [`READ`]:         https://doc.rust-lang.org/std/io/trait.Read.html
26//! [`IntoIterator`]: https://doc.rust-lang.org/std/iter/trait.IntoIterator.html
27
28use std::io::Read;
29use std::path::Path;
30
31pub mod file;
32pub mod read;
33
34pub use self::file::FileConcatReader;
35pub use self::read::ConcatReader;
36
37/// Concats multiple readers into a single reader.
38///
39/// ```
40/// use concat_reader::concat;
41/// use std::io::Read;
42///
43/// let value1 = "some string".as_bytes();
44/// let value2 = "another string".as_bytes();
45///
46/// let mut buffer = String::new();
47/// let mut f = concat(vec![value1, value2]);
48/// f.read_to_string(&mut buffer).unwrap();
49/// ```
50pub fn concat<I: IntoIterator>(items: I) -> impl ConcatRead
51where
52    I::Item: Read,
53{
54    read::ConcatReader::from(items)
55}
56
57/// Concats multiple file paths into a single reader over all files.
58///
59/// ```no_run
60/// use concat_reader::{FileConcatRead, concat_path};
61/// use std::io::{self, Read, BufRead, BufReader, Write};
62/// fn main() -> io::Result<()>{
63///     let files = vec!["/path/to/file_1", "/path/to/file_2", "/path/to/file_3"];
64///     let mut f = concat_path(files);
65///     let mut buffered = BufReader::new(f);
66///     let stdout = io::stdout();
67///     let mut handle = stdout.lock();
68///     loop {
69///         let mut line = String::new();
70///         let r = buffered.read_line(&mut line)?;
71///         if r == 0 {
72///             return Ok(())
73///         }
74///         let f = buffered.get_ref().file_path();
75///         eprintln!("read from {:?}", f);
76///         handle.write(line.as_bytes())?;
77///     }
78/// }
79/// ```
80pub fn concat_path<I: IntoIterator>(items: I) -> impl FileConcatRead
81where
82    I::Item: AsRef<Path>,
83{
84    file::FileConcatReader::from(items)
85}
86
87/// A special [`Read`] trait for concatenated readers.
88///
89/// This traids adds special function to fetch the current `Read` item and to skip to the next item.
90pub trait ConcatRead: Read {
91    type Item;
92
93    /// Skips to the next [`Read`] item in the internal [`Iterator`].
94    ///
95    /// ```rust
96    /// use concat_reader::concat;
97    /// use std::io::{self, Read};
98    /// use crate::concat_reader::ConcatRead;
99    ///
100    /// fn main() -> io::Result<()> {
101    ///     let value1 = "some string".as_bytes();
102    ///     let value2 = "another string".as_bytes();
103    ///
104    ///     let mut buffer = [0; 4];
105    ///     let mut f = concat(vec![value1, value2]);
106    ///     f.read_exact(&mut buffer)?;
107    ///     assert_eq!(buffer, "some".as_bytes());
108    ///
109    ///     //skip to the next Read object
110    ///     f.skip();
111    ///     f.read_exact(&mut buffer)?;
112    ///     assert_eq!(buffer, "anot".as_bytes());
113    ///     Ok(())
114    /// }
115    /// ```
116    /// [`READ`]:                   https://doc.rust-lang.org/std/io/trait.Read.html
117    /// [`Iterator`]:               https://doc.rust-lang.org/std/iter/trait.Iterator.html
118    ///
119    fn skip(&mut self) -> bool;
120
121    /// Returns the current `Read` item in the internal iterator being read from.
122    fn current(&self) -> Option<&Self::Item>;
123}
124
125/// `FileConcatRead` is a kind of `ConcatRead` which can provide information about the file currently read.
126///
127/// # Example
128///
129/// ```no_run
130/// use std::io;
131/// use std::io::prelude::*;
132/// use std::path::Path;
133/// use crate::concat_reader::*;
134///
135/// fn main() -> io::Result<()> {
136///     let files = vec!["/path/to/file_1", "/path/to/file_2", "/path/to/file_3"];
137///     let mut f = concat_path(files);
138///     assert!(f.file_path().is_none());
139///     let mut buffer = [0; 10];
140///     f.read(&mut buffer)?;
141///     assert_eq!(f.file_path(), Some(Path::new("/path/to/file_1")));
142///     Ok(())
143/// }
144pub trait FileConcatRead: ConcatRead {
145    /// Returns the path to the current [`File`] being read from.
146    ///
147    /// ```no_run
148    /// use std::io;
149    /// use std::io::prelude::*;
150    /// use crate::concat_reader::*;
151    ///
152    /// fn main() -> io::Result<()> {
153    ///     let files = vec!["/path/to/file_1", "/path/to/file_2", "/path/to/file_3"];
154    ///     let mut f = concat_path(files);
155    ///
156    ///     let mut buffer = [0; 1];
157    ///     //read 1 bytes from the reader
158    ///     f.read_exact(&mut buffer);
159    ///     println!("read from {}", f.file_path().unwrap().display());
160    ///
161    ///     //skip to next file in reader
162    ///     f.skip();
163    ///     ///     //read 1 bytes from the reader
164    ///     f.read_exact(&mut buffer);
165    ///     println!("read from {}", f.file_path().unwrap().display());
166    ///     Ok(())
167    /// }
168    /// ```
169    ///
170    /// [`File`]:                   https://doc.rust-lang.org/std/fs/struct.File.html
171    fn file_path(&self) -> Option<&Path>;
172}