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}