use std::io::prelude::*;
mod error;
mod header;
mod record;
pub use error::VCFError;
pub use header::{
Number, VCFHeader, VCFHeaderContent, VCFHeaderFilterAlt, VCFHeaderInfoFormat, VCFHeaderLine,
VCFVersion, ValueType,
};
pub use record::VCFRecord;
pub type U8Vec = Vec<u8>;
type VResult<I, O> = nom::IResult<I, O, nom::error::VerboseError<I>>;
pub struct VCFReader<R: BufRead> {
buffer: Vec<u8>,
unprocessed_line: Option<Vec<u8>>,
current_line: u64,
reader: R,
vcf_header: VCFHeader,
}
impl<R: BufRead> VCFReader<R> {
pub fn new(mut reader: R) -> Result<Self, VCFError> {
let (current_line, unprocessed_line, vcf_header) = header::parse_header(&mut reader)?;
Ok(VCFReader {
buffer: Vec::new(),
unprocessed_line,
current_line,
reader,
vcf_header,
})
}
pub fn next_record(&mut self, record: &mut record::VCFRecord) -> Result<bool, VCFError> {
if let Some(val) = self.unprocessed_line.as_ref() {
self.buffer.extend(val);
self.unprocessed_line = None;
} else {
self.buffer.clear();
self.reader.read_until(b'\n', &mut self.buffer)?;
self.current_line += 1;
}
if self.buffer.is_empty() {
return Ok(false);
}
record::parse_record::<nom::error::VerboseError<_>>(&self.buffer, record)
.map_err(|_e| VCFError::RecordParseError(self.current_line))?;
Ok(true)
}
pub fn header(&self) -> &header::VCFHeader {
&self.vcf_header
}
pub fn empty_record(&self) -> VCFRecord {
VCFRecord::new(self.vcf_header.clone())
}
}
pub struct VCFWriter<W: Write> {
writer: W,
}
impl<W: Write> VCFWriter<W> {
pub fn new(mut writer: W, header: &VCFHeader) -> Result<Self, VCFError> {
for one in header.items() {
writer.write_all(one.line())?;
}
write!(writer, "#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO")?;
if !header.samples().is_empty() {
write!(writer, "\tFORMAT")?;
for one in header.samples() {
writer.write_all(b"\t")?;
writer.write_all(one)?;
}
}
writer.write_all(b"\n")?;
Ok(VCFWriter { writer })
}
pub fn write_record(&mut self, vcf_record: &VCFRecord) -> Result<(), VCFError> {
vcf_record.write_record(&mut self.writer)?;
Ok(())
}
}
#[cfg(test)]
mod test;