read_with/
lib.rs

1//! Create a [`Read`](https://doc.rust-lang.org/std/io/trait.Read.html) object
2//! that gets its data incrementally from a function.
3//!
4//! This lets you read from an a vector of vectors or create
5//! a reader that gets blocks from a database or other data source.
6//!
7//! Example:
8//!
9//! ```rust
10//! let many_strings = ["one", "two", "three"];
11//! let mut pos = 0;
12//! std::io::copy(
13//!     &mut read_with::ReadWith::new(
14//!         ||
15//!         {
16//!             if pos == many_strings.len() { return None; }
17//!             let o = many_strings[pos];
18//!             pos+=1;
19//!             Some(o)
20//!         }
21//!     ),
22//!     &mut std::io::stdout(),
23//! ).unwrap();
24//! ```
25
26use std::io::Read;
27
28/// An object that implements the `Read` trait
29pub struct ReadWith<F, S>
30	where F: FnMut() -> Option<S>,
31	S: AsRef<[u8]> + Default
32{
33	f: F,
34	current: S,
35	offset: usize,
36	end: bool
37}
38
39impl<F, S> ReadWith<F, S>
40	where F: FnMut() -> Option<S>,
41	S: AsRef<[u8]> + Default
42{
43	/// Create an object that will read from the given function.
44	///
45	/// Keeps on reading from `f` until it returns a None.
46	/// The function may return anything that can be turned into
47	/// a `&[u8]` which includes `String` and `&str`.
48	pub fn new(f: F) -> Self
49	{
50		ReadWith
51		{
52			f: f,
53			current: Default::default(),
54			offset: 0,
55			end: false,
56		}
57	}
58}
59
60impl<F,S> Read for ReadWith<F, S>
61	where F: FnMut() -> Option<S>,
62	S: AsRef<[u8]> + Default
63{
64	fn read(&mut self, buf: &mut [u8])
65		-> std::io::Result<usize>
66	{
67		let mut wrote = 0;
68		while !self.end && wrote < buf.len()
69		{
70			let count = (buf.len()-wrote).min(self.current.as_ref().len()-self.offset);
71			buf[wrote..wrote+count]
72				.copy_from_slice( &self.current.as_ref()[self.offset..self.offset+count] );
73			wrote += count;
74			self.offset += count;
75			if self.offset == self.current.as_ref().len()
76			{
77				self.offset = 0;
78				let n = (self.f)();
79				if let Some(n) = n
80					{ self.current = n; }
81				else
82					{ self.end = true; }
83			}
84		}
85
86		Ok(wrote)
87	}
88}
89
90
91#[cfg(test)]
92mod tests
93{
94	use ::ReadWith;
95
96	#[test]
97	fn references()
98	{
99		let mut output = vec!();
100		let many_strings = ["one", "two", "three"];
101		let mut pos = 0;
102
103		::std::io::copy(
104			&mut ReadWith::new(
105				||
106				{
107					if pos == many_strings.len() { return None; }
108					let o = many_strings[pos];
109					pos+=1;
110					Some(o)
111				}
112			),
113			&mut output,
114		).unwrap();
115		assert_eq!("onetwothree", ::std::str::from_utf8(&output).unwrap());
116	}
117
118	#[test]
119	fn strings()
120	{
121		let mut output = vec!();
122		let many_strings = ["one", "two", "three"];
123		let mut pos = 0;
124		::std::io::copy(
125			&mut ReadWith::new(
126				||
127				{
128					if pos == many_strings.len() { return None; }
129					let o = many_strings[pos];
130					pos+=1;
131					Some(o.to_string() + "\n")
132				}
133			),
134			&mut output,
135		).unwrap();
136		assert_eq!("one\ntwo\nthree\n", ::std::str::from_utf8(&output).unwrap());
137	}
138}