1use std::fmt;
4use std::io;
5use std::io::BufRead;
6use std::io::Read;
7use std::iter::Peekable;
8
9use crate::errors;
10use crate::errors::Result;
11
12pub struct Reader<R: Read> {
14 lines: Peekable<io::Lines<io::BufReader<R>>>,
15}
16
17impl<R: Read> Reader<R> {
18 pub fn new(readable: R) -> Self {
20 Reader {
21 lines: io::BufReader::new(readable).lines().peekable(),
22 }
23 }
24
25 pub fn read_record(&mut self) -> Result<Option<Record>> {
27 let mut header = match self.lines.next() {
29 None => return Ok(None),
30 Some(header) => header?,
31 };
32 if !header.starts_with('@') {
33 bail!(errors::ErrorKind::Io(io::Error::new(
34 io::ErrorKind::Other,
35 "Expected @ at beginning of fastq header."
36 )));
37 }
38 let _ = header.remove(0);
39
40 let mut lines = 0;
42 let mut sequence = String::new();
43 while self
44 .lines
45 .peek()
46 .and_then(|line| line.as_ref().ok())
47 .map(|line| !line.starts_with('+'))
48 .unwrap_or(false)
49 {
50 sequence.push_str(&self.lines.next().unwrap()?);
51 lines += 1;
52 }
53
54 if self
56 .lines
57 .next()
58 .and_then(|line| line.ok())
59 .map(|line| !line.starts_with('+'))
60 .unwrap_or(false)
61 {
62 bail!(errors::ErrorKind::Io(io::Error::new(
63 io::ErrorKind::Other,
64 "Expected a + as separator."
65 )));
66 }
67
68 let mut quality = String::with_capacity(sequence.len());
70 for _ in 0..lines {
71 if let Some(line) = self.lines.next() {
72 quality.push_str(&line?)
73 } else {
74 bail!(errors::ErrorKind::Io(io::Error::new(
75 io::ErrorKind::Other,
76 "Expected as many quality lines as \
77 sequence lines."
78 )));
79 }
80 }
81
82 Ok(Some(Record {
83 header,
84 sequence,
85 quality,
86 }))
87 }
88
89 pub fn records(self) -> Records<R> {
91 Records { reader: self }
92 }
93}
94
95#[derive(Debug)]
97pub struct Record {
98 pub header: String,
100
101 pub sequence: String,
103
104 pub quality: String,
108}
109
110impl fmt::Display for Record {
111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112 write!(
113 f,
114 "Record ({},{},{})",
115 self.header, self.sequence, self.quality
116 )
117 }
118}
119
120pub struct Records<R: Read> {
122 reader: Reader<R>,
123}
124
125impl<R: Read> Iterator for Records<R> {
126 type Item = Result<Record>;
127
128 fn next(&mut self) -> Option<Result<Record>> {
129 match self.reader.read_record() {
130 Ok(None) => None,
131 Ok(Some(record)) => Some(Ok(record)),
132 Err(err) => Some(Err(err)),
133 }
134 }
135}