// ahda: Pseudoalignment compression and conversion between formats.
//
// Copyright 2025 Tommi Mäklin [tommi@maklin.fi].
//
// Copyrights in this project are retained by contributors. No copyright assignment
// is required to contribute to this project.
//
// Except as otherwise noted (below and/or in individual files), this
// project is licensed under the Apache License, Version 2.0
// <LICENSE-APACHE> or <http://www.apache.org/licenses/LICENSE-2.0> or
// the MIT license, <LICENSE-MIT> or <http://opensource.org/licenses/MIT>,
// at your option.
//
//! Printer for outputting [PseudoAln] records as plain text in any supported [Format].
//!
//! Can be used to convert any iterator over [PseudoAln] data to their plain
//! text representation.
//!
//! Returns 1 line at a time using next().
//!
//! If the desired output format has header lines, this can be formatted by
//! Printer using [print_header](Printer::print_header).
//!
//! ## Usage
//!
//! ### Print PseudoAln records stored in memory
//!
//! ```rust
//! use ahda::{Format, PseudoAln};
//! use ahda::printer::Printer;
//! use std::io::{Cursor, Write};
//!
//! let targets = vec!["chr.fasta".as_bytes().to_vec(), "plasmid.fasta".as_bytes().to_vec(), "virus.fasta".as_bytes().to_vec()];
//! let queries = vec!["r1".as_bytes().to_vec(), "r2".as_bytes().to_vec(), "r651903".as_bytes().to_vec(), "r7543".as_bytes().to_vec(), "r16".as_bytes().to_vec()];
//! let name = "sample".as_bytes().to_vec();
//!
//! let data = vec![
//! PseudoAln { ones: Some(vec![2]), ones_names: Some(vec!["virus.fasta".as_bytes().to_vec()]), query_id: Some(0), query_name: Some("r1".as_bytes().to_vec()) },
//! PseudoAln { ones: Some(vec![0, 2]), ones_names: Some(vec!["chr.fasta".as_bytes().to_vec(), "virus.fasta".as_bytes().to_vec()]), query_id: Some(3), query_name: Some("r7543".as_bytes().to_vec()) },
//! PseudoAln { ones: Some(vec![0, 1, 2]), ones_names: Some(vec!["chr.fasta".as_bytes().to_vec(), "plasmid.fasta".as_bytes().to_vec(), "virus.fasta".as_bytes().to_vec()]), query_id: Some(4), query_name: Some("r16".as_bytes().to_vec()) },
//! PseudoAln { ones: Some(vec![]), ones_names: Some(vec![]), query_id: Some(2), query_name: Some("r651903".as_bytes().to_vec()) }
//! ];
//!
//! let mut iter = data.into_iter(); // Printer expectes PseudoAln, not &PseudoAln
//!
//! let mut printer = Printer::new(&mut iter, &targets, &name, queries.len(), Format::Metagraph).unwrap();
//!
//! // Print the records in Metagraph format
//! let mut output: Cursor<Vec<u8>> = Cursor::new(Vec::new());
//! for line in printer.by_ref() {
//! output.write_all(&line.unwrap()).unwrap()
//! }
//!
//! // Expect this plain text output
//! // 0 r1 virus.fasta
//! // 3 r7543 chr.fasta:virus.fasta
//! // 4 r16 chr.fasta:plasmid.fasta:virus.fasta
//! // 2 r651903
//! //
//! let mut expected: Vec<u8> = Vec::new();
//! expected.append(&mut b"0\tr1\tvirus.fasta\n".to_vec());
//! expected.append(&mut b"3\tr7543\tchr.fasta:virus.fasta\n".to_vec());
//! expected.append(&mut b"4\tr16\tchr.fasta:plasmid.fasta:virus.fasta\n".to_vec());
//! expected.append(&mut b"2\tr651903\t\n".to_vec());
//!
//! assert_eq!(output.get_ref(), &expected);
//! ```
//!
//! ### Print encoded PseudoAln records
//!
//! Initialize a [Decoder](crate::decoder::Decoder) on the encoded bytes and pass
//! this to a Printer to print all records.
//!
//! ```rust
//! use ahda::decoder::Decoder;
//! use ahda::encoder::Encoder;
//! use ahda::printer::Printer;
//! use ahda::{Format, PseudoAln};
//! use std::io::{Cursor, Seek, Write};
//!
//! // Set up some encoded data
//! let targets = vec!["chr.fasta".as_bytes().to_vec(), "plasmid.fasta".as_bytes().to_vec(), "virus.fasta".as_bytes().to_vec()];
//! let queries = vec!["r1".as_bytes().to_vec(), "r2".as_bytes().to_vec(), "r651903".as_bytes().to_vec(), "r7543".as_bytes().to_vec(), "r16".as_bytes().to_vec()];
//! let name = "sample".as_bytes().to_vec();
//!
//! let data: Vec<PseudoAln> = vec![
//! PseudoAln { ones: Some(vec![2]), ones_names: Some(vec!["virus.fasta".as_bytes().to_vec()]), query_id: Some(0), query_name: Some("r1".as_bytes().to_vec()) },
//! PseudoAln { ones: Some(vec![0, 2]), ones_names: Some(vec!["chr.fasta".as_bytes().to_vec(), "virus.fasta".as_bytes().to_vec()]), query_id: Some(3), query_name: Some("r7543".as_bytes().to_vec()) },
//! PseudoAln { ones: Some(vec![0, 1, 2]), ones_names: Some(vec!["chr.fasta".as_bytes().to_vec(), "plasmid.fasta".as_bytes().to_vec(), "virus.fasta".as_bytes().to_vec()]), query_id: Some(4), query_name: Some("r16".as_bytes().to_vec()) },
//! PseudoAln { ones: Some(vec![]), ones_names: Some(vec![]), query_id: Some(2), query_name: Some("r651903".as_bytes().to_vec()) }
//! ];
//!
//! let mut iter = data.into_iter(); // Encoder::new expects PseudoAln and doesn't work on &PseudoAln
//! let mut encoder = Encoder::new(&mut iter, &targets, &name, queries.len()).unwrap();
//!
//! let mut bytes = encoder.encode_file_header_and_flags().unwrap();
//! for mut data in encoder.by_ref() {
//! bytes.append(&mut data.unwrap());
//! }
//!
//! // Decode from `bytes` to Metagraph plaintext format
//! let mut input = Cursor::new(&bytes);
//! let mut decoder = Decoder::new(&mut input).unwrap().map(|block| block.unwrap());
//! let mut printer = Printer::new(&mut decoder, &targets, &name, queries.len(), Format::Metagraph).unwrap();
//!
//! let mut output: Vec<u8> = Vec::new();
//! for mut line in printer.by_ref() {
//! output.append(&mut line.unwrap());
//! }
//!
//! // Expect this plain text output
//! // 0 r1 virus.fasta
//! // 3 r7543 chr.fasta:virus.fasta
//! // 4 r16 chr.fasta:plasmid.fasta:virus.fasta
//! // 2 r651903
//! //
//! let mut expected: Vec<u8> = Vec::new();
//! expected.append(&mut b"0\tr1\tvirus.fasta\n".to_vec());
//! expected.append(&mut b"3\tr7543\tchr.fasta:virus.fasta\n".to_vec());
//! expected.append(&mut b"4\tr16\tchr.fasta:plasmid.fasta:virus.fasta\n".to_vec());
//! expected.append(&mut b"2\tr651903\t\n".to_vec());
//!
//! assert_eq!(output, expected);
//! ```
//!
use crate::Format;
use crate::PseudoAln;
use crate::headers::file::FileHeader;
use crate::headers::file::FileFlags;
use crate::headers::file::build_file_header_and_flags;
use crate::compression::MetadataCompression;
use bifrost::format_bifrost_header;
use ahda_tsv::format_ahda_tsv_header;
use ahda_tsv::format_ahda_tsv_line;
use bifrost::format_bifrost_line;
use fulgor::format_fulgor_line;
use fulgor::format_fulgor_line_v4;
use metagraph::format_metagraph_line;
#[cfg(feature = "sam")]
use sam::{
build_sam_header,
format_sam_line,
format_sam_header,
};
use themisto::format_themisto_line;
// Format specific implementations
pub mod ahda_tsv;
pub mod bifrost;
pub mod fulgor;
pub mod metagraph;
#[cfg(feature = "sam")]
pub mod sam;
pub mod themisto;
type E = Box<dyn std::error::Error>;
/// Format [PseudoAln] as plain text data,
pub struct Printer<'a, I: Iterator> where I: Iterator<Item=PseudoAln> {
// Inputs
records: &'a mut I,
header: FileHeader,
flags: FileFlags,
#[cfg(feature = "sam")]
sam_header: Option<noodles_sam::Header>,
index: usize,
pub format: Format,
}
impl<'a, I: Iterator> Printer<'a, I> where I: Iterator<Item=PseudoAln> {
/// # Initialize with required info about the records.
///
/// ## Usage
///
/// ```rust
/// use ahda::Format;
/// use ahda::PseudoAln;
/// use ahda::printer::Printer;
///
/// let data: Vec<PseudoAln> = Vec::new();
/// let targets: Vec<Vec<u8>> = Vec::new();
/// let sample_name: Vec<u8> = Vec::new();
/// let n_queries: usize = 0;
/// let format = Format::default();
///
/// let mut data_it = data.into_iter();
/// let printer = Printer::new(&mut data_it, &targets, &sample_name, n_queries, format);
/// assert!(printer.is_ok());
/// ```
pub fn new(
records: &'a mut I,
targets: &[Vec<u8>],
sample_name: &[u8],
n_queries: usize,
format: Format,
) -> Result<Self, E> {
let (header, flags) = build_file_header_and_flags(targets, n_queries, sample_name, &MetadataCompression::default())?;
Printer::new_from_header_and_flags(records, header, flags, format)
}
/// Initialize from a [FileHeader] and [FileFlags].
///
/// ## Usage
///
/// ```rust
/// use ahda::Format;
/// use ahda::PseudoAln;
/// use ahda::headers::file::{FileHeader, FileFlags};
/// use ahda::printer::Printer;
///
/// let data: Vec<PseudoAln> = Vec::new();
/// let format = Format::default();
/// let header = FileHeader::default();
/// let flags = FileFlags::default();
///
/// let mut data_it = data.into_iter();
/// let printer = Printer::new_from_header_and_flags(&mut data_it, header, flags, format);
/// assert!(printer.is_ok());
/// ```
pub fn new_from_header_and_flags(
records: &'a mut I,
header: FileHeader,
flags: FileFlags,
format: Format,
) -> Result<Self, E> {
#[cfg(feature = "sam")]
if format == Format::SAM {
todo!("printing .sam plain text data.")
}
#[cfg(feature = "sam")]
let sam_header = if format == Format::SAM {
Some(sam::build_sam_header(&flags.target_names)?)
} else {
None
};
Ok(Printer{
records,
header, flags,
#[cfg(feature = "sam")]
sam_header,
index: 0,
format,
})
}
}
impl<'a, I: Iterator> Printer<'a, I> where I: Iterator<Item=PseudoAln> {
/// Print the header line, or nothing, for the specified format.
fn print_header(
&mut self,
) -> Option<Result<Vec<u8>, E>> {
let mut out: Vec<u8> = Vec::new();
match self.format {
Format::Themisto => None,
Format::Fulgor => None,
Format::FulgorV4 => None,
Format::Metagraph => None,
Format::Bifrost => {
match format_bifrost_header(&self.flags.target_names, &mut out) {
Ok(_) => Some(Ok(out)),
Err(e) => Some(Err(e)),
}
},
#[cfg(feature = "sam")]
Format::SAM => {
match build_sam_header(&self.flags.target_names) {
Ok(header) => self.sam_header = Some(header),
Err(e) => return Some(Err(e)),
}
match format_sam_header(self.sam_header.as_ref().unwrap(), &mut out) {
Ok(_) => Some(Ok(out)),
Err(e) => Some(Err(e)),
}
},
Format::AhdaTSV => {
match format_ahda_tsv_header(&self.flags.target_names, &mut out) {
Ok(_) => Some(Ok(out)),
Err(e) => Some(Err(e)),
}
}
}
}
}
impl<'a, I: Iterator> Iterator for Printer<'a, I> where I: Iterator<Item=PseudoAln> {
type Item = Result<Vec<u8>, E>;
/// Get the next record
///
/// ## Usage
///
/// ### Print records in a format without a header line
///
/// ```rust
/// use ahda::{Format, PseudoAln};
/// use ahda::printer::Printer;
/// use std::io::{Cursor, Write};
///
/// let targets = vec!["chr.fasta".as_bytes().to_vec(), "plasmid.fasta".as_bytes().to_vec(), "virus.fasta".as_bytes().to_vec()];
/// let queries = vec!["r1".as_bytes().to_vec(), "r2".as_bytes().to_vec(), "r651903".as_bytes().to_vec(), "r7543".as_bytes().to_vec(), "r16".as_bytes().to_vec()];
/// let name = "sample".as_bytes().to_vec();
///
/// let data = vec![
/// PseudoAln { ones: Some(vec![2]), ones_names: Some(vec!["virus.fasta".as_bytes().to_vec()]), query_id: Some(0), query_name: Some("r1".as_bytes().to_vec()) },
/// ];
///
/// let expected: Vec<u8> = b"0\tr1\tvirus.fasta\n".to_vec();
///
/// let mut iter = data.into_iter();
///
/// let printer = Printer::new(&mut iter, &targets, &name, queries.len(), Format::Metagraph);
/// assert!(printer.is_ok());
/// let mut printer = printer.unwrap();
///
/// let record = printer.next();
/// assert!(record.is_some());
/// assert!(record.as_ref().unwrap().is_ok());
/// assert_eq!(record.unwrap().unwrap(), expected);
///
/// let record = printer.next();
/// assert!(record.is_none());
/// ```
///
/// ### Print records in a format with a header line
///
/// ```rust
/// use ahda::{Format, PseudoAln};
/// use ahda::printer::Printer;
/// use std::io::{Cursor, Write};
///
/// let targets = vec!["chr.fasta".as_bytes().to_vec(), "plasmid.fasta".as_bytes().to_vec(), "virus.fasta".as_bytes().to_vec()];
/// let queries = vec!["r1".as_bytes().to_vec(), "r2".as_bytes().to_vec(), "r651903".as_bytes().to_vec(), "r7543".as_bytes().to_vec(), "r16".as_bytes().to_vec()];
/// let name = "sample".as_bytes().to_vec();
///
/// let data = vec![
/// PseudoAln { ones: Some(vec![2]), ones_names: Some(vec!["virus.fasta".as_bytes().to_vec()]), query_id: Some(0), query_name: Some("r1".as_bytes().to_vec()) },
/// ];
///
/// let expected: Vec<u8> = vec![
/// b"query_index\tquery_name\tchr.fasta\tplasmid.fasta\tvirus.fasta\n".to_vec(),
/// b"0\tr1\t0\t0\t1\n".to_vec(),
/// ].into_iter().flatten().collect();
///
/// let mut iter = data.into_iter();
///
/// let printer = Printer::new(&mut iter, &targets, &name, queries.len(), Format::AhdaTSV);
/// assert!(printer.is_ok());
/// let mut printer = printer.unwrap();
///
/// let record = printer.next();
/// assert!(record.is_some());
/// assert!(record.as_ref().unwrap().is_ok());
/// assert_eq!(record.unwrap().unwrap(), expected);
///
/// let record = printer.next();
/// assert!(record.is_none());
/// ```
fn next(
&mut self,
) -> Option<Result<Vec<u8>, E>> {
let mut out: Vec<u8> = Vec::new();
if self.index == 0 {
if let Some(header) = self.print_header() {
match header {
Ok(header) => out.append(&mut header.clone()),
Err(e) => return Some(Err(e)),
}
}
}
if let Some(record) = self.records.next() {
let ret = match self.format {
Format::Themisto => format_themisto_line(&record, &mut out),
Format::Fulgor => format_fulgor_line(&record, &mut out),
Format::FulgorV4 => format_fulgor_line_v4(&record, &mut out),
Format::Metagraph => format_metagraph_line(&record, &mut out),
Format::Bifrost => format_bifrost_line(&record, self.header.n_targets as usize, &mut out),
#[cfg(feature = "sam")]
Format::SAM => format_sam_line(&record, self.sam_header.as_ref().unwrap(), &mut out),
Format::AhdaTSV => format_ahda_tsv_line(&record, self.header.n_targets as usize, &mut out),
};
match ret {
Ok(_) => {
self.index += 1;
Some(Ok(out))
},
Err(e) => Some(Err(e)),
}
} else {
None
}
}
}
// Tests
#[cfg(test)]
mod tests {
#[test]
fn print_themisto_output() {
use super::Printer;
use crate::Format;
use crate::FileFlags;
use crate::FileHeader;
use crate::PseudoAln;
use crate::AhdaFormatVersion;
use crate::compression::MetadataCompression;
use crate::compression::BitmapType;
use crate::headers::file::build_ahda_header;
use std::io::Cursor;
use std::io::Write;
let data = vec![
PseudoAln{ones_names: None, query_id: Some(128), ones: Some(vec![0, 7, 11, 3]), ..Default::default()},
PseudoAln{ones_names: None, query_id: Some(7), ones: Some(vec![3, 2, 1, 0]), ..Default::default()},
PseudoAln{ones_names: None, query_id: Some(8), ones: Some(vec![]), ..Default::default()},
PseudoAln{ones_names: None, query_id: Some(0), ones: Some(vec![]), ..Default::default()},
PseudoAln{ones_names: None, query_id: Some(1), ones: Some(vec![4, 2, 9, 7]), ..Default::default()},
];
let flags = FileFlags { query_name: "ERR4035126".as_bytes().to_vec(), target_names: vec!["chromosome.fasta".as_bytes().to_vec(), "plasmid.fasta".as_bytes().to_vec()] };
let header = FileHeader {
ahda_header: build_ahda_header().unwrap(),
file_format: AhdaFormatVersion::V1_0_0.to_u8(),
metadata_compression: MetadataCompression::default().to_u8(),
fields_present: 0,
n_targets: 2_u32,
n_queries: 5_u32,
bitmap_type: BitmapType::Roaring32.to_u16(),
block_size: ((u32::MAX as u64) / (2_u64)).min(65537_u64) as u32,
flags_len: 0_u64,
};
let expected: Vec<u8> = vec![b"128 0 7 11 3\n".to_vec(),
b"7 3 2 1 0\n".to_vec(),
b"8\n".to_vec(),
b"0\n".to_vec(),
b"1 4 2 9 7\n".to_vec(),
].concat();
let mut cursor: Cursor<Vec<u8>> = Cursor::new(Vec::new());
let mut data_iter = data.into_iter();
let mut printer = Printer::new_from_header_and_flags(&mut data_iter, header, flags, Format::Themisto).unwrap();
for bytes in printer.by_ref() {
cursor.write_all(&bytes.unwrap()).unwrap();
}
let got = cursor.get_ref();
assert_eq!(got, &expected);
}
#[test]
fn print_fulgor_output() {
use super::Printer;
use crate::Format;
use crate::FileFlags;
use crate::FileHeader;
use crate::PseudoAln;
use crate::AhdaFormatVersion;
use crate::compression::MetadataCompression;
use crate::compression::BitmapType;
use crate::headers::file::build_ahda_header;
use std::io::Cursor;
use std::io::Write;
let data = vec![
PseudoAln{ones_names: None, query_id: None, ones: Some(vec![]), query_name: Some("ERR4035126.4996".as_bytes().to_vec()) },
PseudoAln{ones_names: None, query_id: None, ones: Some(vec![0]), query_name: Some("ERR4035126.1262953".as_bytes().to_vec()) },
PseudoAln{ones_names: None, query_id: None, ones: Some(vec![1]), query_name: Some("ERR4035126.1262954".as_bytes().to_vec()) },
PseudoAln{ones_names: None, query_id: None, ones: Some(vec![1]), query_name: Some("ERR4035126.1262955".as_bytes().to_vec()) },
PseudoAln{ones_names: None, query_id: None, ones: Some(vec![0]), query_name: Some("ERR4035126.1262956".as_bytes().to_vec()) },
PseudoAln{ones_names: None, query_id: None, ones: Some(vec![0]), query_name: Some("ERR4035126.1262957".as_bytes().to_vec()) },
PseudoAln{ones_names: None, query_id: None, ones: Some(vec![0]), query_name: Some("ERR4035126.1262958".as_bytes().to_vec()) },
PseudoAln{ones_names: None, query_id: None, ones: Some(vec![0]), query_name: Some("ERR4035126.1262959".as_bytes().to_vec()) },
PseudoAln{ones_names: None, query_id: None, ones: Some(vec![0, 1]), query_name: Some("ERR4035126.651965".as_bytes().to_vec()) },
PseudoAln{ones_names: None, query_id: None, ones: Some(vec![]), query_name: Some("ERR4035126.11302".as_bytes().to_vec()) },
PseudoAln{ones_names: None, query_id: None, ones: Some(vec![0]), query_name: Some("ERR4035126.1262960".as_bytes().to_vec()) },
PseudoAln{ones_names: None, query_id: None, ones: Some(vec![0]), query_name: Some("ERR4035126.1262961".as_bytes().to_vec()) },
PseudoAln{ones_names: None, query_id: None, ones: Some(vec![0]), query_name: Some("ERR4035126.1262962".as_bytes().to_vec()) },
PseudoAln{ones_names: None, query_id: None, ones: Some(vec![0, 1]), query_name: Some("ERR4035126.651965".as_bytes().to_vec()) },
];
let flags = FileFlags { query_name: "ERR4035126".as_bytes().to_vec(), target_names: vec!["chromosome.fasta".as_bytes().to_vec(), "plasmid.fasta".as_bytes().to_vec()] };
let header = FileHeader {
ahda_header: build_ahda_header().unwrap(),
file_format: AhdaFormatVersion::V1_0_0.to_u8(),
metadata_compression: MetadataCompression::default().to_u8(),
fields_present: 0,
n_targets: 2_u32,
n_queries: 14_u32,
bitmap_type: BitmapType::Roaring32.to_u16(),
block_size: ((u32::MAX as u64) / (14_u64)).min(65537_u64) as u32,
flags_len: 0_u64,
};
let mut expected: Vec<u8> = b"ERR4035126.4996\t0\n".to_vec();
expected.append(&mut b"ERR4035126.1262953\t1\t0\n".to_vec());
expected.append(&mut b"ERR4035126.1262954\t1\t1\n".to_vec());
expected.append(&mut b"ERR4035126.1262955\t1\t1\n".to_vec());
expected.append(&mut b"ERR4035126.1262956\t1\t0\n".to_vec());
expected.append(&mut b"ERR4035126.1262957\t1\t0\n".to_vec());
expected.append(&mut b"ERR4035126.1262958\t1\t0\n".to_vec());
expected.append(&mut b"ERR4035126.1262959\t1\t0\n".to_vec());
expected.append(&mut b"ERR4035126.651965\t2\t0\t1\n".to_vec());
expected.append(&mut b"ERR4035126.11302\t0\n".to_vec());
expected.append(&mut b"ERR4035126.1262960\t1\t0\n".to_vec());
expected.append(&mut b"ERR4035126.1262961\t1\t0\n".to_vec());
expected.append(&mut b"ERR4035126.1262962\t1\t0\n".to_vec());
expected.append(&mut b"ERR4035126.651965\t2\t0\t1\n".to_vec());
let mut cursor: Cursor<Vec<u8>> = Cursor::new(Vec::new());
let mut data_iter = data.into_iter();
let mut printer = Printer::new_from_header_and_flags(&mut data_iter, header, flags, Format::Fulgor).unwrap();
for bytes in printer.by_ref() {
cursor.write_all(&bytes.unwrap()).unwrap();
}
let got = cursor.get_ref();
assert_eq!(got, &expected);
}
#[test]
fn print_bifrost_output() {
use super::Printer;
use crate::Format;
use crate::FileFlags;
use crate::FileHeader;
use crate::PseudoAln;
use crate::AhdaFormatVersion;
use crate::compression::MetadataCompression;
use crate::compression::BitmapType;
use crate::headers::file::build_ahda_header;
use std::io::Cursor;
use std::io::Write;
let data = vec![
PseudoAln{ query_name: Some("ERR4035126.724962".as_bytes().to_vec()), ones: Some(vec![]), ones_names: None, query_id: None },
PseudoAln{ query_name: Some("ERR4035126.1235744".as_bytes().to_vec()), ones: Some(vec![]), ones_names: None, query_id: None },
PseudoAln{ query_name: Some("ERR4035126.431001".as_bytes().to_vec()), ones: Some(vec![]), ones_names: None, query_id: None },
PseudoAln{ query_name: Some("ERR4035126.645400".as_bytes().to_vec()), ones: Some(vec![]), ones_names: None, query_id: None },
PseudoAln{ query_name: Some("ERR4035126.3001".as_bytes().to_vec()), ones: Some(vec![0]), ones_names: None, query_id: None },
PseudoAln{ query_name: Some("ERR4035126.515778".as_bytes().to_vec()), ones: Some(vec![0]), ones_names: None, query_id: None },
PseudoAln{ query_name: Some("ERR4035126.886205".as_bytes().to_vec()), ones: Some(vec![0]), ones_names: None, query_id: None },
PseudoAln{ query_name: Some("ERR4035126.1254676".as_bytes().to_vec()), ones: Some(vec![0]), ones_names: None, query_id: None },
PseudoAln{ query_name: Some("ERR4035126.668031".as_bytes().to_vec()), ones: Some(vec![1]), ones_names: None, query_id: None },
PseudoAln{ query_name: Some("ERR4035126.388619".as_bytes().to_vec()), ones: Some(vec![0]), ones_names: None, query_id: None },
PseudoAln{ query_name: Some("ERR4035126.959743".as_bytes().to_vec()), ones: Some(vec![]), ones_names: None, query_id: None },
PseudoAln{ query_name: Some("ERR4035126.1146685".as_bytes().to_vec()), ones: Some(vec![]), ones_names: None, query_id: None },
PseudoAln{ query_name: Some("ERR4035126.1017809".as_bytes().to_vec()), ones: Some(vec![]), ones_names: None, query_id: None },
PseudoAln{ query_name: Some("ERR4035126.788136".as_bytes().to_vec()), ones: Some(vec![]), ones_names: None, query_id: None },
PseudoAln{ query_name: Some("ERR4035126.1223924".as_bytes().to_vec()), ones: Some(vec![0, 1]), ones_names: None, query_id: None },
PseudoAln{ query_name: Some("ERR4035126.910807".as_bytes().to_vec()), ones: Some(vec![]), ones_names: None, query_id: None },
PseudoAln{ query_name: Some("ERR4035126.824748".as_bytes().to_vec()), ones: Some(vec![0]), ones_names: None, query_id: None },
];
let flags = FileFlags { query_name: "ERR4035126".as_bytes().to_vec(), target_names: vec!["chromosome.fasta".as_bytes().to_vec(), "plasmid.fasta".as_bytes().to_vec()] };
let header = FileHeader {
ahda_header: build_ahda_header().unwrap(),
file_format: AhdaFormatVersion::V1_0_0.to_u8(),
metadata_compression: MetadataCompression::default().to_u8(),
fields_present: 0,
n_targets: 2_u32,
n_queries: 17_u32,
bitmap_type: BitmapType::Roaring32.to_u16(),
block_size: ((u32::MAX as u64) / (17_u64)).min(65537_u64) as u32,
flags_len: 0_u64,
};
let mut expected: Vec<u8> = b"query_name\tchromosome.fasta\tplasmid.fasta\n".to_vec();
expected.append(&mut b"ERR4035126.724962\t0\t0\n".to_vec());
expected.append(&mut b"ERR4035126.1235744\t0\t0\n".to_vec());
expected.append(&mut b"ERR4035126.431001\t0\t0\n".to_vec());
expected.append(&mut b"ERR4035126.645400\t0\t0\n".to_vec());
expected.append(&mut b"ERR4035126.3001\t1\t0\n".to_vec());
expected.append(&mut b"ERR4035126.515778\t1\t0\n".to_vec());
expected.append(&mut b"ERR4035126.886205\t1\t0\n".to_vec());
expected.append(&mut b"ERR4035126.1254676\t1\t0\n".to_vec());
expected.append(&mut b"ERR4035126.668031\t0\t1\n".to_vec());
expected.append(&mut b"ERR4035126.388619\t1\t0\n".to_vec());
expected.append(&mut b"ERR4035126.959743\t0\t0\n".to_vec());
expected.append(&mut b"ERR4035126.1146685\t0\t0\n".to_vec());
expected.append(&mut b"ERR4035126.1017809\t0\t0\n".to_vec());
expected.append(&mut b"ERR4035126.788136\t0\t0\n".to_vec());
expected.append(&mut b"ERR4035126.1223924\t1\t1\n".to_vec());
expected.append(&mut b"ERR4035126.910807\t0\t0\n".to_vec());
expected.append(&mut b"ERR4035126.824748\t1\t0\n".to_vec());
let mut cursor: Cursor<Vec<u8>> = Cursor::new(Vec::new());
let mut data_iter = data.into_iter();
let mut printer = Printer::new_from_header_and_flags(&mut data_iter, header, flags, Format::Bifrost).unwrap();
for bytes in printer.by_ref() {
cursor.write_all(&bytes.unwrap()).unwrap();
}
let got = cursor.get_ref();
assert_eq!(got, &expected);
}
#[test]
fn print_metagraph_output() {
use super::Printer;
use crate::Format;
use crate::FileFlags;
use crate::FileHeader;
use crate::PseudoAln;
use crate::AhdaFormatVersion;
use crate::compression::MetadataCompression;
use crate::compression::BitmapType;
use crate::headers::file::build_ahda_header;
use std::io::Cursor;
use std::io::Write;
let data = vec![
PseudoAln{ones_names: Some(vec!["chr.fasta".as_bytes().to_vec()]), query_id: Some(3), ones: Some(vec![]), query_name: Some("ERR4035126.2".as_bytes().to_vec()) },
PseudoAln{ones_names: Some(vec!["chr.fasta".as_bytes().to_vec()]), query_id: Some(2), ones: Some(vec![]), query_name: Some("ERR4035126.1".as_bytes().to_vec()) },
PseudoAln{ones_names: Some(vec!["chr.fasta".as_bytes().to_vec(), "plasmid.fasta".as_bytes().to_vec()]), query_id: Some(1303804), ones: Some(vec![]), query_name: Some("ERR4035126.651903".as_bytes().to_vec()) },
PseudoAln{ones_names: Some(vec![]), query_id: Some(30), ones: Some(vec![]), query_name: Some("ERR4035126.16".as_bytes().to_vec()) },
PseudoAln{ones_names: Some(vec!["plasmid.fasta".as_bytes().to_vec()]), query_id: Some(15084), ones: Some(vec![]), query_name: Some("ERR4035126.7543".as_bytes().to_vec()) },
];
let flags = FileFlags { query_name: "ERR4035126".as_bytes().to_vec(), target_names: vec!["chromosome.fasta".as_bytes().to_vec(), "plasmid.fasta".as_bytes().to_vec()] };
let header = FileHeader {
ahda_header: build_ahda_header().unwrap(),
file_format: AhdaFormatVersion::V1_0_0.to_u8(),
metadata_compression: MetadataCompression::default().to_u8(),
fields_present: 0,
n_targets: 2_u32,
n_queries: 5_u32,
bitmap_type: BitmapType::Roaring32.to_u16(),
block_size: ((u32::MAX as u64) / (5_u64)).min(65537_u64) as u32,
flags_len: 0_u64,
};
let mut expected: Vec<u8> = b"3\tERR4035126.2\tchr.fasta\n".to_vec();
expected.append(&mut b"2\tERR4035126.1\tchr.fasta\n".to_vec());
expected.append(&mut b"1303804\tERR4035126.651903\tchr.fasta:plasmid.fasta\n".to_vec());
expected.append(&mut b"30\tERR4035126.16\t\n".to_vec());
expected.append(&mut b"15084\tERR4035126.7543\tplasmid.fasta\n".to_vec());
let mut cursor: Cursor<Vec<u8>> = Cursor::new(Vec::new());
let mut data_iter = data.into_iter();
let mut printer = Printer::new_from_header_and_flags(&mut data_iter, header, flags, Format::Metagraph).unwrap();
for bytes in printer.by_ref() {
cursor.write_all(&bytes.unwrap()).unwrap();
}
let got = cursor.get_ref();
assert_eq!(got, &expected);
}
#[test]
#[should_panic(expected = "not yet implemented")]
#[cfg(feature = "sam")]
fn print_sam_output() {
use super::Printer;
use crate::Format;
use crate::FileFlags;
use crate::FileHeader;
use crate::AhdaFormatVersion;
use crate::compression::MetadataCompression;
use crate::compression::BitmapType;
use crate::headers::file::build_ahda_header;
use std::io::Cursor;
use std::io::Write;
use super::PseudoAln;
let data = vec![
PseudoAln{ query_id: None, query_name: Some("ERR4035126.1".as_bytes().to_vec()), ones_names: Some(vec!["OZ038621.1".as_bytes().to_vec()]), ones: Some(vec![0]) },
PseudoAln{ query_id: None, query_name: Some("ERR4035126.2".as_bytes().to_vec()), ones_names: Some(vec!["OZ038621.1".as_bytes().to_vec()]), ones: Some(vec![0]) },
PseudoAln{ query_id: None, query_name: Some("ERR4035126.3".as_bytes().to_vec()), ones_names: Some(vec!["OZ038621.1".as_bytes().to_vec()]), ones: Some(vec![0]) },
PseudoAln{ query_id: None, query_name: Some("ERR4035126.1261584".as_bytes().to_vec()), ones_names: Some(vec!["OZ038622.1".as_bytes().to_vec()]), ones: Some(vec![1]) },
PseudoAln{ query_id: None, query_name: Some("ERR4035126.1213410".as_bytes().to_vec()), ones_names: Some(vec!["OZ038622.1".as_bytes().to_vec()]), ones: Some(vec![1]) },
PseudoAln{ query_id: None, query_name: Some("ERR4035126.1213410".as_bytes().to_vec()), ones_names: Some(vec!["OZ038621.1".as_bytes().to_vec()]), ones: Some(vec![0]) },
PseudoAln{ query_id: None, query_name: Some("ERR4035126.4".as_bytes().to_vec()), ones_names: Some(vec!["OZ038621.1".as_bytes().to_vec()]), ones: Some(vec![0]) },
PseudoAln{ query_id: None, query_name: Some("ERR4035126.5".as_bytes().to_vec()), ones_names: Some(vec!["OZ038621.1".as_bytes().to_vec()]), ones: Some(vec![0]) },
PseudoAln{ query_id: None, query_name: Some("ERR4035126.6".as_bytes().to_vec()), ones_names: Some(vec!["OZ038621.1".as_bytes().to_vec()]), ones: Some(vec![0]) },
PseudoAln{ query_id: None, query_name: Some("ERR4035126.973529".as_bytes().to_vec()), ones_names: Some(vec!["OZ038621.1".as_bytes().to_vec()]), ones: Some(vec![0]) },
PseudoAln{ query_id: None, query_name: Some("ERR4035126.973529".as_bytes().to_vec()), ones_names: Some(vec!["OZ038621.1".as_bytes().to_vec()]), ones: Some(vec![0]) },
PseudoAln{ query_id: None, query_name: Some("ERR4035126.621281".as_bytes().to_vec()), ones_names: Some(vec!["OZ038621.1".as_bytes().to_vec()]), ones: Some(vec![0]) },
PseudoAln{ query_id: None, query_name: Some("ERR4035126.1178767".as_bytes().to_vec()), ones_names: None, ones: Some(vec![]) },
PseudoAln{ query_id: None, query_name: Some("ERR4035126.621281".as_bytes().to_vec()), ones_names: Some(vec!["OZ038621.1".as_bytes().to_vec()]), ones: Some(vec![0]) },
PseudoAln{ query_id: None, query_name: Some("ERR4035126.621281".as_bytes().to_vec()), ones_names: Some(vec!["OZ038621.1".as_bytes().to_vec()]), ones: Some(vec![0]) },
];
let flags = FileFlags { query_name: "ERR4035126".as_bytes().to_vec(), target_names: vec!["chromosome.fasta".as_bytes().to_vec(), "plasmid.fasta".as_bytes().to_vec()] };
let header = FileHeader {
ahda_header: build_ahda_header().unwrap(),
file_format: AhdaFormatVersion::V1_0_0.to_u8(),
metadata_compression: MetadataCompression::default().to_u8(),
fields_present: 0,
n_targets: 2_u32,
n_queries: 15_u32,
bitmap_type: BitmapType::Roaring32.to_u16(),
block_size: ((u32::MAX as u64) / (15_u64)).min(65537_u64) as u32,
flags_len: 0_u64,
};
let mut expected: Vec<u8> = b"@HD\tVN:1.6\n".to_vec();
expected.append(&mut b"@SQ\tSN:OZ038621.1\tLN:1\n".to_vec());
expected.append(&mut b"@SQ\tSN:OZ038622.1\tLN:1\n".to_vec());
expected.append(&mut b"ERR4035126.1\t16\tOZ038621.1\t4541508\t60\t151M\t*\t0\t0\tAGTATTTAGTGACCTAAGTCAATAAAATTTTAATTTACTCACGGCAGGTAACCAGTTCAGAAGCTGCTATCAGACACTCTTTTTTTAATCCACACAGAGACATATTGCCCGTTGCAGTCAGAATGAAAAGCTGAAAATCACTTACTAAGGC\tFJ<<JJFJAA<-JFAJFAF<JFFJJJJJJJFJFJJA<A<AJJAAAFFJJJJFJJFJFJAJJ7JJJJJFJJJJJFFJFFJFJJJJJJFJ7FFJAJJJJJJJJFJJFJJFJFJJJJFJJFJJJJJJJJJFFJJJJJJJJJJJJJFJJJFFAAA\tNM:i:0\tMD:Z:151\tAS:i:151\tXS:i:0\n".to_vec());
expected.append(&mut b"ERR4035126.2\t16\tOZ038621.1\t4541557\t60\t151M\t*\t0\t0\tAACCAGTTCAGAAGCTGCTATCAGACACTCTTTTTTTAATCCACACAGAGACATATTGCCCGTTGCAGTCAGAATGAAAAGCTGAAAATCACTTACTAAGGCGTTTTTTATTTGGTGATATTTTTTTCAATATCATGCAGCAAACGGTGCA\tJAFJFJJJFFJFAJJJJJJJJJJFFA<JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJFJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJFJJJJJJJJJJJJJJJFF-FFFAA\tNM:i:0\tMD:Z:151\tAS:i:151\tXS:i:0\n".to_vec());
expected.append(&mut b"ERR4035126.3\t16\tOZ038621.1\t4541521\t60\t151M\t*\t0\t0\tCTAAGTCAATAAAATTTTAATTTACTCACGGCAGGTAACCAGTTCAGAAGCTGCTATCAGACACTCTTTTTTTAATCCACACAGAGACATATTGCCCGTTGCAGTCAGAATGAAAAGCTGAAAATCACTTACTAAGGCGTTTTTTATTTGG\tJJJJJJJFJFFFJJJJJJAJJJF7JJJJJ<JJFFJJJJJJJFJJJJJJJJJFFFJJJFJJJJJJJJJJJJJJJJAJFJJJJFJJJJJJJJJJJJJJJJJJJJJJAJJJJJJJJJJJJJJJJJAJFJFJJJJJJJJJJJJJJJJJFJFAFAA\tNM:i:0\tMD:Z:151\tAS:i:151\tXS:i:0\n".to_vec());
expected.append(&mut b"ERR4035126.1261584\t16\tOZ038622.1\t66398\t60\t151M\t*\t0\t0\tGCCGCTGTCTGAACCATGATATTGGCGGAACCGATGCCCATGATGGATGCGCCCCACAGCATGACCAGTTGCGCCAGACTCCAGCCGGAAGCGGTGGGCACAATCATCAAAAATCCACTCACGACACTGAGTATGCCGACGACGTCCCGTC\tFFJJJJFFJFJFFFJJJJJJJJJJJ7FA<JJ<JFJJFJJJJF-FJJA<FJJJJAJJJJJJJJJJJJJJJJJJJJFFJJJFJJJJJJJJJJJJJJJJJJFJFJJJJJJJJJFJJJJJFJJFJFJFJJJJJJJJJJJJJJJJJJJJJJFFFAA\tNM:i:0\tMD:Z:151\tAS:i:151\tXS:i:0\n".to_vec());
expected.append(&mut b"ERR4035126.1213410\t16\tOZ038622.1\t3996\t60\t151M\t*\t0\t0\tGCTGGCGCTTCGGGGATATGTGTTTCGACGGCAGATGAATTTATTCCGGCGGGGGCTGATTCTGCCGTCTGTTCAGTAAATACAGGTGCGATAATATCTGTTTTTTCGGATAAGGACGGTGGCGAAAAAGTACGACGTTTTTTCACCACAA\tJJJJJJJJJJJJJJJJJJJJJJJJJJJFJFJFFJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJFJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJFFJJJJJJJJJJJFFAAA\tNM:i:0\tMD:Z:151\tAS:i:151\tXS:i:0\n".to_vec());
expected.append(&mut b"ERR4035126.1213410\t16\tOZ038621.1\t3996\t60\t151M\t*\t0\t0\tGCTGGCGCTTCGGGGATATGTGTTTCGACGGCAGATGAATTTATTCCGGCGGGGGCTGATTCTGCCGTCTGTTCAGTAAATACAGGTGCGATAATATCTGTTTTTTCGGATAAGGACGGTGGCGAAAAAGTACGACGTTTTTTCACCACAA\tJJJJJJJJJJJJJJJJJJJJJJJJJJJFJFJFFJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJFJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJFFJJJJJJJJJJJFFAAA\tNM:i:0\tMD:Z:151\tAS:i:151\tXS:i:0\n".to_vec());
expected.append(&mut b"ERR4035126.4\t0\tOZ038621.1\t4541351\t60\t151M\t*\t0\t0\tAGGTGCGGGCTTTTTTCTGTGTTTCCTGTACGCGTCAGCCCGCACCGTTACCTGTGGTAATGGTGATGGTGGTGGTAATGGTGGTGCTAATGCGTTTCATGGATGTTGTGTACTCTGTAATTTTTATCTGTCTGTGCGCTATGCCTATATT\tAAFFFJJJJJJJJJJJJJJJJJJJJJFFJJJJJJJJJJJJJJJJJJJFFJJJJJJJJFJJJJJJJJJJJ<JFJJJJJJJJJJJAJJJFJJJJJJJJJJJJJJJJJJJFJJJJJJJJJJJJJJJJJJJJJJJFFFJFAFJJJJF<FFFJJJJ\tNM:i:0\tMD:Z:151\tAS:i:151\tXS:i:0\n".to_vec());
expected.append(&mut b"ERR4035126.5\t16\tOZ038621.1\t4541533\t60\t151M\t*\t0\t0\tAATTTTAATTTACTCACGGCAGGTAACCAGTTCAGAAGCTGCTATCAGACACTCTTTTTTTAATCCACACAGAGACATATTGCCCGTTGCAGTCAGAATGAAAAGCTGAAAATCACTTACTAAGGCGTTTTTTATTTGGTGATATTTTTTT\tFJJJJJJJJFJJJJJJJJFJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJFJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJFJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJFJJJJJJJJJJJJJFFFAA\tNM:i:0\tMD:Z:151\tAS:i:151\tXS:i:0\n".to_vec());
expected.append(&mut b"ERR4035126.6\t0\tOZ038621.1\t4541261\t60\t151M\t*\t0\t0\tTCTGCATTTGCCACTGATGTACCGCCGAACTTCAACACTCGCATGGTTGTTACCTCGTTACCTTTGGTCGAAAAAAAAGCCCGCACTGTCAGGTGCGGGCTTTTTTCTGTGTTTCCTGTACGCGTCAGCCCGCACCGTTACCTGTGGTAAT\tAAAFFJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJFJJFJJJJJJJJ<FJJJJJJJJJJJJJJJJJJJJJJJJJJJFJJJJJJJJFJJJJF7FJJJJJJJFJFJJJJJJJJFJJJJJJJJAJJJJFJFFFJFJF\tNM:i:0\tMD:Z:151\tAS:i:151\tXS:i:0\n".to_vec());
expected.append(&mut b"ERR4035126.973529\t16\tOZ038621.1\t3695316\t60\t66S85M\t*\t0\t0\tGGAGATGATTTCGTGTTTCTTCTCCGGGATGACCATGTCATCGATACCAACAGATGCACCAGAACGCGCCAAGTCGGGCAATCTGGTGAACTGGAAAGCCGGGGCGCTGTATCACCTGACGGAAAACGGCAATGTCTATATTAACTATGCC\tJJFJFF7-FFJJJA-FJFFFJJFAJJJJJJJJJJJJJJJJFJJJJJJJJFJJJJFJ<JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJFFJFJJJJJJJJFJJJJJJJJFAJJJJJJJJJJJJJFJJJJJFFJJJFJJJJJJFJJFFFAA\tNM:i:0\tMD:Z:85\tAS:i:85\tXS:i:0\tSA:Z:OZ038621.1,5194124,-,69M82S,60,0;\n".to_vec());
expected.append(&mut b"ERR4035126.973529\t2064\tOZ038621.1\t5194124\t60\t69M82H\t*\t0\t0\tGGAGATGATTTCGTGTTTCTTCTCCGGGATGACCATGTCATCGATACCAACAGATGCACCAGAACGCGC\tJJFJFF7-FFJJJA-FJFFFJJFAJJJJJJJJJJJJJJJJFJJJJJJJJFJJJJFJ<JJJJJJJJJJJJ\tNM:i:0\tMD:Z:69\tAS:i:69\tXS:i:0\tSA:Z:OZ038621.1,3695316,-,66S85M,60,0;\n".to_vec());
expected.append(&mut b"ERR4035126.621281\t16\tOZ038621.1\t1040569\t60\t39S86M26S\t*\t0\t0\tGCTCGACCGCGTCCCAGTTGAAATGCAACTCCCCAGCCAACTCGATAAACACGATGATTAACACGGCAGTCATGGTCAGAATGGAAACGGGATCGAAAATCGGCATACCAAATGACATCGGCGTGCCACAGCACAAACTGGACGCCCTGGC\tAFAJJJJJJJJJJJJFJJJJJJJJJJJJJJJJFJFJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJFFFAA\tNM:i:0\tMD:Z:86\tAS:i:86\tXS:i:0\tSA:Z:OZ038621.1,3172373,-,46M105S,60,0;OZ038621.1,1301509,+,33M118S,60,0;\n".to_vec());
expected.append(&mut b"ERR4035126.1178767\t4\t*\t0\t0\t*\t*\t0\t0\tACTTGGCTCATGTTCCGTCAATGCCGGAGAGACAATTGAAGTTGATTTAGGTGATGTCTTCGCTGCCAATTTCCGTGTTGTAGGGCATAAACCTCTTGGGGCCAGAACGGCAGAACTTGCAATTCCAGTCAGGTGTAACACGGGAAACGCG\tAAFFFJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJFJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJFJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ\tAS:i:0\tXS:i:0\n".to_vec());
expected.append(&mut b"ERR4035126.621281\t2064\tOZ038621.1\t3172373\t60\t46M105H\t*\t0\t0\tGCTCGACCGCGTCCCAGTTGAAATGCAACTCCCCAGCCAACTCGAT\tAFAJJJJJJJJJJJJFJJJJJJJJJJJJJJJJFJFJJJJJJJJJJJ\tNM:i:0\tMD:Z:46\tAS:i:46\tXS:i:0\tSA:Z:OZ038621.1,1040569,-,39S86M26S,60,0;OZ038621.1,1301509,+,33M118S,60,0;\n".to_vec());
expected.append(&mut b"ERR4035126.621281\t2048\tOZ038621.1\t1301509\t60\t33M118H\t*\t0\t0\tGCCAGGGCGTCCAGTTTGTGCTGTGGCACGCCG\tAAFFFJJJJJJJJJJJJJJJJJJJJJJJJJJJJ\tNM:i:0\tMD:Z:33\tAS:i:33\tXS:i:0\tSA:Z:OZ038621.1,1040569,-,39S86M26S,60,0;OZ038621.1,3172373,-,46M105S,60,0;\n".to_vec());
let mut cursor: Cursor<Vec<u8>> = Cursor::new(Vec::new());
let mut data_iter = data.into_iter();
let mut printer = Printer::new_from_header_and_flags(&mut data_iter, header, flags, Format::SAM).unwrap();
for bytes in printer.by_ref() {
cursor.write_all(&bytes.unwrap()).unwrap();
}
let got = cursor.get_ref();
eprintln!("{}", got.iter().map(|x| *x as char).collect::<String>());
eprintln!("{}", expected.iter().map(|x| *x as char).collect::<String>());
assert_eq!(got, &expected);
}
}