use std::fmt;
use std::io;
use std::io::BufRead;
use std::io::Read;
use std::iter::Peekable;
use crate::errors;
use crate::errors::Result;
pub struct Reader<R: Read> {
lines: Peekable<io::Lines<io::BufReader<R>>>,
}
impl<R: Read> Reader<R> {
pub fn new(readable: R) -> Self {
Reader {
lines: io::BufReader::new(readable).lines().peekable(),
}
}
pub fn read_record(&mut self) -> Result<Option<Record>> {
let mut header = match self.lines.next() {
None => return Ok(None),
Some(header) => header?,
};
if !header.starts_with('@') {
bail!(errors::ErrorKind::Io(io::Error::new(
io::ErrorKind::Other,
"Expected @ at beginning of fastq header."
)));
}
let _ = header.remove(0);
let mut lines = 0;
let mut sequence = String::new();
while self
.lines
.peek()
.and_then(|line| line.as_ref().ok())
.map(|line| !line.starts_with('+'))
.unwrap_or(false)
{
sequence.push_str(&self.lines.next().unwrap()?);
lines += 1;
}
if self
.lines
.next()
.and_then(|line| line.ok())
.map(|line| !line.starts_with('+'))
.unwrap_or(false)
{
bail!(errors::ErrorKind::Io(io::Error::new(
io::ErrorKind::Other,
"Expected a + as separator."
)));
}
let mut quality = String::with_capacity(sequence.len());
for _ in 0..lines {
if let Some(line) = self.lines.next() {
quality.push_str(&line?)
} else {
bail!(errors::ErrorKind::Io(io::Error::new(
io::ErrorKind::Other,
"Expected as many quality lines as \
sequence lines."
)));
}
}
Ok(Some(Record {
header,
sequence,
quality,
}))
}
pub fn records(self) -> Records<R> {
Records { reader: self }
}
}
#[derive(Debug)]
pub struct Record {
pub header: String,
pub sequence: String,
pub quality: String,
}
impl fmt::Display for Record {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Record ({},{},{})",
self.header, self.sequence, self.quality
)
}
}
pub struct Records<R: Read> {
reader: Reader<R>,
}
impl<R: Read> Iterator for Records<R> {
type Item = Result<Record>;
fn next(&mut self) -> Option<Result<Record>> {
match self.reader.read_record() {
Ok(None) => None,
Ok(Some(record)) => Some(Ok(record)),
Err(err) => Some(Err(err)),
}
}
}