use std::io::{Write, BufWriter, Result, BufReader, BufRead};
use std::fs::File;
use std::path::Path;
use super::{RecordReader, RecordWriter, Header, Record};
pub struct SamWriterBuilder {
write_header: bool,
}
impl SamWriterBuilder {
pub fn new() -> Self {
Self {
write_header: true,
}
}
pub fn write_header(&mut self, write: bool) -> &mut Self {
self.write_header = write;
self
}
pub fn from_path<P: AsRef<Path>>(&mut self, path: P, header: Header)
-> Result<SamWriter<BufWriter<File>>> {
let stream = BufWriter::new(File::create(path)?);
self.from_stream(stream, header)
}
pub fn from_stream<W: Write>(&mut self, mut stream: W, header: Header) -> Result<SamWriter<W>> {
if self.write_header {
header.write_text(&mut stream)?;
}
Ok(SamWriter { stream, header })
}
}
pub struct SamWriter<W: Write> {
stream: W,
header: Header,
}
impl SamWriter<BufWriter<File>> {
pub fn build() -> SamWriterBuilder {
SamWriterBuilder::new()
}
pub fn from_path<P: AsRef<Path>>(path: P, header: Header) -> Result<Self> {
SamWriterBuilder::new().from_path(path, header)
}
}
impl<W: Write> SamWriter<W> {
pub fn from_stream(stream: W, header: Header) -> Result<Self> {
SamWriterBuilder::new().from_stream(stream, header)
}
pub fn header(&self) -> &Header {
&self.header
}
pub fn flush(&mut self) -> Result<()> {
self.stream.flush()
}
pub fn take_stream(mut self) -> W {
let _ignore = self.stream.flush();
self.stream
}
}
impl<W: Write> RecordWriter for SamWriter<W> {
fn write(&mut self, record: &Record) -> Result<()> {
record.write_sam(&mut self.stream, &self.header)
}
fn finish(&mut self) -> Result<()> {
self.flush()
}
fn flush(&mut self) -> Result<()> {
self.flush()
}
}
pub struct SamReader<R: BufRead> {
stream: R,
header: Header,
buffer: String,
}
impl SamReader<BufReader<File>> {
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self> {
let stream = BufReader::new(File::open(path)?);
SamReader::from_stream(stream)
}
}
impl<R: BufRead> SamReader<R> {
pub fn from_stream(mut stream: R) -> Result<Self> {
let mut header = Header::new();
let mut buffer = String::new();
loop {
buffer.clear();
if stream.read_line(&mut buffer)? == 0 {
break;
};
if buffer.starts_with('@') {
header.push_line(buffer.trim_end())?;
} else {
break;
}
}
Ok(SamReader { stream, header, buffer })
}
pub fn header(&self) -> &Header {
&self.header
}
pub fn take_stream(self) -> R {
self.stream
}
}
impl<R: BufRead> RecordReader for SamReader<R> {
fn read_into(&mut self, record: &mut Record) -> Result<bool> {
if self.buffer.is_empty() {
return Ok(false);
}
let res = match record.fill_from_sam(self.buffer.trim(), &self.header) {
Ok(()) => Ok(true),
Err(e) => {
record.clear();
Err(e)
},
};
self.buffer.clear();
match self.stream.read_line(&mut self.buffer) {
Ok(_) => res,
Err(e) => res.or(Err(e)),
}
}
fn pause(&mut self) {}
}
impl<R: BufRead> Iterator for SamReader<R> {
type Item = Result<Record>;
fn next(&mut self) -> Option<Self::Item> {
let mut record = Record::new();
match self.read_into(&mut record) {
Ok(true) => Some(Ok(record)),
Ok(false) => None,
Err(e) => Some(Err(e)),
}
}
}