use anyhow::{bail, Result};
use std::ops::{Range, RangeInclusive};
const DEFAULT_QUAL: u8 = b'F';
pub trait MyRange: Iterator<Item = i32> {
fn start(&self) -> i32;
fn end(&self) -> i32;
}
impl MyRange for Range<i32> {
fn start(&self) -> i32 {
self.start
}
fn end(&self) -> i32 {
self.end
}
}
impl MyRange for RangeInclusive<i32> {
fn start(&self) -> i32 {
*self.start()
}
fn end(&self) -> i32 {
*self.end()
}
}
#[derive(Debug)]
pub struct Record {
data: Vec<u8>,
id: usize,
seq: usize,
plus: Option<usize>,
qual: Option<usize>,
}
impl Record {
#[must_use]
pub fn new() -> Self {
Self {
data: Vec::new(),
id: 0,
seq: 0,
plus: None,
qual: None,
}
}
#[must_use]
pub fn new_fasta(data: Vec<u8>, id: usize, seq: usize) -> Self {
Self {
data,
id,
seq,
plus: None,
qual: None,
}
}
#[must_use]
pub fn new_fastq(data: Vec<u8>, id: usize, seq: usize, plus: usize, qual: usize) -> Self {
Self {
data,
id,
seq,
plus: Some(plus),
qual: Some(qual),
}
}
#[must_use]
pub fn new_fasta_from_parts(id: &[u8], seq: &[u8]) -> Result<Self> {
let mut data = Vec::with_capacity(id.len() + seq.len() + 2);
if id.starts_with(b">") {
bail!("ID cannot start with '>'");
}
if id.ends_with(b"\n") {
bail!("ID cannot end with newline");
}
if seq.ends_with(b"\n") {
bail!("Sequence cannot end with newline");
}
data.extend_from_slice(b">");
data.extend_from_slice(id);
data.extend_from_slice(b"\n");
data.extend_from_slice(seq);
data.extend_from_slice(b"\n");
let id_idx = id.len() + 1;
let seq_idx = seq.len() + 1;
Ok(Self {
data,
id: id_idx,
seq: seq_idx,
plus: None,
qual: None,
})
}
#[must_use]
pub fn new_fastq_from_parts(id: &[u8], seq: &[u8], qual: &[u8]) -> Result<Self> {
let mut data = Vec::with_capacity(id.len() + seq.len() + qual.len() + 4);
if id.starts_with(b"@") {
bail!("ID cannot start with '@'");
}
if seq.len() != qual.len() {
bail!("Sequence and Quality must be the same length");
}
if id.ends_with(b"\n") {
bail!("ID cannot end with newline");
}
if seq.ends_with(b"\n") {
bail!("Sequence cannot end with newline");
}
if qual.ends_with(b"\n") {
bail!("Quality cannot end with newline");
}
data.extend_from_slice(b"@");
data.extend_from_slice(id);
data.extend_from_slice(b"\n");
data.extend_from_slice(seq);
data.extend_from_slice(b"\n+\n");
data.extend_from_slice(qual);
data.extend_from_slice(b"\n");
let id_idx = id.len() + 1;
let seq_idx = seq.len() + 1;
let plus_idx = 2;
let qual_idx = qual.len() + 1;
Ok(Self {
data,
id: id_idx,
seq: seq_idx,
plus: Some(plus_idx),
qual: Some(qual_idx),
})
}
#[must_use]
pub fn is_fasta(&self) -> bool {
self.plus.is_none() & self.qual.is_none()
}
#[must_use]
pub fn is_fastq(&self) -> bool {
self.plus.is_some() & self.qual.is_some()
}
#[must_use]
pub fn valid_header(&self) -> bool {
if self.is_fasta() {
self.data[0] == b'>'
} else {
self.data[0] == b'@'
}
}
#[must_use]
pub fn empty(&self) -> bool {
(self.id == 0) | (self.seq == 0)
}
#[must_use]
pub fn id_range(&self) -> Range<usize> {
1..self.id
}
#[must_use]
pub fn seq_range(&self) -> Range<usize> {
1 + self.id..self.id + self.seq
}
#[must_use]
pub fn plus_range(&self) -> Option<Range<usize>> {
match self.plus {
Some(plus) => Some(1 + self.id + self.seq..self.id + self.seq + plus),
None => None,
}
}
#[must_use]
pub fn qual_range(&self) -> Option<Range<usize>> {
let plus = match self.plus {
Some(plus) => plus,
None => return None,
};
match self.qual {
Some(qual) => Some(1 + self.id + self.seq + plus..self.id + self.seq + plus + qual),
None => None,
}
}
#[must_use]
pub fn id(&self) -> &[u8] {
&self.data[self.id_range()]
}
#[must_use]
pub fn seq(&self) -> &[u8] {
&self.data[self.seq_range()]
}
#[must_use]
pub fn seq_mut(&mut self) -> &mut [u8] {
let range = self.seq_range();
&mut self.data[range]
}
#[must_use]
pub fn plus(&self) -> Option<&[u8]> {
if let Some(range) = self.plus_range() {
Some(&self.data[range])
} else {
None
}
}
#[must_use]
pub fn qual(&self) -> Option<&[u8]> {
if let Some(range) = self.qual_range() {
Some(&self.data[range])
} else {
None
}
}
#[must_use]
pub fn qual_mut(&mut self) -> Option<&mut [u8]> {
if let Some(range) = self.qual_range() {
Some(&mut self.data[range])
} else {
None
}
}
#[must_use]
pub fn data(&self) -> &[u8] {
&self.data
}
#[must_use]
pub fn valid(&self) -> bool {
if self.empty() {
false
} else {
self.valid_sequence()
}
}
#[must_use]
pub fn seq_upper(&self) -> Vec<u8> {
self.seq()
.iter()
.map(|c| if c & b' ' == 0 { *c } else { c ^ b' ' })
.collect()
}
#[must_use]
pub fn seq_rev_comp(&self) -> Vec<u8> {
self.seq()
.iter()
.rev()
.map(|c| if c & 2 == 0 { c ^ 21 } else { c ^ 4 })
.collect()
}
pub fn fix(&mut self) {
self.seq_mut().iter_mut().for_each(|c| {
if !matches!(
c,
b'A' | b'a' | b'C' | b'c' | b'G' | b'g' | b'T' | b't' | b'N' | b'n'
) {
*c = b'N';
}
});
}
pub fn upper(&mut self) {
self.seq_mut().iter_mut().for_each(|c| {
if *c & b' ' != 0 {
*c ^= b' ';
}
});
}
pub fn rev_comp(&mut self) {
self.seq_mut().reverse();
self.seq_mut().iter_mut().for_each(|c| {
if *c & 2 == 0 {
*c ^= 21;
} else {
*c ^= 4;
}
});
if let Some(qual) = self.qual_mut() {
qual.reverse();
}
}
pub fn insert_seq(&mut self, seq: &[u8], pos: usize) -> Result<()> {
if pos >= self.seq {
bail!("Cannot insert at a position greater than the sequence length");
}
let insert_pos = self.seq_range().start + pos;
self.data
.splice(insert_pos..insert_pos, seq.iter().cloned());
self.seq += seq.len();
self.insert_qual(seq.len(), pos)?;
Ok(())
}
fn insert_qual(&mut self, insert_size: usize, pos: usize) -> Result<()> {
if let Some(qual) = self.qual {
if pos > qual {
bail!("Cannot insert at a position greater than the quality length");
}
let insert_pos = self.qual_range().unwrap().start + pos;
self.data
.splice(insert_pos..insert_pos, vec![DEFAULT_QUAL; insert_size]);
self.qual = Some(qual + insert_size);
}
Ok(())
}
pub fn insert_seq_left(&mut self, seq: &[u8]) -> Result<()> {
self.insert_seq(seq, 0)
}
pub fn insert_seq_right(&mut self, seq: &[u8]) -> Result<()> {
self.insert_seq(seq, self.seq - 1)
}
pub fn trim_left(&mut self, size: usize) -> Result<()> {
if size > self.seq {
bail!("Cannot trim more than the sequence length");
}
let seq_start = self.seq_range().start;
let trim_end = seq_start + size;
self.data.drain(seq_start..trim_end);
self.seq = self.seq.saturating_sub(size);
if self.is_fastq() {
let qual_start = self.qual_range().unwrap().start;
let trim_end = qual_start + size;
self.data.drain(qual_start..trim_end);
self.qual = Some(self.qual.unwrap().saturating_sub(size));
}
Ok(())
}
pub fn trim_right(&mut self, size: usize) -> Result<()> {
if size > self.seq {
bail!("Cannot trim more than the sequence length");
}
let seq_end = self.seq_range().end;
let trim_start = seq_end - size;
self.data.drain(trim_start..seq_end);
self.seq = self.seq.saturating_sub(size);
if self.is_fastq() {
let qual_end = self.qual_range().unwrap().end;
let trim_start = qual_end - size;
self.data.drain(trim_start..qual_end);
self.qual = Some(self.qual.unwrap().saturating_sub(size));
}
Ok(())
}
fn valid_sequence(&self) -> bool {
self.seq().iter().all(|b| {
matches!(
b,
b'A' | b'a' | b'C' | b'c' | b'G' | b'g' | b'T' | b't' | b'N' | b'n' | b'U' | b'u'
)
})
}
#[must_use]
pub fn as_str_checked(&self) -> Result<&str, std::str::Utf8Error> {
std::str::from_utf8(self.data())
}
#[must_use]
pub fn id_str_checked(&self) -> Result<&str, std::str::Utf8Error> {
std::str::from_utf8(self.id())
}
#[must_use]
pub fn seq_str_checked(&self) -> Result<&str, std::str::Utf8Error> {
std::str::from_utf8(self.seq())
}
#[must_use]
pub fn qual_str_checked(&self) -> Option<Result<&str, std::str::Utf8Error>> {
if let Some(qual) = self.qual() {
Some(std::str::from_utf8(qual))
} else {
None
}
}
#[must_use]
pub fn as_str(&self) -> &str {
self.as_str_checked().unwrap()
}
#[must_use]
pub fn id_str(&self) -> &str {
self.id_str_checked().unwrap()
}
#[must_use]
pub fn seq_str(&self) -> &str {
self.seq_str_checked().unwrap()
}
#[must_use]
pub fn qual_str(&self) -> Option<&str> {
self.qual_str_checked().map(|qual| qual.unwrap())
}
}
impl Default for Record {
fn default() -> Self {
Self::new()
}
}
impl Into<String> for Record {
fn into(self) -> String {
self.as_str().to_string()
}
}
#[cfg(test)]
mod test {
use super::Record;
fn gen_valid_fasta() -> (Vec<u8>, usize, usize) {
(b">seq.0\nACGT\n".to_vec(), 6, 5)
}
fn gen_invalid_fasta() -> (Vec<u8>, usize, usize) {
(b">seq.0\nABCD\n".to_vec(), 6, 5)
}
fn gen_valid_fasta_lower() -> (Vec<u8>, usize, usize) {
(b">seq.0\nacgt\n".to_vec(), 6, 5)
}
fn gen_invalid_fasta_lower() -> (Vec<u8>, usize, usize) {
(b">seq.0\nabcd\n".to_vec(), 6, 5)
}
fn gen_valid_fasta_rev() -> (Vec<u8>, usize, usize) {
(b">seq.0\nATCGGCTA\n".to_vec(), 6, 9)
}
fn gen_valid_fasta_rev_lower() -> (Vec<u8>, usize, usize) {
(b">seq.0\natcggcta\n".to_vec(), 6, 9)
}
fn gen_valid_fastq() -> (Vec<u8>, usize, usize, usize, usize) {
(b"@seq.0\nACGT\n+\n1234\n".to_vec(), 6, 5, 2, 5)
}
fn gen_invalid_fastq() -> (Vec<u8>, usize, usize, usize, usize) {
(b"@seq.0\nABCD\n+\n1234\n".to_vec(), 6, 5, 2, 5)
}
#[test]
fn create() {
let record = Record::new();
assert!(record.empty());
assert!(!record.valid());
}
#[test]
fn valid_fasta() {
let (fasta, id, seq) = gen_valid_fasta();
let record = Record::new_fasta(fasta, id, seq);
assert!(!record.empty());
assert!(record.valid());
assert_eq!(record.id(), b"seq.0");
assert_eq!(record.seq(), b"ACGT");
}
#[test]
fn invalid_fasta() {
let (fasta, id, seq) = gen_invalid_fasta();
let record = Record::new_fasta(fasta, id, seq);
assert!(!record.empty());
assert!(!record.valid());
}
#[test]
fn valid_fastq() {
let (fasta, id, seq, plus, qual) = gen_valid_fastq();
let record = Record::new_fastq(fasta, id, seq, plus, qual);
assert!(!record.empty());
assert!(record.valid());
assert_eq!(record.id(), b"seq.0");
assert_eq!(record.seq(), b"ACGT");
assert_eq!(record.plus().unwrap(), b"+");
assert_eq!(record.qual().unwrap(), b"1234");
}
#[test]
fn invalid_fastq() {
let (fasta, id, seq, plus, qual) = gen_invalid_fastq();
let record = Record::new_fastq(fasta, id, seq, plus, qual);
assert!(!record.empty());
assert!(!record.valid());
}
#[test]
fn valid_lowercase() {
let (fasta, id, seq) = gen_valid_fasta_lower();
let record = Record::new_fasta(fasta, id, seq);
assert!(!record.empty());
assert!(record.valid());
assert_eq!(record.id(), b"seq.0");
assert_eq!(record.seq(), b"acgt");
}
#[test]
fn invalid_lowercase() {
let (fasta, id, seq) = gen_invalid_fasta_lower();
let record = Record::new_fasta(fasta, id, seq);
assert!(!record.empty());
assert!(!record.valid());
}
#[test]
fn upper_conversion() {
let (fasta, id, seq) = gen_valid_fasta_lower();
let record = Record::new_fasta(fasta, id, seq);
assert!(!record.empty());
assert!(record.valid());
assert_eq!(record.seq_upper(), b"ACGT");
}
#[test]
fn reverse_complement() {
let (fasta, id, seq) = gen_valid_fasta_rev();
let record = Record::new_fasta(fasta, id, seq);
assert!(!record.empty());
assert!(record.valid());
assert_eq!(record.seq_rev_comp(), b"TAGCCGAT");
}
#[test]
fn reverse_complement_lower() {
let (fasta, id, seq) = gen_valid_fasta_rev_lower();
let record = Record::new_fasta(fasta, id, seq);
assert!(!record.empty());
assert!(record.valid());
assert_eq!(record.seq_rev_comp(), b"tagccgat");
}
#[test]
fn invalid_fix_fasta() {
let (fasta, id, seq) = gen_invalid_fasta();
let mut record = Record::new_fasta(fasta, id, seq);
assert!(!record.empty());
assert!(!record.valid());
assert_eq!(record.seq(), b"ABCD");
record.fix();
assert!(record.valid());
assert_eq!(record.seq(), b"ANCN");
}
#[test]
fn invalid_fix_fastq() {
let (fasta, id, seq, plus, qual) = gen_invalid_fastq();
let mut record = Record::new_fastq(fasta, id, seq, plus, qual);
assert!(!record.empty());
assert!(!record.valid());
assert_eq!(record.seq(), b"ABCD");
record.fix();
assert!(record.valid());
assert_eq!(record.seq(), b"ANCN");
}
#[test]
fn upper_inplace() {
let (fasta, id, seq) = gen_valid_fasta_lower();
let mut record = Record::new_fasta(fasta, id, seq);
assert!(!record.empty());
assert!(record.valid());
assert_eq!(record.seq(), b"acgt");
record.upper();
assert_eq!(record.seq(), b"ACGT");
}
#[test]
fn upper_nochange_inplace() {
let (fasta, id, seq) = gen_valid_fasta();
let mut record = Record::new_fasta(fasta, id, seq);
assert!(!record.empty());
assert!(record.valid());
assert_eq!(record.seq(), b"ACGT");
record.upper();
assert_eq!(record.seq(), b"ACGT");
}
#[test]
fn reverse_complement_inplace() {
let (fasta, id, seq) = gen_valid_fasta_rev();
let mut record = Record::new_fasta(fasta, id, seq);
assert!(!record.empty());
assert!(record.valid());
assert_eq!(record.seq(), b"ATCGGCTA");
record.rev_comp();
assert_eq!(record.seq(), b"TAGCCGAT");
}
#[test]
fn reverse_complement_fastq_inplace() {
let (fasta, id, seq, plus, qual) = gen_valid_fastq();
let mut record = Record::new_fastq(fasta, id, seq, plus, qual);
assert!(!record.empty());
assert!(record.valid());
assert_eq!(record.seq(), b"ACGT");
assert_eq!(record.qual().unwrap(), b"1234");
record.rev_comp();
assert_eq!(record.seq(), b"ACGT");
assert_eq!(record.qual().unwrap(), b"4321");
}
#[test]
fn fasta_str_methods() {
let (fasta, id, seq) = gen_valid_fasta();
let expected = String::from(">seq.0\nACGT\n");
let record = Record::new_fasta(fasta, id, seq);
assert_eq!(record.id_str(), "seq.0");
assert_eq!(record.id_str_checked(), Ok("seq.0"));
assert_eq!(record.seq_str(), "ACGT");
assert_eq!(record.seq_str_checked(), Ok("ACGT"));
let repr = record.as_str();
assert_eq!(repr, &expected);
let repr: String = record.into();
assert_eq!(repr, expected);
}
#[test]
fn fastq_str_methods() {
let (fasta, id, seq, plus, qual) = gen_valid_fastq();
let expected = String::from("@seq.0\nACGT\n+\n1234\n");
let record = Record::new_fastq(fasta, id, seq, plus, qual);
assert_eq!(record.id_str(), "seq.0");
assert_eq!(record.seq_str(), "ACGT");
assert_eq!(record.qual_str(), Some("1234"));
assert_eq!(record.id_str_checked(), Ok("seq.0"));
assert_eq!(record.seq_str_checked(), Ok("ACGT"));
assert_eq!(record.qual_str_checked(), Some(Ok("1234")));
let repr = record.as_str();
assert_eq!(repr, &expected);
let repr: String = record.into();
assert_eq!(repr, expected);
}
#[test]
fn fasta_trim_left_oversized() {
let (fasta, id, seq) = gen_valid_fasta();
let mut record = Record::new_fasta(fasta, id, seq);
assert!(record.trim_left(10).is_err());
}
#[test]
fn fasta_trim_right_oversized() {
let (fasta, id, seq) = gen_valid_fasta();
let mut record = Record::new_fasta(fasta, id, seq);
assert!(record.trim_right(10).is_err());
}
#[test]
fn fastq_trim_left_oversized() {
let (fasta, id, seq, plus, qual) = gen_valid_fastq();
let mut record = Record::new_fastq(fasta, id, seq, plus, qual);
assert!(record.trim_left(10).is_err());
}
#[test]
fn fastq_trim_right_oversized() {
let (fasta, id, seq, plus, qual) = gen_valid_fastq();
let mut record = Record::new_fastq(fasta, id, seq, plus, qual);
assert!(record.trim_right(10).is_err());
}
#[test]
fn fasta_trim_left() {
let (fasta, id, seq) = gen_valid_fasta();
let mut record = Record::new_fasta(fasta, id, seq);
record.trim_left(2).unwrap();
assert_eq!(record.id_str(), "seq.0");
assert_eq!(record.seq_str(), "GT");
assert_eq!(record.as_str(), ">seq.0\nGT\n");
}
#[test]
fn fastq_trim_left() {
let (fasta, id, seq, plus, qual) = gen_valid_fastq();
let mut record = Record::new_fastq(fasta, id, seq, plus, qual);
record.trim_left(2).unwrap();
assert_eq!(record.id_str(), "seq.0");
assert_eq!(record.seq_str(), "GT");
assert_eq!(record.qual_str(), Some("34"));
assert_eq!(record.as_str(), "@seq.0\nGT\n+\n34\n");
}
#[test]
fn fasta_trim_right() {
let (fasta, id, seq) = gen_valid_fasta();
let mut record = Record::new_fasta(fasta, id, seq);
record.trim_right(2).unwrap();
assert_eq!(record.id_str(), "seq.0");
assert_eq!(record.seq_str(), "AC");
assert_eq!(record.as_str(), ">seq.0\nAC\n");
}
#[test]
fn fastq_trim_right() {
let (fasta, id, seq, plus, qual) = gen_valid_fastq();
let mut record = Record::new_fastq(fasta, id, seq, plus, qual);
record.trim_right(2).unwrap();
assert_eq!(record.id_str(), "seq.0");
assert_eq!(record.seq_str(), "AC");
assert_eq!(record.qual_str(), Some("12"));
assert_eq!(record.as_str(), "@seq.0\nAC\n+\n12\n");
}
#[test]
fn fasta_insert_left() {
let (fasta, id, seq) = gen_valid_fasta();
let mut record = Record::new_fasta(fasta, id, seq);
record.insert_seq_left(b"TT").unwrap();
assert_eq!(record.id_str(), "seq.0");
assert_eq!(record.seq_str(), "TTACGT");
assert_eq!(record.as_str(), ">seq.0\nTTACGT\n");
}
#[test]
fn fastq_insert_left() {
let (fasta, id, seq, plus, qual) = gen_valid_fastq();
let mut record = Record::new_fastq(fasta, id, seq, plus, qual);
record.insert_seq_left(b"TT").unwrap();
assert_eq!(record.id_str(), "seq.0");
assert_eq!(record.seq_str(), "TTACGT");
assert_eq!(record.qual_str(), Some("FF1234"));
assert_eq!(record.as_str(), "@seq.0\nTTACGT\n+\nFF1234\n");
}
#[test]
fn fasta_insert_right() {
let (fasta, id, seq) = gen_valid_fasta();
let mut record = Record::new_fasta(fasta, id, seq);
record.insert_seq_right(b"TT").unwrap();
assert_eq!(record.id_str(), "seq.0");
assert_eq!(record.seq_str(), "ACGTTT");
assert_eq!(record.as_str(), ">seq.0\nACGTTT\n");
}
#[test]
fn fastq_insert_right() {
let (fasta, id, seq, plus, qual) = gen_valid_fastq();
let mut record = Record::new_fastq(fasta, id, seq, plus, qual);
record.insert_seq_right(b"TT").unwrap();
assert_eq!(record.id_str(), "seq.0");
assert_eq!(record.seq_str(), "ACGTTT");
assert_eq!(record.qual_str(), Some("1234FF"));
assert_eq!(record.as_str(), "@seq.0\nACGTTT\n+\n1234FF\n");
}
#[test]
fn fasta_insert_middle() {
let (fasta, id, seq) = gen_valid_fasta();
let mut record = Record::new_fasta(fasta, id, seq);
record.insert_seq(b"TT", 2).unwrap();
assert_eq!(record.id_str(), "seq.0");
assert_eq!(record.seq_str(), "ACTTGT");
assert_eq!(record.as_str(), ">seq.0\nACTTGT\n");
}
#[test]
fn fastq_insert_middle() {
let (fasta, id, seq, plus, qual) = gen_valid_fastq();
let mut record = Record::new_fastq(fasta, id, seq, plus, qual);
record.insert_seq(b"TT", 2).unwrap();
assert_eq!(record.id_str(), "seq.0");
assert_eq!(record.seq_str(), "ACTTGT");
assert_eq!(record.qual_str(), Some("12FF34"));
assert_eq!(record.as_str(), "@seq.0\nACTTGT\n+\n12FF34\n");
}
#[test]
fn fasta_insert_custom_oversized() {
let (fasta, id, seq) = gen_valid_fasta();
let mut record = Record::new_fasta(fasta, id, seq);
assert!(record.insert_seq(b"TT", 10).is_err());
let (fasta, id, seq) = gen_valid_fasta();
let mut record = Record::new_fasta(fasta, id, seq);
assert!(record.insert_seq(b"TT", seq).is_err());
}
#[test]
fn fastq_insert_custom_oversized() {
let (fasta, id, seq, plus, qual) = gen_valid_fastq();
let mut record = Record::new_fastq(fasta, id, seq, plus, qual);
assert!(record.insert_seq(b"TT", 10).is_err());
let (fasta, id, seq, plus, qual) = gen_valid_fastq();
let mut record = Record::new_fastq(fasta, id, seq, plus, qual);
assert!(record.insert_seq(b"TT", seq).is_err());
}
#[test]
fn init_fastq_from_parts() {
let id = b"seq.0";
let seq = b"ACGT";
let qual = b"1234";
let record = Record::new_fastq_from_parts(id, seq, qual).unwrap();
let expected_plus = vec![b'+'];
assert_eq!(record.id_str(), "seq.0");
assert_eq!(record.seq_str(), "ACGT");
assert_eq!(record.plus(), Some(expected_plus.as_slice()));
assert_eq!(record.qual_str(), Some("1234"));
}
#[test]
fn init_fasta_from_parts() {
let id = b"seq.0";
let seq = b"ACGT";
let record = Record::new_fasta_from_parts(id, seq).unwrap();
assert_eq!(record.id_str(), "seq.0");
assert_eq!(record.seq_str(), "ACGT");
assert!(record.plus().is_none());
assert!(record.qual_str().is_none());
}
#[test]
fn init_fastq_from_parts_invalid_id_a() {
let id = b"seq.0\n";
let seq = b"ACGT";
let qual = b"1234";
assert!(Record::new_fastq_from_parts(id, seq, qual).is_err());
}
#[test]
fn init_fastq_from_parts_invalid_id_b() {
let id = b"@seq.0";
let seq = b"ACGT";
let qual = b"1234";
assert!(Record::new_fastq_from_parts(id, seq, qual).is_err());
}
#[test]
fn init_fastq_from_parts_invalid_seq_a() {
let id = b"seq.0";
let seq = b"ACGT\n";
let qual = b"12345";
assert!(Record::new_fastq_from_parts(id, seq, qual).is_err());
}
#[test]
fn init_fastq_from_parts_invalid_seq_qual_size_mismatch_a() {
let id = b"seq.0";
let seq = b"ACGTA";
let qual = b"1234";
assert!(Record::new_fastq_from_parts(id, seq, qual).is_err());
}
#[test]
fn init_fastq_from_parts_invalid_seq_qual_size_mismatch_b() {
let id = b"seq.0";
let seq = b"ACGT";
let qual = b"12345";
assert!(Record::new_fastq_from_parts(id, seq, qual).is_err());
}
#[test]
fn init_fastq_from_parts_invalid_qual_a() {
let id = b"seq.0";
let seq = b"ACGTA";
let qual = b"1234\n";
assert!(Record::new_fastq_from_parts(id, seq, qual).is_err());
}
#[test]
fn init_fasta_from_parts_invalid_id_a() {
let id = b"seq.0\n";
let seq = b"ACGT";
assert!(Record::new_fasta_from_parts(id, seq).is_err());
}
#[test]
fn init_fasta_from_parts_invalid_id_b() {
let id = b">seq.0";
let seq = b"ACGT";
assert!(Record::new_fasta_from_parts(id, seq).is_err());
}
#[test]
fn init_fasta_from_parts_invalid_seq_a() {
let id = b"seq.0";
let seq = b"ACGT\n";
assert!(Record::new_fasta_from_parts(id, seq).is_err());
}
}