use std::io;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Lines;
use std::io::Read;
use std::io::Write;
use std::iter::Peekable;
use crate::errors;
use crate::errors::Result;
const BUFFER_SIZE: usize = 10_000_000; const FASTA_WIDTH: usize = 70;
pub struct Reader<R: Read> {
lines: Peekable<Lines<BufReader<R>>>,
unwrap: bool,
}
impl<R: Read> Reader<R> {
pub fn new(reader: R, unwrap: bool) -> Self {
let lines = BufReader::with_capacity(BUFFER_SIZE, reader)
.lines()
.peekable();
Reader { lines, unwrap }
}
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 fasta header."
)));
}
let _ = header.remove(0);
let mut sequence = Vec::new();
while self
.lines
.peek()
.and_then(|line| line.as_ref().ok())
.map(|line| !line.starts_with('>'))
.unwrap_or(false)
{
sequence.push(self.lines.next().unwrap()?);
}
if self.unwrap {
sequence = vec![sequence.concat()];
}
Ok(Some(Record { header, sequence }))
}
pub fn records(self) -> Records<R> {
Records { reader: self }
}
}
#[derive(Debug)]
pub struct Record {
pub header: String,
pub sequence: Vec<String>,
}
pub struct Records<R: Read> {
reader: Reader<R>,
}
impl<R: Read> Records<R> {
pub fn chunked(self, size: usize) -> ChunkedRecords<R> {
ChunkedRecords {
reader: self.reader,
chunk_size: size,
}
}
}
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)),
}
}
}
pub struct ChunkedRecords<R: Read> {
reader: Reader<R>,
chunk_size: usize,
}
impl<R: Read> Iterator for ChunkedRecords<R> {
type Item = Result<Vec<Record>>;
fn next(&mut self) -> Option<Result<Vec<Record>>> {
let mut chunk = Vec::with_capacity(self.chunk_size);
while chunk.len() < self.chunk_size {
match self.reader.read_record() {
Ok(Some(result)) => chunk.push(result),
Ok(None) => break,
Err(err) => return Some(Err(err)),
}
}
if chunk.is_empty() {
None
} else {
Some(Ok(chunk))
}
}
}
pub struct Writer<'a, W: Write> {
buffer: io::BufWriter<W>,
separator: &'a str,
wrap: bool,
}
impl<'a, W: Write> Writer<'a, W> {
pub fn new(write: W, separator: &'a str, wrap: bool) -> Self {
Writer {
buffer: io::BufWriter::new(write),
separator,
wrap,
}
}
pub fn write_record(&mut self, record: Record) -> Result<()> {
self.write_record_ref(&record)
}
pub fn write_record_ref(&mut self, record: &Record) -> Result<()> {
write!(self.buffer, ">{}", record.header)?;
let sequence = record.sequence.join(self.separator);
if !self.wrap {
self.buffer.write_all(&[b'\n'])?;
self.buffer.write_all(sequence.as_bytes())?;
} else {
for subseq in sequence.as_bytes().chunks(FASTA_WIDTH) {
self.buffer.write_all(&[b'\n'])?;
self.buffer.write_all(subseq)?;
}
}
if !sequence.is_empty() {
self.buffer.write_all(&[b'\n'])?;
}
Ok(())
}
}