read_first_line/
read_first_line.rs

1// In this example we're trying to read just the first line
2// from a reader (the file some_text.txt). To be efficient,
3// we don't want any calls to Read::read to occur after we
4// encounter the first '\n', and we want all of the bytes to
5// be read into one large buffer that we allocate in the
6// beginning.
7
8use std::{cmp, io, str};
9use std::fs::File;
10use std::io::Read;
11use std::path::PathBuf;
12use read_buffer::ReadBuffer;
13
14// We create an adapter over a Read to emulate the behavior
15// that a call to read may read less bytes than the length
16// of the given buffer. This is explicitly allowed in the
17// documentation for Read::read.
18// (https://doc.rust-lang.org/std/io/trait.Read.html#tymethod.read)
19struct ChunkedReadAdapter<T: Read> (T);
20
21impl<T: Read> Read for ChunkedReadAdapter<T> {
22	fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
23		let bytes_to_read = cmp::min(7, buf.len());
24		self.0.read(&mut buf[..bytes_to_read])
25	}
26}
27
28fn main() -> Result<(), io::Error> {
29	// Get the libraries root directory
30	let mut path: PathBuf = env!("CARGO_MANIFEST_DIR").into();
31	
32	// Get the example text file
33	path.push("examples");
34	path.push("some_text.txt");
35	
36	// Open the file
37	let file = File::open(path)?;
38	
39	// Create the adapter emulating a reader that
40	// returns 5 bytes at a time
41	let mut reader = ChunkedReadAdapter(file);
42	
43	// Create the buffer to read into with a size large enough
44	// to hold the entire line
45	let mut buffer: ReadBuffer<512> = ReadBuffer::new();
46	
47	// Read into the buffer while the condition is true
48	// (or until the buffer is full, "end of file" is
49	// encountered, or an error occurs)
50	let read_data = buffer.read_while(&mut reader, |chunk| {
51		// Keep reading until we encounter a '\n'
52		!chunk.contains(&b'\n')
53	})?;
54	
55	// Is safe to use as the file contains only ASCII characters
56	let string = str::from_utf8(read_data)
57		.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
58	
59	// We read some extra data as part of the chunk which contained '\n'
60	// so we'll still have to cut the string off after the first line
61	let first_line = string.lines().next()
62		.ok_or(io::Error::from(io::ErrorKind::InvalidData))?;
63	
64	// Print out the first line
65	println!("First line: {:?}", first_line);
66	
67	// Print out all data that was read to show that it didn't
68	// stop precisely at the '\n' but it didn't read too much
69	// data after the end of the line
70	println!("All read data: {:?}", string);
71	
72	Ok(())
73}