use std::borrow::Cow;
use std::io::Write;
use memchr::memchr;
use crate::sequence::{QualitySequence, Sequence};
use crate::util::ParseError;
pub fn mask_header_tabs(id: &[u8]) -> Option<Vec<u8>> {
memchr(b'\t', id).map(|_| {
id.iter()
.map(|x| if *x == b'\t' { b'|' } else { *x })
.collect()
})
}
pub fn mask_header_utf8(id: &[u8]) -> Option<Vec<u8>> {
match String::from_utf8_lossy(id) {
Cow::Owned(s) => Some(s.into_bytes()),
Cow::Borrowed(_) => None,
}
}
pub struct SequenceRecord<'a> {
pub id: Cow<'a, [u8]>,
pub seq: Cow<'a, [u8]>,
pub qual: Option<Cow<'a, [u8]>>,
}
impl<'a> SequenceRecord<'a> {
pub fn new(id: Cow<'a, [u8]>, seq: Cow<'a, [u8]>, qual: Option<Cow<'a, [u8]>>) -> Self {
SequenceRecord { id, seq, qual }
}
pub fn mask_header(mut self) -> Self {
if let Some(id) = mask_header_tabs(&self.id) {
self.id = id.into();
}
if let Some(id) = mask_header_utf8(&self.id) {
self.id = id.into();
}
self
}
pub fn write_fasta(&self, writer: &mut dyn Write, ending: &[u8]) -> Result<(), ParseError> {
writer.write_all(b">")?;
writer.write_all(&self.id)?;
writer.write_all(ending)?;
writer.write_all(&self.seq)?;
writer.write_all(ending)?;
Ok(())
}
pub fn write_fastq(&self, writer: &mut dyn Write, ending: &[u8]) -> Result<(), ParseError> {
writer.write_all(b"@")?;
writer.write_all(&self.id)?;
writer.write_all(ending)?;
writer.write_all(&self.seq)?;
writer.write_all(ending)?;
writer.write_all(b"+")?;
writer.write_all(ending)?;
if let Some(qual) = &self.qual {
writer.write_all(&qual)?;
} else {
writer.write_all(&vec![b'I'; self.seq.len()])?;
}
writer.write_all(ending)?;
Ok(())
}
}
impl<'a> From<&'a [u8]> for SequenceRecord<'a> {
fn from(slice: &'a [u8]) -> Self {
SequenceRecord::new(Cow::from(&b""[..]), slice.into(), None)
}
}
impl<'a> Sequence<'a> for SequenceRecord<'a> {
fn sequence(&'a self) -> &'a [u8] {
self.seq.as_ref()
}
}
static EMPTY_VEC: &[u8] = b"";
impl<'a> QualitySequence<'a> for SequenceRecord<'a> {
fn quality(&'a self) -> &'a [u8] {
if let Some(q) = self.qual.as_ref() {
q.as_ref()
} else {
&EMPTY_VEC
}
}
}