1pub mod buffer;
9pub mod ext;
10pub mod header;
11pub mod index;
12pub mod pileup;
13pub mod record;
14
15#[cfg(feature = "serde_feature")]
16pub mod record_serde;
17
18use std::ffi;
19use std::os::raw::c_char;
20use std::path::Path;
21use std::rc::Rc;
22use std::slice;
23use std::str;
24
25use url::Url;
26
27use crate::errors::{Error, Result};
28use crate::htslib;
29use crate::tpool::ThreadPool;
30use crate::utils::path_as_bytes;
31
32pub use crate::bam::buffer::RecordBuffer;
33pub use crate::bam::header::Header;
34pub use crate::bam::record::Record;
35use hts_sys::{hts_fmt_option, sam_fields};
36use std::convert::{TryFrom, TryInto};
37use std::mem::MaybeUninit;
38
39unsafe fn set_threads(htsfile: *mut htslib::htsFile, n_threads: usize) -> Result<()> {
43 assert!(n_threads != 0, "n_threads must be > 0");
44
45 if htslib::hts_set_threads(htsfile, n_threads as i32) != 0 {
46 Err(Error::SetThreads)
47 } else {
48 Ok(())
49 }
50}
51
52unsafe fn set_thread_pool(htsfile: *mut htslib::htsFile, tpool: &ThreadPool) -> Result<()> {
53 let mut b = tpool.handle.borrow_mut();
54
55 if htslib::hts_set_thread_pool(htsfile, &mut b.inner as *mut _) != 0 {
56 Err(Error::ThreadPool)
57 } else {
58 Ok(())
59 }
60}
61
62pub unsafe fn set_fai_filename<P: AsRef<Path>>(
66 htsfile: *mut htslib::htsFile,
67 fasta_path: P,
68) -> Result<()> {
69 let path = if let Some(ext) = fasta_path.as_ref().extension() {
70 fasta_path
71 .as_ref()
72 .with_extension(format!("{}.fai", ext.to_str().unwrap()))
73 } else {
74 fasta_path.as_ref().with_extension(".fai")
75 };
76 let p: &Path = path.as_ref();
77 let c_str = ffi::CString::new(p.to_str().unwrap().as_bytes()).unwrap();
78 if htslib::hts_set_fai_filename(htsfile, c_str.as_ptr()) == 0 {
79 Ok(())
80 } else {
81 Err(Error::BamInvalidReferencePath { path: p.to_owned() })
82 }
83}
84
85pub trait Read: Sized {
87 fn read(&mut self, record: &mut record::Record) -> Option<Result<()>>;
119
120 fn records(&mut self) -> Records<'_, Self>;
129
130 fn rc_records(&mut self) -> RcRecords<'_, Self>;
162
163 fn pileup(&mut self) -> pileup::Pileups<'_, Self>;
165
166 fn htsfile(&self) -> *mut htslib::htsFile;
168
169 fn header(&self) -> &HeaderView;
171
172 fn seek(&mut self, offset: i64) -> Result<()> {
174 let htsfile = unsafe { self.htsfile().as_ref() }.expect("bug: null pointer to htsFile");
175 let ret = match htsfile.format.format {
176 htslib::htsExactFormat_cram => unsafe {
177 i64::from(htslib::cram_seek(
178 htsfile.fp.cram,
179 offset as libc::off_t,
180 libc::SEEK_SET,
181 ))
182 },
183 _ => unsafe { htslib::bgzf_seek(htsfile.fp.bgzf, offset, libc::SEEK_SET) },
184 };
185
186 if ret == 0 {
187 Ok(())
188 } else {
189 Err(Error::FileSeek)
190 }
191 }
192
193 fn tell(&self) -> i64 {
195 let htsfile = unsafe { self.htsfile().as_ref() }.expect("bug: null pointer to htsFile");
197 let bgzf = unsafe { *htsfile.fp.bgzf };
198 (bgzf.block_address << 16) | (i64::from(bgzf.block_offset) & 0xFFFF)
199 }
200
201 fn set_threads(&mut self, n_threads: usize) -> Result<()> {
211 unsafe { set_threads(self.htsfile(), n_threads) }
212 }
213
214 fn set_thread_pool(&mut self, tpool: &ThreadPool) -> Result<()>;
222
223 fn set_cram_options(&mut self, fmt_opt: hts_fmt_option, fields: sam_fields) -> Result<()> {
239 unsafe {
240 if hts_sys::hts_set_opt(self.htsfile(), fmt_opt, fields) != 0 {
241 Err(Error::HtsSetOpt)
242 } else {
243 Ok(())
244 }
245 }
246 }
247}
248
249#[derive(Debug)]
251pub struct Reader {
252 htsfile: *mut htslib::htsFile,
253 header: Rc<HeaderView>,
254 tpool: Option<ThreadPool>,
255}
256
257unsafe impl Send for Reader {}
258
259impl Reader {
260 pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self> {
266 Self::new(&path_as_bytes(path, true)?)
267 }
268
269 pub fn from_stdin() -> Result<Self> {
271 Self::new(b"-")
272 }
273
274 pub fn from_url(url: &Url) -> Result<Self> {
276 Self::new(url.as_str().as_bytes())
277 }
278
279 fn new(path: &[u8]) -> Result<Self> {
285 let htsfile = hts_open(path, b"r")?;
286
287 let header = unsafe { htslib::sam_hdr_read(htsfile) };
288 if header.is_null() {
289 return Err(Error::BamOpen {
290 target: String::from_utf8_lossy(path).to_string(),
291 });
292 }
293
294 unsafe {
296 let _ = htslib::sam_hdr_line_name(header, b"SQ".as_ptr().cast::<c_char>(), 0);
297 }
298
299 Ok(Reader {
300 htsfile,
301 header: Rc::new(HeaderView::new(header)),
302 tpool: None,
303 })
304 }
305
306 extern "C" fn pileup_read(
307 data: *mut ::std::os::raw::c_void,
308 record: *mut htslib::bam1_t,
309 ) -> i32 {
310 let mut _self = unsafe { (data as *mut Self).as_mut().unwrap() };
311 unsafe {
312 htslib::sam_read1(
313 _self.htsfile(),
314 _self.header().inner_ptr() as *mut hts_sys::sam_hdr_t,
315 record,
316 )
317 }
318 }
319
320 pub fn iter_chunk(&mut self, start: Option<i64>, end: Option<i64>) -> ChunkIterator<'_, Self> {
329 if let Some(pos) = start {
330 self.seek(pos)
331 .expect("Failed to seek to the starting position");
332 };
333
334 ChunkIterator { reader: self, end }
335 }
336
337 pub fn set_reference<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
343 unsafe { set_fai_filename(self.htsfile, path) }
344 }
345}
346
347impl Read for Reader {
348 fn read(&mut self, record: &mut record::Record) -> Option<Result<()>> {
375 match unsafe {
376 htslib::sam_read1(
377 self.htsfile,
378 self.header().inner_ptr() as *mut hts_sys::sam_hdr_t,
379 record.inner_ptr_mut(),
380 )
381 } {
382 -1 => None,
383 -2 => Some(Err(Error::BamTruncatedRecord)),
384 -4 => Some(Err(Error::BamInvalidRecord)),
385 _ => {
386 record.set_header(Rc::clone(&self.header));
387
388 Some(Ok(()))
389 }
390 }
391 }
392
393 fn records(&mut self) -> Records<'_, Self> {
398 Records { reader: self }
399 }
400
401 fn rc_records(&mut self) -> RcRecords<'_, Self> {
402 RcRecords {
403 reader: self,
404 record: Rc::new(record::Record::new()),
405 }
406 }
407
408 fn pileup(&mut self) -> pileup::Pileups<'_, Self> {
409 let _self = self as *const Self;
410 let itr = unsafe {
411 htslib::bam_plp_init(
412 Some(Reader::pileup_read),
413 _self as *mut ::std::os::raw::c_void,
414 )
415 };
416 pileup::Pileups::new(self, itr)
417 }
418
419 fn htsfile(&self) -> *mut htslib::htsFile {
420 self.htsfile
421 }
422
423 fn header(&self) -> &HeaderView {
424 &self.header
425 }
426
427 fn set_thread_pool(&mut self, tpool: &ThreadPool) -> Result<()> {
428 unsafe { set_thread_pool(self.htsfile(), tpool)? }
429 self.tpool = Some(tpool.clone());
430 Ok(())
431 }
432}
433
434impl Drop for Reader {
435 fn drop(&mut self) {
436 unsafe {
437 htslib::hts_close(self.htsfile);
438 }
439 }
440}
441
442#[doc(hidden)]
445pub struct FetchCoordinate(i64);
446
447impl From<i32> for FetchCoordinate {
449 fn from(coord: i32) -> FetchCoordinate {
450 FetchCoordinate(coord as i64)
451 }
452}
453
454impl From<u32> for FetchCoordinate {
456 fn from(coord: u32) -> FetchCoordinate {
457 FetchCoordinate(coord as i64)
458 }
459}
460
461impl From<i64> for FetchCoordinate {
463 fn from(coord: i64) -> FetchCoordinate {
464 FetchCoordinate(coord)
465 }
466}
467
468impl From<u64> for FetchCoordinate {
470 fn from(coord: u64) -> FetchCoordinate {
471 FetchCoordinate(coord.try_into().expect("Coordinate exceeded 2^^63-1"))
472 }
473}
474
475#[derive(Debug)]
487pub enum FetchDefinition<'a> {
488 Region(i32, i64, i64),
490 RegionString(&'a [u8], i64, i64),
492 CompleteTid(i32),
494 String(&'a [u8]),
496 All,
498 Unmapped,
500}
501
502impl<'a, X: Into<FetchCoordinate>, Y: Into<FetchCoordinate>> From<(i32, X, Y)>
503 for FetchDefinition<'a>
504{
505 fn from(tup: (i32, X, Y)) -> FetchDefinition<'a> {
506 let start: FetchCoordinate = tup.1.into();
507 let stop: FetchCoordinate = tup.2.into();
508 FetchDefinition::Region(tup.0, start.0, stop.0)
509 }
510}
511
512impl<'a, X: Into<FetchCoordinate>, Y: Into<FetchCoordinate>> From<(u32, X, Y)>
513 for FetchDefinition<'a>
514{
515 fn from(tup: (u32, X, Y)) -> FetchDefinition<'a> {
516 let start: FetchCoordinate = tup.1.into();
517 let stop: FetchCoordinate = tup.2.into();
518 FetchDefinition::Region(
519 tup.0.try_into().expect("Tid exceeded 2^31-1"),
520 start.0,
521 stop.0,
522 )
523 }
524}
525
526impl<'a> From<i32> for FetchDefinition<'a> {
528 fn from(tid: i32) -> FetchDefinition<'a> {
529 FetchDefinition::CompleteTid(tid)
530 }
531}
532
533impl<'a> From<u32> for FetchDefinition<'a> {
534 fn from(tid: u32) -> FetchDefinition<'a> {
535 let tid: i32 = tid.try_into().expect("tid exceeded 2^31-1");
536 FetchDefinition::CompleteTid(tid)
537 }
538}
539
540impl<'a> From<&'a str> for FetchDefinition<'a> {
541 fn from(s: &'a str) -> FetchDefinition<'a> {
542 FetchDefinition::String(s.as_bytes())
543 }
544}
545
546impl<'a> From<&'a [u8]> for FetchDefinition<'a> {
548 fn from(s: &'a [u8]) -> FetchDefinition<'a> {
549 FetchDefinition::String(s)
550 }
551}
552
553impl<'a, T: AsRef<[u8]>> From<&'a T> for FetchDefinition<'a> {
555 fn from(s: &'a T) -> FetchDefinition<'a> {
556 FetchDefinition::String(s.as_ref())
557 }
558}
559
560impl<'a, X: Into<FetchCoordinate>, Y: Into<FetchCoordinate>> From<(&'a str, X, Y)>
561 for FetchDefinition<'a>
562{
563 fn from(tup: (&'a str, X, Y)) -> FetchDefinition<'a> {
564 let start: FetchCoordinate = tup.1.into();
565 let stop: FetchCoordinate = tup.2.into();
566 FetchDefinition::RegionString(tup.0.as_bytes(), start.0, stop.0)
567 }
568}
569
570impl<'a, X: Into<FetchCoordinate>, Y: Into<FetchCoordinate>> From<(&'a [u8], X, Y)>
571 for FetchDefinition<'a>
572{
573 fn from(tup: (&'a [u8], X, Y)) -> FetchDefinition<'a> {
574 let start: FetchCoordinate = tup.1.into();
575 let stop: FetchCoordinate = tup.2.into();
576 FetchDefinition::RegionString(tup.0, start.0, stop.0)
577 }
578}
579
580impl<'a, T: AsRef<[u8]>, X: Into<FetchCoordinate>, Y: Into<FetchCoordinate>> From<(&'a T, X, Y)>
582 for FetchDefinition<'a>
583{
584 fn from(tup: (&'a T, X, Y)) -> FetchDefinition<'a> {
585 let start: FetchCoordinate = tup.1.into();
586 let stop: FetchCoordinate = tup.2.into();
587 FetchDefinition::RegionString(tup.0.as_ref(), start.0, stop.0)
588 }
589}
590
591#[derive(Debug)]
592pub struct IndexedReader {
593 htsfile: *mut htslib::htsFile,
594 header: Rc<HeaderView>,
595 idx: Rc<IndexView>,
596 itr: Option<*mut htslib::hts_itr_t>,
597 tpool: Option<ThreadPool>,
598}
599
600unsafe impl Send for IndexedReader {}
601
602impl IndexedReader {
603 pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self> {
609 Self::new(&path_as_bytes(path, true)?)
610 }
611
612 pub fn from_path_and_index<P: AsRef<Path>>(path: P, index_path: P) -> Result<Self> {
613 Self::new_with_index_path(
614 &path_as_bytes(path, true)?,
615 &path_as_bytes(index_path, true)?,
616 )
617 }
618
619 pub fn from_url(url: &Url) -> Result<Self> {
620 Self::new(url.as_str().as_bytes())
621 }
622
623 fn new(path: &[u8]) -> Result<Self> {
629 let htsfile = hts_open(path, b"r")?;
630 let header = unsafe { htslib::sam_hdr_read(htsfile) };
631 let c_str = ffi::CString::new(path).unwrap();
632 let idx = unsafe { htslib::sam_index_load(htsfile, c_str.as_ptr()) };
633 if idx.is_null() {
634 Err(Error::BamInvalidIndex {
635 target: str::from_utf8(path).unwrap().to_owned(),
636 })
637 } else {
638 Ok(IndexedReader {
639 htsfile,
640 header: Rc::new(HeaderView::new(header)),
641 idx: Rc::new(IndexView::new(idx)),
642 itr: None,
643 tpool: None,
644 })
645 }
646 }
647 fn new_with_index_path(path: &[u8], index_path: &[u8]) -> Result<Self> {
654 let htsfile = hts_open(path, b"r")?;
655 let header = unsafe { htslib::sam_hdr_read(htsfile) };
656 let c_str_path = ffi::CString::new(path).unwrap();
657 let c_str_index_path = ffi::CString::new(index_path).unwrap();
658 let idx = unsafe {
659 htslib::sam_index_load2(htsfile, c_str_path.as_ptr(), c_str_index_path.as_ptr())
660 };
661 if idx.is_null() {
662 Err(Error::BamInvalidIndex {
663 target: str::from_utf8(path).unwrap().to_owned(),
664 })
665 } else {
666 Ok(IndexedReader {
667 htsfile,
668 header: Rc::new(HeaderView::new(header)),
669 idx: Rc::new(IndexView::new(idx)),
670 itr: None,
671 tpool: None,
672 })
673 }
674 }
675
676 pub fn fetch<'a, T: Into<FetchDefinition<'a>>>(&mut self, fetch_definition: T) -> Result<()> {
710 self._inner_fetch(fetch_definition.into())
713 }
714
715 fn _inner_fetch(&mut self, fetch_definition: FetchDefinition) -> Result<()> {
716 match fetch_definition {
717 FetchDefinition::Region(tid, start, stop) => {
718 self._fetch_by_coord_tuple(tid, start, stop)
719 }
720 FetchDefinition::RegionString(s, start, stop) => {
721 let tid = self.header().tid(s);
722 match tid {
723 Some(tid) => self._fetch_by_coord_tuple(tid as i32, start, stop),
724 None => Err(Error::Fetch),
725 }
726 }
727 FetchDefinition::CompleteTid(tid) => {
728 let len = self.header().target_len(tid as u32);
729 match len {
730 Some(len) => self._fetch_by_coord_tuple(tid, 0, len as i64),
731 None => Err(Error::Fetch),
732 }
733 }
734 FetchDefinition::String(s) => {
735 let tid = self.header().tid(s);
737 match tid {
738 Some(tid) => {
739 let len: i64 = self.header.target_len(tid).unwrap().try_into().unwrap();
741 self._fetch_by_coord_tuple(tid as i32, 0, len)
742 }
743 None => self._fetch_by_str(s),
744 }
745 }
746 FetchDefinition::All => self._fetch_by_str(b"."),
747 FetchDefinition::Unmapped => self._fetch_by_str(b"*"),
748 }
749 }
750
751 fn _fetch_by_coord_tuple(&mut self, tid: i32, beg: i64, end: i64) -> Result<()> {
752 if let Some(itr) = self.itr {
753 unsafe { htslib::hts_itr_destroy(itr) }
754 }
755 let itr = unsafe { htslib::sam_itr_queryi(self.index().inner_ptr(), tid, beg, end) };
756 if itr.is_null() {
757 self.itr = None;
758 Err(Error::Fetch)
759 } else {
760 self.itr = Some(itr);
761 Ok(())
762 }
763 }
764
765 fn _fetch_by_str(&mut self, region: &[u8]) -> Result<()> {
766 if let Some(itr) = self.itr {
767 unsafe { htslib::hts_itr_destroy(itr) }
768 }
769 let rstr = ffi::CString::new(region).unwrap();
770 let rptr = rstr.as_ptr();
771 let itr = unsafe {
772 htslib::sam_itr_querys(
773 self.index().inner_ptr(),
774 self.header().inner_ptr() as *mut hts_sys::sam_hdr_t,
775 rptr,
776 )
777 };
778 if itr.is_null() {
779 self.itr = None;
780 Err(Error::Fetch)
781 } else {
782 self.itr = Some(itr);
783 Ok(())
784 }
785 }
786
787 extern "C" fn pileup_read(
788 data: *mut ::std::os::raw::c_void,
789 record: *mut htslib::bam1_t,
790 ) -> i32 {
791 let _self = unsafe { (data as *mut Self).as_mut().unwrap() };
792 match _self.itr {
793 Some(itr) => itr_next(_self.htsfile, itr, record), None => unsafe {
795 htslib::sam_read1(
796 _self.htsfile,
797 _self.header().inner_ptr() as *mut hts_sys::sam_hdr_t,
798 record,
799 )
800 }, }
802 }
803
804 pub fn set_reference<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
810 unsafe { set_fai_filename(self.htsfile, path) }
811 }
812
813 pub fn index(&self) -> &IndexView {
814 &self.idx
815 }
816
817 unsafe fn slow_idxstats(&mut self) -> Result<Vec<(i64, u64, u64, u64)>> {
820 self.set_cram_options(
821 hts_sys::hts_fmt_option_CRAM_OPT_REQUIRED_FIELDS,
822 hts_sys::sam_fields_SAM_RNAME | hts_sys::sam_fields_SAM_FLAG,
823 )?;
824 let header = self.header();
825 let h = header.inner;
826 let mut ret;
827 let mut last_tid = -2;
828 let fp = self.htsfile();
829
830 let nref =
831 usize::try_from(hts_sys::sam_hdr_nref(h)).map_err(|_| Error::NoSequencesInReference)?;
832 if nref == 0 {
833 return Ok(vec![]);
834 }
835 let mut counts = vec![vec![0; 2]; nref + 1];
836 let mut bb: hts_sys::bam1_t = MaybeUninit::zeroed().assume_init();
837 let b = &mut bb as *mut hts_sys::bam1_t;
838 loop {
839 ret = hts_sys::sam_read1(fp, h, b);
840 if ret < 0 {
841 break;
842 }
843 let tid = (*b).core.tid;
844 if tid >= nref as i32 || tid < -1 {
845 return Err(Error::InvalidTid { tid });
846 }
847
848 if tid != last_tid {
849 if (last_tid >= -1) && (counts[tid as usize][0] + counts[tid as usize][1]) > 0 {
850 return Err(Error::BamUnsorted);
851 }
852 last_tid = tid;
853 }
854
855 let idx = if ((*b).core.flag as u32 & hts_sys::BAM_FUNMAP) > 0 {
856 1
857 } else {
858 0
859 };
860 counts[(*b).core.tid as usize][idx] += 1;
861 }
862
863 if ret == -1 {
864 let res = (0..nref)
865 .map(|i| {
866 (
867 i as i64,
868 header.target_len(i as u32).unwrap(),
869 counts[i][0],
870 counts[i][1],
871 )
872 })
873 .chain([(-1, 0, counts[nref][0], counts[nref][1])])
874 .collect();
875 Ok(res)
876 } else {
877 Err(Error::SlowIdxStats)
878 }
879 }
880
881 pub fn index_stats(&mut self) -> Result<Vec<(i64, u64, u64, u64)>> {
886 let header = self.header();
887 let index = self.index();
888 if index.inner_ptr().is_null() {
889 panic!("Index is null");
890 }
891 unsafe {
893 if (*self.htsfile()).format.format != htslib::htsExactFormat_bam {
894 return self.slow_idxstats();
895 }
896 }
897 Ok((0..header.target_count())
898 .map(|tid| {
899 let (mapped, unmapped) = index.number_mapped_unmapped(tid);
900 let tlen = header.target_len(tid).unwrap();
901 (tid as i64, tlen, mapped, unmapped)
902 })
903 .chain([(-1, 0, 0, index.number_unmapped())])
904 .collect::<_>())
905 }
906}
907
908#[derive(Debug)]
909pub struct IndexView {
910 inner: *mut hts_sys::hts_idx_t,
911 owned: bool,
912}
913
914impl IndexView {
915 fn new(hts_idx: *mut hts_sys::hts_idx_t) -> Self {
916 Self {
917 inner: hts_idx,
918 owned: true,
919 }
920 }
921
922 #[inline]
923 pub fn inner(&self) -> &hts_sys::hts_idx_t {
924 unsafe { self.inner.as_ref().unwrap() }
925 }
926
927 #[inline]
928 pub fn inner_ptr(&self) -> *const hts_sys::hts_idx_t {
930 self.inner
931 }
932
933 #[inline]
934 pub fn inner_mut(&mut self) -> &mut hts_sys::hts_idx_t {
935 unsafe { self.inner.as_mut().unwrap() }
936 }
937
938 #[inline]
939 pub fn inner_ptr_mut(&mut self) -> *mut hts_sys::hts_idx_t {
941 self.inner
942 }
943
944 fn number_mapped_unmapped(&self, tid: u32) -> (u64, u64) {
947 let (mut mapped, mut unmapped) = (0, 0);
948 unsafe {
949 hts_sys::hts_idx_get_stat(self.inner, tid as i32, &mut mapped, &mut unmapped);
950 }
951 (mapped, unmapped)
952 }
953
954 fn number_unmapped(&self) -> u64 {
957 unsafe { hts_sys::hts_idx_get_n_no_coor(self.inner) }
958 }
959}
960
961impl Drop for IndexView {
962 fn drop(&mut self) {
963 if self.owned {
964 unsafe {
965 htslib::hts_idx_destroy(self.inner);
966 }
967 }
968 }
969}
970
971impl Read for IndexedReader {
972 fn read(&mut self, record: &mut record::Record) -> Option<Result<()>> {
973 match self.itr {
974 Some(itr) => {
975 match itr_next(self.htsfile, itr, &mut record.inner as *mut htslib::bam1_t) {
976 -1 => None,
977 -2 => Some(Err(Error::BamTruncatedRecord)),
978 -4 => Some(Err(Error::BamInvalidRecord)),
979 _ => {
980 record.set_header(Rc::clone(&self.header));
981
982 Some(Ok(()))
983 }
984 }
985 }
986 None => None,
987 }
988 }
989
990 fn records(&mut self) -> Records<'_, Self> {
995 Records { reader: self }
996 }
997
998 fn rc_records(&mut self) -> RcRecords<'_, Self> {
999 RcRecords {
1000 reader: self,
1001 record: Rc::new(record::Record::new()),
1002 }
1003 }
1004
1005 fn pileup(&mut self) -> pileup::Pileups<'_, Self> {
1006 let _self = self as *const Self;
1007 let itr = unsafe {
1008 htslib::bam_plp_init(
1009 Some(IndexedReader::pileup_read),
1010 _self as *mut ::std::os::raw::c_void,
1011 )
1012 };
1013 pileup::Pileups::new(self, itr)
1014 }
1015
1016 fn htsfile(&self) -> *mut htslib::htsFile {
1017 self.htsfile
1018 }
1019
1020 fn header(&self) -> &HeaderView {
1021 &self.header
1022 }
1023
1024 fn set_thread_pool(&mut self, tpool: &ThreadPool) -> Result<()> {
1025 unsafe { set_thread_pool(self.htsfile(), tpool)? }
1026 self.tpool = Some(tpool.clone());
1027 Ok(())
1028 }
1029}
1030
1031impl Drop for IndexedReader {
1032 fn drop(&mut self) {
1033 unsafe {
1034 if self.itr.is_some() {
1035 htslib::hts_itr_destroy(self.itr.unwrap());
1036 }
1037 htslib::hts_close(self.htsfile);
1038 }
1039 }
1040}
1041
1042#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1043pub enum Format {
1044 Sam,
1045 Bam,
1046 Cram,
1047}
1048
1049impl Format {
1050 fn write_mode(self) -> &'static [u8] {
1051 match self {
1052 Format::Sam => b"w",
1053 Format::Bam => b"wb",
1054 Format::Cram => b"wc",
1055 }
1056 }
1057}
1058
1059#[derive(Debug)]
1061pub struct Writer {
1062 f: *mut htslib::htsFile,
1063 header: Rc<HeaderView>,
1064 tpool: Option<ThreadPool>,
1065}
1066
1067unsafe impl Send for Writer {}
1068
1069impl Writer {
1070 pub fn from_path<P: AsRef<Path>>(
1078 path: P,
1079 header: &header::Header,
1080 format: Format,
1081 ) -> Result<Self> {
1082 Self::new(&path_as_bytes(path, false)?, format.write_mode(), header)
1083 }
1084
1085 pub fn from_stdout(header: &header::Header, format: Format) -> Result<Self> {
1092 Self::new(b"-", format.write_mode(), header)
1093 }
1094
1095 fn new(path: &[u8], mode: &[u8], header: &header::Header) -> Result<Self> {
1103 let f = hts_open(path, mode)?;
1104
1105 let header_record = unsafe {
1110 let mut header_string = header.to_bytes();
1111 if !header_string.is_empty() && header_string[header_string.len() - 1] != b'\n' {
1112 header_string.push(b'\n');
1113 }
1114 let l_text = header_string.len();
1115 let text = ::libc::malloc(l_text + 1);
1116 libc::memset(text, 0, l_text + 1);
1117 libc::memcpy(
1118 text,
1119 header_string.as_ptr() as *const ::libc::c_void,
1120 header_string.len(),
1121 );
1122
1123 let rec = htslib::sam_hdr_parse(l_text + 1, text as *const c_char);
1125
1126 (*rec).text = text as *mut c_char;
1127 (*rec).l_text = l_text;
1128 rec
1129 };
1130
1131 unsafe {
1132 htslib::sam_hdr_write(f, header_record);
1133 }
1134
1135 Ok(Writer {
1136 f,
1137 header: Rc::new(HeaderView::new(header_record)),
1138 tpool: None,
1139 })
1140 }
1141
1142 pub fn set_threads(&mut self, n_threads: usize) -> Result<()> {
1149 unsafe { set_threads(self.f, n_threads) }
1150 }
1151
1152 pub fn set_thread_pool(&mut self, tpool: &ThreadPool) -> Result<()> {
1160 unsafe { set_thread_pool(self.f, tpool)? }
1161 self.tpool = Some(tpool.clone());
1162 Ok(())
1163 }
1164
1165 pub fn write(&mut self, record: &record::Record) -> Result<()> {
1171 if unsafe { htslib::sam_write1(self.f, self.header.inner(), record.inner_ptr()) } == -1 {
1172 Err(Error::WriteRecord)
1173 } else {
1174 Ok(())
1175 }
1176 }
1177
1178 pub fn header(&self) -> &HeaderView {
1180 &self.header
1181 }
1182
1183 pub fn set_reference<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
1189 unsafe { set_fai_filename(self.f, path) }
1190 }
1191
1192 pub fn set_compression_level(&mut self, compression_level: CompressionLevel) -> Result<()> {
1198 let level = compression_level.convert()?;
1199 match unsafe {
1200 htslib::hts_set_opt(
1201 self.f,
1202 htslib::hts_fmt_option_HTS_OPT_COMPRESSION_LEVEL,
1203 level,
1204 )
1205 } {
1206 0 => Ok(()),
1207 _ => Err(Error::BamInvalidCompressionLevel { level }),
1208 }
1209 }
1210}
1211
1212#[derive(Debug, Clone, Copy)]
1219pub enum CompressionLevel {
1220 Uncompressed,
1221 Fastest,
1222 Maximum,
1223 Level(u32),
1224}
1225
1226impl CompressionLevel {
1227 fn convert(self) -> Result<u32> {
1229 match self {
1230 CompressionLevel::Uncompressed => Ok(0),
1231 CompressionLevel::Fastest => Ok(1),
1232 CompressionLevel::Maximum => Ok(9),
1233 CompressionLevel::Level(i @ 0..=9) => Ok(i),
1234 CompressionLevel::Level(i) => Err(Error::BamInvalidCompressionLevel { level: i }),
1235 }
1236 }
1237}
1238
1239impl Drop for Writer {
1240 fn drop(&mut self) {
1241 unsafe {
1242 htslib::hts_close(self.f);
1243 }
1244 }
1245}
1246
1247#[derive(Debug)]
1249pub struct Records<'a, R: Read> {
1250 reader: &'a mut R,
1251}
1252
1253impl<R: Read> Iterator for Records<'_, R> {
1254 type Item = Result<record::Record>;
1255
1256 fn next(&mut self) -> Option<Result<record::Record>> {
1257 let mut record = record::Record::new();
1258 match self.reader.read(&mut record) {
1259 None => None,
1260 Some(Ok(_)) => Some(Ok(record)),
1261 Some(Err(err)) => Some(Err(err)),
1262 }
1263 }
1264}
1265
1266#[derive(Debug)]
1270pub struct RcRecords<'a, R: Read> {
1271 reader: &'a mut R,
1272 record: Rc<record::Record>,
1273}
1274
1275impl<R: Read> Iterator for RcRecords<'_, R> {
1276 type Item = Result<Rc<record::Record>>;
1277
1278 fn next(&mut self) -> Option<Self::Item> {
1279 let record = match Rc::get_mut(&mut self.record) {
1280 Some(x) => x,
1282 None => {
1283 self.record = Rc::new(record::Record::new());
1284 Rc::get_mut(&mut self.record).unwrap()
1285 }
1286 };
1287
1288 match self.reader.read(record) {
1289 None => None,
1290 Some(Ok(_)) => Some(Ok(Rc::clone(&self.record))),
1291 Some(Err(err)) => Some(Err(err)),
1292 }
1293 }
1294}
1295
1296pub struct ChunkIterator<'a, R: Read> {
1298 reader: &'a mut R,
1299 end: Option<i64>,
1300}
1301
1302impl<R: Read> Iterator for ChunkIterator<'_, R> {
1303 type Item = Result<record::Record>;
1304 fn next(&mut self) -> Option<Result<record::Record>> {
1305 if let Some(pos) = self.end {
1306 if self.reader.tell() >= pos {
1307 return None;
1308 }
1309 }
1310 let mut record = record::Record::new();
1311 match self.reader.read(&mut record) {
1312 None => None,
1313 Some(Ok(_)) => Some(Ok(record)),
1314 Some(Err(err)) => Some(Err(err)),
1315 }
1316 }
1317}
1318
1319fn hts_open(path: &[u8], mode: &[u8]) -> Result<*mut htslib::htsFile> {
1321 let cpath = ffi::CString::new(path).unwrap();
1322 let path = str::from_utf8(path).unwrap();
1323 let c_str = ffi::CString::new(mode).unwrap();
1324 let ret = unsafe { htslib::hts_open(cpath.as_ptr(), c_str.as_ptr()) };
1325 if ret.is_null() {
1326 Err(Error::BamOpen {
1327 target: path.to_owned(),
1328 })
1329 } else {
1330 if !mode.contains(&b'w') {
1331 unsafe {
1332 if (*ret).format.format != htslib::htsExactFormat_sam
1335 && (*ret).format.format != htslib::htsExactFormat_bam
1336 && (*ret).format.format != htslib::htsExactFormat_cram
1337 {
1338 return Err(Error::BamOpen {
1339 target: path.to_owned(),
1340 });
1341 }
1342 }
1343 }
1344 Ok(ret)
1345 }
1346}
1347
1348fn itr_next(
1350 htsfile: *mut htslib::htsFile,
1351 itr: *mut htslib::hts_itr_t,
1352 record: *mut htslib::bam1_t,
1353) -> i32 {
1354 unsafe {
1355 htslib::hts_itr_next(
1356 (*htsfile).fp.bgzf,
1357 itr,
1358 record as *mut ::std::os::raw::c_void,
1359 htsfile as *mut ::std::os::raw::c_void,
1360 )
1361 }
1362}
1363
1364#[derive(Debug)]
1365pub struct HeaderView {
1366 inner: *mut htslib::bam_hdr_t,
1367 owned: bool,
1368}
1369
1370impl HeaderView {
1371 pub fn from_header(header: &Header) -> Self {
1373 let mut header_string = header.to_bytes();
1374 if !header_string.is_empty() && header_string[header_string.len() - 1] != b'\n' {
1375 header_string.push(b'\n');
1376 }
1377 Self::from_bytes(&header_string)
1378 }
1379
1380 pub fn from_bytes(header_string: &[u8]) -> Self {
1382 let header_record = unsafe {
1383 let l_text = header_string.len();
1384 let text = ::libc::malloc(l_text + 1);
1385 ::libc::memset(text, 0, l_text + 1);
1386 ::libc::memcpy(
1387 text,
1388 header_string.as_ptr() as *const ::libc::c_void,
1389 header_string.len(),
1390 );
1391
1392 let rec = htslib::sam_hdr_parse(l_text + 1, text as *const c_char);
1393 (*rec).text = text as *mut c_char;
1394 (*rec).l_text = l_text;
1395 rec
1396 };
1397
1398 HeaderView::new(header_record)
1399 }
1400
1401 pub fn new(inner: *mut htslib::bam_hdr_t) -> Self {
1403 HeaderView { inner, owned: true }
1404 }
1405
1406 #[inline]
1407 pub fn inner(&self) -> &htslib::bam_hdr_t {
1408 unsafe { self.inner.as_ref().unwrap() }
1409 }
1410
1411 #[inline]
1412 pub fn inner_ptr(&self) -> *const htslib::bam_hdr_t {
1414 self.inner
1415 }
1416
1417 #[inline]
1418 pub fn inner_mut(&mut self) -> &mut htslib::bam_hdr_t {
1419 unsafe { self.inner.as_mut().unwrap() }
1420 }
1421
1422 #[inline]
1423 pub fn inner_ptr_mut(&mut self) -> *mut htslib::bam_hdr_t {
1425 self.inner
1426 }
1427
1428 pub fn tid(&self, name: &[u8]) -> Option<u32> {
1429 let c_str = ffi::CString::new(name).expect("Expected valid name.");
1430 let tid = unsafe { htslib::sam_hdr_name2tid(self.inner, c_str.as_ptr()) };
1431 if tid < 0 {
1432 None
1433 } else {
1434 Some(tid as u32)
1435 }
1436 }
1437
1438 pub fn tid2name(&self, tid: u32) -> &[u8] {
1439 unsafe { ffi::CStr::from_ptr(htslib::sam_hdr_tid2name(self.inner, tid as i32)).to_bytes() }
1440 }
1441
1442 pub fn target_count(&self) -> u32 {
1443 self.inner().n_targets as u32
1444 }
1445
1446 pub fn target_names(&self) -> Vec<&[u8]> {
1447 let names = unsafe {
1448 slice::from_raw_parts(self.inner().target_name, self.target_count() as usize)
1449 };
1450 names
1451 .iter()
1452 .map(|name| unsafe { ffi::CStr::from_ptr(*name).to_bytes() })
1453 .collect()
1454 }
1455
1456 pub fn target_len(&self, tid: u32) -> Option<u64> {
1457 let inner = unsafe { *self.inner };
1458 if (tid as i32) < inner.n_targets {
1459 let l: &[u32] =
1460 unsafe { slice::from_raw_parts(inner.target_len, inner.n_targets as usize) };
1461 Some(l[tid as usize] as u64)
1462 } else {
1463 None
1464 }
1465 }
1466
1467 pub fn as_bytes(&self) -> &[u8] {
1469 unsafe {
1470 let rebuilt_hdr = htslib::sam_hdr_str(self.inner);
1471 if rebuilt_hdr.is_null() {
1472 return b"";
1473 }
1474 ffi::CStr::from_ptr(rebuilt_hdr).to_bytes()
1475 }
1476 }
1477}
1478
1479impl Clone for HeaderView {
1480 fn clone(&self) -> Self {
1481 HeaderView {
1482 inner: unsafe { htslib::sam_hdr_dup(self.inner) },
1483 owned: true,
1484 }
1485 }
1486}
1487
1488impl Drop for HeaderView {
1489 fn drop(&mut self) {
1490 if self.owned {
1491 unsafe {
1492 htslib::sam_hdr_destroy(self.inner);
1493 }
1494 }
1495 }
1496}
1497
1498#[cfg(test)]
1499mod tests {
1500 use super::header::HeaderRecord;
1501 use super::record::{Aux, Cigar, CigarString};
1502 use super::*;
1503 use std::collections::HashMap;
1504 use std::fs;
1505 use std::path::Path;
1506 use std::str;
1507
1508 type GoldType = (
1509 [&'static [u8]; 6],
1510 [u16; 6],
1511 [&'static [u8]; 6],
1512 [&'static [u8]; 6],
1513 [CigarString; 6],
1514 );
1515 fn gold() -> GoldType {
1516 let names = [
1517 &b"I"[..],
1518 &b"II.14978392"[..],
1519 &b"III"[..],
1520 &b"IV"[..],
1521 &b"V"[..],
1522 &b"VI"[..],
1523 ];
1524 let flags = [16u16, 16u16, 16u16, 16u16, 16u16, 2048u16];
1525 let seqs = [
1526 &b"CCTAGCCCTAACCCTAACCCTAACCCTAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCC\
1527TAAGCCTAAGCCTAAGCCTAA"[..],
1528 &b"CCTAGCCCTAACCCTAACCCTAACCCTAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCC\
1529TAAGCCTAAGCCTAAGCCTAA"[..],
1530 &b"CCTAGCCCTAACCCTAACCCTAACCCTAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCC\
1531TAAGCCTAAGCCTAAGCCTAA"[..],
1532 &b"CCTAGCCCTAACCCTAACCCTAACCCTAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCC\
1533TAAGCCTAAGCCTAAGCCTAA"[..],
1534 &b"CCTAGCCCTAACCCTAACCCTAACCCTAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCC\
1535TAAGCCTAAGCCTAAGCCTAA"[..],
1536 &b"ACTAAGCCTAAGCCTAAGCCTAAGCCAATTATCGATTTCTGAAAAAATTATCGAATTTTCTAGAAATTTTGCAAATTTT\
1537TTCATAAAATTATCGATTTTA"[..],
1538 ];
1539 let quals = [
1540 &b"#############################@B?8B?BA@@DDBCDDCBC@CDCDCCCCCCCCCCCCCCCCCCCCCCCCCCCC\
1541CCCCCCCCCCCCCCCCCCC"[..],
1542 &b"#############################@B?8B?BA@@DDBCDDCBC@CDCDCCCCCCCCCCCCCCCCCCCCCCCCCCCC\
1543CCCCCCCCCCCCCCCCCCC"[..],
1544 &b"#############################@B?8B?BA@@DDBCDDCBC@CDCDCCCCCCCCCCCCCCCCCCCCCCCCCCCC\
1545CCCCCCCCCCCCCCCCCCC"[..],
1546 &b"#############################@B?8B?BA@@DDBCDDCBC@CDCDCCCCCCCCCCCCCCCCCCCCCCCCCCCC\
1547CCCCCCCCCCCCCCCCCCC"[..],
1548 &b"#############################@B?8B?BA@@DDBCDDCBC@CDCDCCCCCCCCCCCCCCCCCCCCCCCCCCCC\
1549CCCCCCCCCCCCCCCCCCC"[..],
1550 &b"#############################@B?8B?BA@@DDBCDDCBC@CDCDCCCCCCCCCCCCCCCCCCCCCCCCCCCC\
1551CCCCCCCCCCCCCCCCCCC"[..],
1552 ];
1553 let cigars = [
1554 CigarString(vec![Cigar::Match(27), Cigar::Del(1), Cigar::Match(73)]),
1555 CigarString(vec![Cigar::Match(27), Cigar::Del(1), Cigar::Match(73)]),
1556 CigarString(vec![Cigar::Match(27), Cigar::Del(1), Cigar::Match(73)]),
1557 CigarString(vec![Cigar::Match(27), Cigar::Del(1), Cigar::Match(73)]),
1558 CigarString(vec![Cigar::Match(27), Cigar::Del(1), Cigar::Match(73)]),
1559 CigarString(vec![Cigar::Match(27), Cigar::Del(100000), Cigar::Match(73)]),
1560 ];
1561 (names, flags, seqs, quals, cigars)
1562 }
1563
1564 fn compare_inner_bam_cram_records(cram_records: &[Record], bam_records: &[Record]) {
1565 for (c1, b1) in cram_records.iter().zip(bam_records.iter()) {
1567 assert_eq!(c1.cigar(), b1.cigar());
1570 assert_eq!(c1.inner().core.pos, b1.inner().core.pos);
1571 assert_eq!(c1.inner().core.mpos, b1.inner().core.mpos);
1572 assert_eq!(c1.inner().core.mtid, b1.inner().core.mtid);
1573 assert_eq!(c1.inner().core.tid, b1.inner().core.tid);
1574 assert_eq!(c1.inner().core.bin, b1.inner().core.bin);
1575 assert_eq!(c1.inner().core.qual, b1.inner().core.qual);
1576 assert_eq!(c1.inner().core.l_extranul, b1.inner().core.l_extranul);
1577 assert_eq!(c1.inner().core.flag, b1.inner().core.flag);
1578 assert_eq!(c1.inner().core.l_qname, b1.inner().core.l_qname);
1579 assert_eq!(c1.inner().core.n_cigar, b1.inner().core.n_cigar);
1580 assert_eq!(c1.inner().core.l_qseq, b1.inner().core.l_qseq);
1581 assert_eq!(c1.inner().core.isize_, b1.inner().core.isize_);
1582 }
1584 }
1585
1586 #[test]
1587 fn test_read() {
1588 let (names, flags, seqs, quals, cigars) = gold();
1589 let mut bam = Reader::from_path(Path::new("test/test.bam")).expect("Error opening file.");
1590 let del_len = [1, 1, 1, 1, 1, 100000];
1591
1592 for (i, record) in bam.records().enumerate() {
1593 let rec = record.expect("Expected valid record");
1594 assert_eq!(rec.qname(), names[i]);
1595 assert_eq!(rec.flags(), flags[i]);
1596 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1597
1598 let cigar = rec.cigar();
1599 assert_eq!(*cigar, cigars[i]);
1600
1601 let end_pos = cigar.end_pos();
1602 assert_eq!(end_pos, rec.pos() + 100 + del_len[i]);
1603 assert_eq!(
1604 cigar
1605 .read_pos(end_pos as u32 - 10, false, false)
1606 .unwrap()
1607 .unwrap(),
1608 90
1609 );
1610 assert_eq!(
1611 cigar
1612 .read_pos(rec.pos() as u32 + 20, false, false)
1613 .unwrap()
1614 .unwrap(),
1615 20
1616 );
1617 assert_eq!(cigar.read_pos(4000000, false, false).unwrap(), None);
1618 let qual: Vec<u8> = quals[i].iter().map(|&q| q - 33).collect();
1620 assert_eq!(rec.qual(), &qual[..]);
1621 }
1622 }
1623
1624 #[test]
1625 fn test_seek() {
1626 let mut bam = Reader::from_path(Path::new("test/test.bam")).expect("Error opening file.");
1627
1628 let mut names_by_voffset = HashMap::new();
1629
1630 let mut offset = bam.tell();
1631 let mut rec = Record::new();
1632 while let Some(r) = bam.read(&mut rec) {
1633 r.expect("error reading bam");
1634 let qname = str::from_utf8(rec.qname()).unwrap().to_string();
1635 println!("{} {}", offset, qname);
1636 names_by_voffset.insert(offset, qname);
1637 offset = bam.tell();
1638 }
1639
1640 for (offset, qname) in names_by_voffset.iter() {
1641 println!("{} {}", offset, qname);
1642 bam.seek(*offset).unwrap();
1643 if let Some(r) = bam.read(&mut rec) {
1644 r.unwrap();
1645 };
1646 let rec_qname = str::from_utf8(rec.qname()).unwrap().to_string();
1647 assert_eq!(qname, &rec_qname);
1648 }
1649 }
1650
1651 #[test]
1652 fn test_read_sam_header() {
1653 let bam = Reader::from_path("test/test.bam").expect("Error opening file.");
1654
1655 let true_header = "@SQ\tSN:CHROMOSOME_I\tLN:15072423\n@SQ\tSN:CHROMOSOME_II\tLN:15279345\
1656 \n@SQ\tSN:CHROMOSOME_III\tLN:13783700\n@SQ\tSN:CHROMOSOME_IV\tLN:17493793\n@SQ\t\
1657 SN:CHROMOSOME_V\tLN:20924149\n"
1658 .to_string();
1659 let header_text = String::from_utf8(bam.header.as_bytes().to_owned()).unwrap();
1660 assert_eq!(header_text, true_header);
1661 }
1662
1663 #[test]
1664 fn test_read_against_sam() {
1665 let mut bam = Reader::from_path("./test/bam2sam_out.sam").unwrap();
1666 for read in bam.records() {
1667 let _read = read.unwrap();
1668 }
1669 }
1670
1671 fn _test_read_indexed_common(mut bam: IndexedReader) {
1672 let (names, flags, seqs, quals, cigars) = gold();
1673 let sq_1 = b"CHROMOSOME_I";
1674 let sq_2 = b"CHROMOSOME_II";
1675 let tid_1 = bam.header.tid(sq_1).expect("Expected tid.");
1676 let tid_2 = bam.header.tid(sq_2).expect("Expected tid.");
1677 assert!(bam.header.target_len(tid_1).expect("Expected target len.") == 15072423);
1678
1679 bam.fetch((tid_1, 0, 2))
1681 .expect("Expected successful fetch.");
1682 assert!(bam.records().count() == 6);
1683
1684 bam.fetch((tid_1, 0, 2))
1686 .expect("Expected successful fetch.");
1687 for (i, record) in bam.records().enumerate() {
1688 let rec = record.expect("Expected valid record");
1689
1690 println!("{}", str::from_utf8(rec.qname()).unwrap());
1691 assert_eq!(rec.qname(), names[i]);
1692 assert_eq!(rec.flags(), flags[i]);
1693 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1694 assert_eq!(*rec.cigar(), cigars[i]);
1695 let qual: Vec<u8> = quals[i].iter().map(|&q| q - 33).collect();
1697 assert_eq!(rec.qual(), &qual[..]);
1698 assert_eq!(rec.aux(b"NotAvailableAux"), Err(Error::BamAuxTagNotFound));
1699 }
1700
1701 bam.fetch((tid_2, 1, 1))
1703 .expect("Expected successful fetch.");
1704 assert!(bam.records().count() == 0);
1705
1706 bam.fetch(format!("{}:{}-{}", str::from_utf8(sq_1).unwrap(), 0, 2).as_bytes())
1711 .expect("Expected successful fetch.");
1712 assert!(bam.records().count() == 6);
1713 bam.fetch((str::from_utf8(sq_1).unwrap(), 0_u32, 2_u64))
1715 .expect("Expected successful fetch.");
1716 assert!(bam.records().count() == 6);
1717 bam.fetch((&sq_1[..], 0, 2))
1719 .expect("Expected successful fetch.");
1720 assert!(bam.records().count() == 6);
1721 bam.fetch((sq_1, 0, 2)).expect("Expected successful fetch.");
1723 assert!(bam.records().count() == 6);
1724
1725 bam.fetch((0i32, 0u32, 2i64))
1727 .expect("Expected successful fetch.");
1728 assert!(bam.records().count() == 6);
1729 bam.fetch((0u32, 0u32, 2i64))
1731 .expect("Expected successful fetch.");
1732 assert!(bam.records().count() == 6);
1733
1734 bam.fetch(format!("{}:{}-{}", str::from_utf8(sq_1).unwrap(), 0, 2).as_bytes())
1736 .expect("Expected successful fetch.");
1737 for (i, record) in bam.records().enumerate() {
1738 let rec = record.expect("Expected valid record");
1739
1740 println!("{}", str::from_utf8(rec.qname()).unwrap());
1741 assert_eq!(rec.qname(), names[i]);
1742 assert_eq!(rec.flags(), flags[i]);
1743 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1744 assert_eq!(*rec.cigar(), cigars[i]);
1745 let qual: Vec<u8> = quals[i].iter().map(|&q| q - 33).collect();
1747 assert_eq!(rec.qual(), &qual[..]);
1748 assert_eq!(rec.aux(b"NotAvailableAux"), Err(Error::BamAuxTagNotFound));
1749 }
1750
1751 bam.fetch(format!("{}:{}-{}", str::from_utf8(sq_2).unwrap(), 1, 1).as_bytes())
1753 .expect("Expected successful fetch.");
1754 assert!(bam.records().count() == 0);
1755
1756 bam.fetch(0).expect("Expected successful fetch.");
1758 assert!(bam.records().count() == 6);
1759 bam.fetch(0u32).expect("Expected successful fetch.");
1761 assert!(bam.records().count() == 6);
1762
1763 bam.fetch(sq_1).expect("Expected successful fetch.");
1765 assert!(bam.records().count() == 6);
1766 bam.fetch(str::from_utf8(sq_1).unwrap())
1768 .expect("Expected successful fetch.");
1769 assert!(bam.records().count() == 6);
1770
1771 bam.fetch(FetchDefinition::All)
1773 .expect("Expected successful fetch.");
1774 assert!(bam.records().count() == 6);
1775
1776 bam.fetch(".").expect("Expected successful fetch.");
1778 assert!(bam.records().count() == 6);
1779
1780 bam.fetch(FetchDefinition::Unmapped)
1782 .expect("Expected successful fetch.");
1783 assert_eq!(bam.records().count(), 1); bam.fetch("*").expect("Expected successful fetch.");
1786 assert_eq!(bam.records().count(), 1); }
1788
1789 #[test]
1790 fn test_read_indexed() {
1791 let bam = IndexedReader::from_path("test/test.bam").expect("Expected valid index.");
1792 _test_read_indexed_common(bam);
1793 }
1794
1795 #[test]
1796 fn test_read_indexed_different_index_name() {
1797 let bam = IndexedReader::from_path_and_index(
1798 &"test/test_different_index_name.bam",
1799 &"test/test.bam.bai",
1800 )
1801 .expect("Expected valid index.");
1802 _test_read_indexed_common(bam);
1803 }
1804
1805 #[test]
1806 fn test_set_record() {
1807 let (names, _, seqs, quals, cigars) = gold();
1808
1809 let mut rec = record::Record::new();
1810 rec.set_reverse();
1811 rec.set(names[0], Some(&cigars[0]), seqs[0], quals[0]);
1812 rec.push_aux(b"NM", Aux::I32(15)).unwrap();
1815
1816 assert_eq!(rec.qname(), names[0]);
1817 assert_eq!(*rec.cigar(), cigars[0]);
1818 assert_eq!(rec.seq().as_bytes(), seqs[0]);
1819 assert_eq!(rec.qual(), quals[0]);
1820 assert!(rec.is_reverse());
1821 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1822 }
1823
1824 #[test]
1825 fn test_set_repeated() {
1826 let mut rec = Record::new();
1827 rec.set(
1828 b"123",
1829 Some(&CigarString(vec![Cigar::Match(3)])),
1830 b"AAA",
1831 b"III",
1832 );
1833 rec.push_aux(b"AS", Aux::I32(12345)).unwrap();
1834 assert_eq!(rec.qname(), b"123");
1835 assert_eq!(rec.seq().as_bytes(), b"AAA");
1836 assert_eq!(rec.qual(), b"III");
1837 assert_eq!(rec.aux(b"AS").unwrap(), Aux::I32(12345));
1838
1839 rec.set(
1840 b"1234",
1841 Some(&CigarString(vec![Cigar::SoftClip(1), Cigar::Match(3)])),
1842 b"AAAA",
1843 b"IIII",
1844 );
1845 assert_eq!(rec.qname(), b"1234");
1846 assert_eq!(rec.seq().as_bytes(), b"AAAA");
1847 assert_eq!(rec.qual(), b"IIII");
1848 assert_eq!(rec.aux(b"AS").unwrap(), Aux::I32(12345));
1849
1850 rec.set(
1851 b"12",
1852 Some(&CigarString(vec![Cigar::Match(2)])),
1853 b"AA",
1854 b"II",
1855 );
1856 assert_eq!(rec.qname(), b"12");
1857 assert_eq!(rec.seq().as_bytes(), b"AA");
1858 assert_eq!(rec.qual(), b"II");
1859 assert_eq!(rec.aux(b"AS").unwrap(), Aux::I32(12345));
1860 }
1861
1862 #[test]
1863 fn test_set_qname() {
1864 let (names, _, seqs, quals, cigars) = gold();
1865
1866 assert!(names[0] != names[1]);
1867
1868 for i in 0..names.len() {
1869 let mut rec = record::Record::new();
1870 rec.set(names[i], Some(&cigars[i]), seqs[i], quals[i]);
1871 rec.push_aux(b"NM", Aux::I32(15)).unwrap();
1872
1873 assert_eq!(rec.qname(), names[i]);
1874 assert_eq!(*rec.cigar(), cigars[i]);
1875 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1876 assert_eq!(rec.qual(), quals[i]);
1877 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1878
1879 assert!(rec.qname()[0] != b'X');
1881 rec.set_qname(b"X");
1882 assert_eq!(rec.qname(), b"X");
1883
1884 let mut longer_name = names[i].to_owned().clone();
1886 let extension = b"BuffaloBUffaloBUFFaloBUFFAloBUFFALoBUFFALO";
1887 longer_name.extend(extension.iter());
1888 rec.set_qname(&longer_name);
1889
1890 assert_eq!(rec.qname(), longer_name.as_slice());
1891 assert_eq!(*rec.cigar(), cigars[i]);
1892 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1893 assert_eq!(rec.qual(), quals[i]);
1894 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1895
1896 let shorter_name = b"42";
1898 rec.set_qname(shorter_name);
1899
1900 assert_eq!(rec.qname(), shorter_name);
1901 assert_eq!(*rec.cigar(), cigars[i]);
1902 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1903 assert_eq!(rec.qual(), quals[i]);
1904 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1905
1906 rec.set_qname(b"");
1908
1909 assert_eq!(rec.qname(), b"");
1910 assert_eq!(*rec.cigar(), cigars[i]);
1911 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1912 assert_eq!(rec.qual(), quals[i]);
1913 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1914 }
1915 }
1916
1917 #[test]
1918 fn test_set_qname2() {
1919 let mut _header = Header::new();
1920 _header.push_record(
1921 HeaderRecord::new(b"SQ")
1922 .push_tag(b"SN", "1")
1923 .push_tag(b"LN", 10000000),
1924 );
1925 let header = HeaderView::from_header(&_header);
1926
1927 let line =
1928 b"blah1 0 1 1 255 1M * 0 0 A F CB:Z:AAAA-1 UR:Z:AAAA UB:Z:AAAA GX:Z:G1 xf:i:1 fx:Z:G1\tli:i:0\ttf:Z:cC";
1929
1930 let mut rec = Record::from_sam(&header, line).unwrap();
1931 assert_eq!(rec.qname(), b"blah1");
1932 rec.set_qname(b"r0");
1933 assert_eq!(rec.qname(), b"r0");
1934 }
1935
1936 #[test]
1937 fn test_set_cigar() {
1938 let (names, _, seqs, quals, cigars) = gold();
1939
1940 assert!(names[0] != names[1]);
1941
1942 for i in 0..names.len() {
1943 let mut rec = record::Record::new();
1944 rec.set(names[i], Some(&cigars[i]), seqs[i], quals[i]);
1945 rec.push_aux(b"NM", Aux::I32(15)).unwrap();
1946
1947 assert_eq!(rec.qname(), names[i]);
1948 assert_eq!(*rec.cigar(), cigars[i]);
1949 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1950 assert_eq!(rec.qual(), quals[i]);
1951 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1952
1953 let new_cigar = CigarString(vec![Cigar::Match(rec.seq_len() as u32)]);
1955 assert_ne!(*rec.cigar(), new_cigar);
1956 rec.set_cigar(Some(&new_cigar));
1957 assert_eq!(*rec.cigar(), new_cigar);
1958
1959 assert_eq!(rec.qname(), names[i]);
1960 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1961 assert_eq!(rec.qual(), quals[i]);
1962 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1963
1964 let new_cigar = (0..rec.seq_len())
1966 .map(|i| {
1967 if i % 2 == 0 {
1968 Cigar::Match(1)
1969 } else {
1970 Cigar::Ins(1)
1971 }
1972 })
1973 .collect::<Vec<_>>();
1974 let new_cigar = CigarString(new_cigar);
1975 assert_ne!(*rec.cigar(), new_cigar);
1976 rec.set_cigar(Some(&new_cigar));
1977 assert_eq!(*rec.cigar(), new_cigar);
1978
1979 assert_eq!(rec.qname(), names[i]);
1980 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1981 assert_eq!(rec.qual(), quals[i]);
1982 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1983
1984 let new_cigar = CigarString(Vec::new());
1986 assert_ne!(*rec.cigar(), new_cigar);
1987 rec.set_cigar(None);
1988 assert_eq!(*rec.cigar(), new_cigar);
1989
1990 assert_eq!(rec.qname(), names[i]);
1991 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1992 assert_eq!(rec.qual(), quals[i]);
1993 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1994 }
1995 }
1996
1997 #[test]
1998 fn test_remove_aux() {
1999 let mut bam = Reader::from_path(Path::new("test/test.bam")).expect("Error opening file.");
2000
2001 for record in bam.records() {
2002 let mut rec = record.expect("Expected valid record");
2003
2004 if rec.aux(b"XS").is_ok() {
2005 rec.remove_aux(b"XS").unwrap();
2006 }
2007
2008 if rec.aux(b"YT").is_ok() {
2009 rec.remove_aux(b"YT").unwrap();
2010 }
2011
2012 assert!(rec.remove_aux(b"ab").is_err());
2013
2014 assert_eq!(rec.aux(b"XS"), Err(Error::BamAuxTagNotFound));
2015 assert_eq!(rec.aux(b"YT"), Err(Error::BamAuxTagNotFound));
2016 }
2017 }
2018
2019 #[test]
2020 fn test_write() {
2021 let (names, _, seqs, quals, cigars) = gold();
2022
2023 let tmp = tempfile::Builder::new()
2024 .prefix("rust-htslib")
2025 .tempdir()
2026 .expect("Cannot create temp dir");
2027 let bampath = tmp.path().join("test.bam");
2028 println!("{:?}", bampath);
2029 {
2030 let mut bam = Writer::from_path(
2031 &bampath,
2032 Header::new().push_record(
2033 HeaderRecord::new(b"SQ")
2034 .push_tag(b"SN", "chr1")
2035 .push_tag(b"LN", 15072423),
2036 ),
2037 Format::Bam,
2038 )
2039 .expect("Error opening file.");
2040
2041 for i in 0..names.len() {
2042 let mut rec = record::Record::new();
2043 rec.set(names[i], Some(&cigars[i]), seqs[i], quals[i]);
2044 rec.push_aux(b"NM", Aux::I32(15)).unwrap();
2045
2046 bam.write(&rec).expect("Failed to write record.");
2047 }
2048 }
2049
2050 {
2051 let mut bam = Reader::from_path(bampath).expect("Error opening file.");
2052
2053 for i in 0..names.len() {
2054 let mut rec = record::Record::new();
2055 if let Some(r) = bam.read(&mut rec) {
2056 r.expect("Failed to read record.");
2057 };
2058
2059 assert_eq!(rec.qname(), names[i]);
2060 assert_eq!(*rec.cigar(), cigars[i]);
2061 assert_eq!(rec.seq().as_bytes(), seqs[i]);
2062 assert_eq!(rec.qual(), quals[i]);
2063 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
2064 }
2065 }
2066
2067 tmp.close().expect("Failed to delete temp dir");
2068 }
2069
2070 #[test]
2071 fn test_write_threaded() {
2072 let (names, _, seqs, quals, cigars) = gold();
2073
2074 let tmp = tempfile::Builder::new()
2075 .prefix("rust-htslib")
2076 .tempdir()
2077 .expect("Cannot create temp dir");
2078 let bampath = tmp.path().join("test.bam");
2079 println!("{:?}", bampath);
2080 {
2081 let mut bam = Writer::from_path(
2082 &bampath,
2083 Header::new().push_record(
2084 HeaderRecord::new(b"SQ")
2085 .push_tag(b"SN", "chr1")
2086 .push_tag(b"LN", 15072423),
2087 ),
2088 Format::Bam,
2089 )
2090 .expect("Error opening file.");
2091 bam.set_threads(4).unwrap();
2092
2093 for i in 0..10000 {
2094 let mut rec = record::Record::new();
2095 let idx = i % names.len();
2096 rec.set(names[idx], Some(&cigars[idx]), seqs[idx], quals[idx]);
2097 rec.push_aux(b"NM", Aux::I32(15)).unwrap();
2098 rec.set_pos(i as i64);
2099
2100 bam.write(&rec).expect("Failed to write record.");
2101 }
2102 }
2103
2104 {
2105 let mut bam = Reader::from_path(bampath).expect("Error opening file.");
2106
2107 for (i, _rec) in bam.records().enumerate() {
2108 let idx = i % names.len();
2109
2110 let rec = _rec.expect("Failed to read record.");
2111
2112 assert_eq!(rec.pos(), i as i64);
2113 assert_eq!(rec.qname(), names[idx]);
2114 assert_eq!(*rec.cigar(), cigars[idx]);
2115 assert_eq!(rec.seq().as_bytes(), seqs[idx]);
2116 assert_eq!(rec.qual(), quals[idx]);
2117 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
2118 }
2119 }
2120
2121 tmp.close().expect("Failed to delete temp dir");
2122 }
2123
2124 #[test]
2125 fn test_write_shared_tpool() {
2126 let (names, _, seqs, quals, cigars) = gold();
2127
2128 let tmp = tempfile::Builder::new()
2129 .prefix("rust-htslib")
2130 .tempdir()
2131 .expect("Cannot create temp dir");
2132 let bampath1 = tmp.path().join("test1.bam");
2133 let bampath2 = tmp.path().join("test2.bam");
2134
2135 {
2136 let (mut bam1, mut bam2) = {
2137 let pool = crate::tpool::ThreadPool::new(4).unwrap();
2138
2139 let mut bam1 = Writer::from_path(
2140 &bampath1,
2141 Header::new().push_record(
2142 HeaderRecord::new(b"SQ")
2143 .push_tag(b"SN", "chr1")
2144 .push_tag(b"LN", 15072423),
2145 ),
2146 Format::Bam,
2147 )
2148 .expect("Error opening file.");
2149
2150 let mut bam2 = Writer::from_path(
2151 &bampath2,
2152 Header::new().push_record(
2153 HeaderRecord::new(b"SQ")
2154 .push_tag(b"SN", "chr1")
2155 .push_tag(b"LN", 15072423),
2156 ),
2157 Format::Bam,
2158 )
2159 .expect("Error opening file.");
2160
2161 bam1.set_thread_pool(&pool).unwrap();
2162 bam2.set_thread_pool(&pool).unwrap();
2163 (bam1, bam2)
2164 };
2165
2166 for i in 0..10000 {
2167 let mut rec = record::Record::new();
2168 let idx = i % names.len();
2169 rec.set(names[idx], Some(&cigars[idx]), seqs[idx], quals[idx]);
2170 rec.push_aux(b"NM", Aux::I32(15)).unwrap();
2171 rec.set_pos(i as i64);
2172
2173 bam1.write(&rec).expect("Failed to write record.");
2174 bam2.write(&rec).expect("Failed to write record.");
2175 }
2176 }
2177
2178 {
2179 let pool = crate::tpool::ThreadPool::new(2).unwrap();
2180
2181 for p in [bampath1, bampath2] {
2182 let mut bam = Reader::from_path(p).expect("Error opening file.");
2183 bam.set_thread_pool(&pool).unwrap();
2184
2185 for (i, _rec) in bam.iter_chunk(None, None).enumerate() {
2186 let idx = i % names.len();
2187
2188 let rec = _rec.expect("Failed to read record.");
2189
2190 assert_eq!(rec.pos(), i as i64);
2191 assert_eq!(rec.qname(), names[idx]);
2192 assert_eq!(*rec.cigar(), cigars[idx]);
2193 assert_eq!(rec.seq().as_bytes(), seqs[idx]);
2194 assert_eq!(rec.qual(), quals[idx]);
2195 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
2196 }
2197 }
2198 }
2199
2200 tmp.close().expect("Failed to delete temp dir");
2201 }
2202
2203 #[test]
2204 fn test_copy_template() {
2205 let tmp = tempfile::Builder::new()
2209 .prefix("rust-htslib")
2210 .tempdir()
2211 .expect("Cannot create temp dir");
2212 let bampath = tmp.path().join("test.bam");
2213 println!("{:?}", bampath);
2214
2215 let mut input_bam = Reader::from_path("test/test.bam").expect("Error opening file.");
2216
2217 {
2218 let mut bam = Writer::from_path(
2219 &bampath,
2220 &Header::from_template(input_bam.header()),
2221 Format::Bam,
2222 )
2223 .expect("Error opening file.");
2224
2225 for rec in input_bam.records() {
2226 bam.write(&rec.unwrap()).expect("Failed to write record.");
2227 }
2228 }
2229
2230 {
2231 let copy_bam = Reader::from_path(bampath).expect("Error opening file.");
2232
2233 assert_eq!(input_bam.header().as_bytes(), copy_bam.header().as_bytes());
2235 }
2236
2237 tmp.close().expect("Failed to delete temp dir");
2238 }
2239
2240 #[test]
2241 fn test_pileup() {
2242 let (_, _, seqs, quals, _) = gold();
2243
2244 let mut bam = Reader::from_path("test/test.bam").expect("Error opening file.");
2245 let pileups = bam.pileup();
2246 for pileup in pileups.take(26) {
2247 let _pileup = pileup.expect("Expected successful pileup.");
2248 let pos = _pileup.pos() as usize;
2249 assert_eq!(_pileup.depth(), 6);
2250 assert!(_pileup.tid() == 0);
2251 for (i, a) in _pileup.alignments().enumerate() {
2252 assert_eq!(a.indel(), pileup::Indel::None);
2253 let qpos = a.qpos().unwrap();
2254 assert_eq!(qpos, pos - 1);
2255 assert_eq!(a.record().seq()[qpos], seqs[i][qpos]);
2256 assert_eq!(a.record().qual()[qpos], quals[i][qpos] - 33);
2257 }
2258 }
2259 }
2260
2261 #[test]
2262 fn test_idx_pileup() {
2263 let mut bam = IndexedReader::from_path("test/test.bam").expect("Error opening file.");
2264 for pileup in bam.pileup() {
2266 pileup.unwrap();
2267 }
2268 let tid = bam.header().tid(b"CHROMOSOME_I").unwrap();
2270 bam.fetch((tid, 0, 5)).unwrap();
2271 for p in bam.pileup() {
2272 println!("{}", p.unwrap().pos())
2273 }
2274 }
2275
2276 #[test]
2277 fn parse_from_sam() {
2278 use std::fs::File;
2279 use std::io::Read;
2280
2281 let bamfile = "./test/bam2sam_test.bam";
2282 let samfile = "./test/bam2sam_expected.sam";
2283
2284 let mut rdr = Reader::from_path(bamfile).unwrap();
2286 let bam_recs: Vec<Record> = rdr.records().map(|v| v.unwrap()).collect();
2287
2288 let mut sam = Vec::new();
2289 assert!(File::open(samfile).unwrap().read_to_end(&mut sam).is_ok());
2290
2291 let sam_recs: Vec<Record> = sam
2292 .split(|x| *x == b'\n')
2293 .filter(|x| !x.is_empty() && x[0] != b'@')
2294 .map(|line| Record::from_sam(rdr.header(), line).unwrap())
2295 .collect();
2296
2297 for (b1, s1) in bam_recs.iter().zip(sam_recs.iter()) {
2298 assert!(b1 == s1);
2299 }
2300 }
2301
2302 #[test]
2303 fn test_cigar_modes() {
2304 let (_, _, _, _, cigars) = gold();
2307 let mut bam = Reader::from_path(Path::new("test/test.bam")).expect("Error opening file.");
2308
2309 for (i, record) in bam.records().enumerate() {
2310 let rec = record.expect("Expected valid record");
2311
2312 let cigar = rec.cigar();
2313 assert_eq!(*cigar, cigars[i]);
2314 }
2315
2316 for (i, record) in bam.records().enumerate() {
2317 let mut rec = record.expect("Expected valid record");
2318 rec.cache_cigar();
2319
2320 let cigar = rec.cigar_cached().unwrap();
2321 assert_eq!(**cigar, cigars[i]);
2322
2323 let cigar = rec.cigar();
2324 assert_eq!(*cigar, cigars[i]);
2325 }
2326 }
2327
2328 #[test]
2329 fn test_read_cram() {
2330 let cram_path = "./test/test_cram.cram";
2331 let bam_path = "./test/test_cram.bam";
2332 let ref_path = "./test/test_cram.fa";
2333
2334 let mut cram_reader = Reader::from_path(cram_path).unwrap();
2336 cram_reader.set_reference(ref_path).unwrap();
2337 let cram_records: Vec<Record> = cram_reader.records().map(|v| v.unwrap()).collect();
2338
2339 let mut bam_reader = Reader::from_path(bam_path).unwrap();
2341 let bam_records: Vec<Record> = bam_reader.records().map(|v| v.unwrap()).collect();
2342
2343 compare_inner_bam_cram_records(&cram_records, &bam_records);
2344 }
2345
2346 #[test]
2347 fn test_write_cram() {
2348 let bam_path = "./test/test_cram.bam";
2350 let ref_path = "./test/test_cram.fa";
2351 let mut bam_reader = Reader::from_path(bam_path).unwrap();
2352 let bam_records: Vec<Record> = bam_reader.records().map(|v| v.unwrap()).collect();
2353
2354 let tmp = tempfile::Builder::new()
2356 .prefix("rust-htslib")
2357 .tempdir()
2358 .expect("Cannot create temp dir");
2359 let cram_path = tmp.path().join("test.cram");
2360
2361 {
2363 let mut header = Header::new();
2364 header.push_record(
2365 HeaderRecord::new(b"HD")
2366 .push_tag(b"VN", "1.5")
2367 .push_tag(b"SO", "coordinate"),
2368 );
2369 header.push_record(
2370 HeaderRecord::new(b"SQ")
2371 .push_tag(b"SN", "chr1")
2372 .push_tag(b"LN", 120)
2373 .push_tag(b"M5", "20a9a0fb770814e6c5e49946750f9724")
2374 .push_tag(b"UR", "test/test_cram.fa"),
2375 );
2376 header.push_record(
2377 HeaderRecord::new(b"SQ")
2378 .push_tag(b"SN", "chr2")
2379 .push_tag(b"LN", 120)
2380 .push_tag(b"M5", "7a2006ccca94ea92b6dae5997e1b0d70")
2381 .push_tag(b"UR", "test/test_cram.fa"),
2382 );
2383 header.push_record(
2384 HeaderRecord::new(b"SQ")
2385 .push_tag(b"SN", "chr3")
2386 .push_tag(b"LN", 120)
2387 .push_tag(b"M5", "a66b336bfe3ee8801c744c9545c87e24")
2388 .push_tag(b"UR", "test/test_cram.fa"),
2389 );
2390
2391 let mut cram_writer = Writer::from_path(&cram_path, &header, Format::Cram)
2392 .expect("Error opening CRAM file.");
2393 cram_writer.set_reference(ref_path).unwrap();
2394
2395 for rec in bam_records.iter() {
2397 cram_writer
2398 .write(rec)
2399 .expect("Faied to write record to CRAM.");
2400 }
2401 }
2402
2403 {
2405 let mut cram_reader = Reader::from_path(cram_path).unwrap();
2407 cram_reader.set_reference(ref_path).unwrap();
2408 let cram_records: Vec<Record> = cram_reader.records().map(|v| v.unwrap()).collect();
2409
2410 compare_inner_bam_cram_records(&cram_records, &bam_records);
2412 }
2413
2414 tmp.close().expect("Failed to delete temp dir");
2415 }
2416
2417 #[test]
2418 fn test_compression_level_conversion() {
2419 assert_eq!(CompressionLevel::Uncompressed.convert().unwrap(), 0);
2421 assert_eq!(CompressionLevel::Fastest.convert().unwrap(), 1);
2422 assert_eq!(CompressionLevel::Maximum.convert().unwrap(), 9);
2423
2424 for level in 0..=9 {
2426 assert_eq!(CompressionLevel::Level(level).convert().unwrap(), level);
2427 }
2428 assert!(CompressionLevel::Level(10).convert().is_err());
2430 }
2431
2432 #[test]
2433 fn test_write_compression() {
2434 let tmp = tempfile::Builder::new()
2435 .prefix("rust-htslib")
2436 .tempdir()
2437 .expect("Cannot create temp dir");
2438 let input_bam_path = "test/test.bam";
2439
2440 let levels_to_test = vec![
2442 CompressionLevel::Maximum,
2443 CompressionLevel::Level(6),
2444 CompressionLevel::Fastest,
2445 CompressionLevel::Uncompressed,
2446 ];
2447 let file_sizes: Vec<_> = levels_to_test
2448 .iter()
2449 .map(|level| {
2450 let output_bam_path = tmp.path().join("test.bam");
2451 {
2452 let mut reader = Reader::from_path(input_bam_path).unwrap();
2453 let header = Header::from_template(reader.header());
2454 let mut writer =
2455 Writer::from_path(&output_bam_path, &header, Format::Bam).unwrap();
2456 writer.set_compression_level(*level).unwrap();
2457 for record in reader.records() {
2458 let r = record.unwrap();
2459 writer.write(&r).unwrap();
2460 }
2461 }
2462 fs::metadata(output_bam_path).unwrap().len()
2463 })
2464 .collect();
2465
2466 println!("testing compression leves: {:?}", levels_to_test);
2468 println!("got compressed sizes: {:?}", file_sizes);
2469
2470 #[cfg(feature = "libdeflate")]
2473 assert!(file_sizes[1..].windows(2).all(|size| size[0] <= size[1]));
2474
2475 #[cfg(not(feature = "libdeflate"))]
2476 assert!(file_sizes.windows(2).all(|size| size[0] <= size[1]));
2477
2478 tmp.close().expect("Failed to delete temp dir");
2479 }
2480
2481 #[test]
2482 fn test_bam_fails_on_vcf() {
2483 let bam_path = "./test/test_left.vcf";
2484 let bam_reader = Reader::from_path(bam_path);
2485 assert!(bam_reader.is_err());
2486 }
2487
2488 #[test]
2489 fn test_indexde_bam_fails_on_vcf() {
2490 let bam_path = "./test/test_left.vcf";
2491 let bam_reader = IndexedReader::from_path(bam_path);
2492 assert!(bam_reader.is_err());
2493 }
2494
2495 #[test]
2496 fn test_bam_fails_on_toml() {
2497 let bam_path = "./Cargo.toml";
2498 let bam_reader = Reader::from_path(bam_path);
2499 assert!(bam_reader.is_err());
2500 }
2501
2502 #[test]
2503 fn test_sam_writer_example() {
2504 fn from_bam_with_filter<F>(bamfile: &str, samfile: &str, f: F) -> bool
2505 where
2506 F: Fn(&record::Record) -> Option<bool>,
2507 {
2508 let mut bam_reader = Reader::from_path(bamfile).unwrap(); let header = header::Header::from_template(bam_reader.header());
2510 let mut sam_writer = Writer::from_path(samfile, &header, Format::Sam).unwrap();
2511 for record in bam_reader.records() {
2512 if record.is_err() {
2513 return false;
2514 }
2515 let parsed = record.unwrap();
2516 match f(&parsed) {
2517 None => return true,
2518 Some(false) => {}
2519 Some(true) => {
2520 if sam_writer.write(&parsed).is_err() {
2521 return false;
2522 }
2523 }
2524 }
2525 }
2526 true
2527 }
2528 use std::fs::File;
2529 use std::io::Read;
2530 let bamfile = "./test/bam2sam_test.bam";
2531 let samfile = "./test/bam2sam_out.sam";
2532 let expectedfile = "./test/bam2sam_expected.sam";
2533 let result = from_bam_with_filter(bamfile, samfile, |_| Some(true));
2534 assert!(result);
2535 let mut expected = Vec::new();
2536 let mut written = Vec::new();
2537 assert!(File::open(expectedfile)
2538 .unwrap()
2539 .read_to_end(&mut expected)
2540 .is_ok());
2541 assert!(File::open(samfile)
2542 .unwrap()
2543 .read_to_end(&mut written)
2544 .is_ok());
2545 assert_eq!(expected, written);
2546 }
2547
2548 #[test]
2563 fn test_rc_records() {
2564 let (names, flags, seqs, quals, cigars) = gold();
2565 let mut bam = Reader::from_path(Path::new("test/test.bam")).expect("Error opening file.");
2566 let del_len = [1, 1, 1, 1, 1, 100000];
2567
2568 for (i, record) in bam.rc_records().enumerate() {
2569 let rec = record.unwrap();
2571 println!("{}", str::from_utf8(rec.qname()).ok().unwrap());
2572 assert_eq!(rec.qname(), names[i]);
2573 assert_eq!(rec.flags(), flags[i]);
2574 assert_eq!(rec.seq().as_bytes(), seqs[i]);
2575
2576 let cigar = rec.cigar();
2577 assert_eq!(*cigar, cigars[i]);
2578
2579 let end_pos = cigar.end_pos();
2580 assert_eq!(end_pos, rec.pos() + 100 + del_len[i]);
2581 assert_eq!(
2582 cigar
2583 .read_pos(end_pos as u32 - 10, false, false)
2584 .unwrap()
2585 .unwrap(),
2586 90
2587 );
2588 assert_eq!(
2589 cigar
2590 .read_pos(rec.pos() as u32 + 20, false, false)
2591 .unwrap()
2592 .unwrap(),
2593 20
2594 );
2595 assert_eq!(cigar.read_pos(4000000, false, false).unwrap(), None);
2596 let qual: Vec<u8> = quals[i].iter().map(|&q| q - 33).collect();
2598 assert_eq!(rec.qual(), &qual[..]);
2599 }
2600 }
2601
2602 #[test]
2603 fn test_aux_arrays() {
2604 let bam_header = Header::new();
2605 let mut test_record = Record::from_sam(
2606 &HeaderView::from_header(&bam_header),
2607 "ali1\t4\t*\t0\t0\t*\t*\t0\t0\tACGT\tFFFF".as_bytes(),
2608 )
2609 .unwrap();
2610
2611 let array_i8: Vec<i8> = vec![i8::MIN, -1, 0, 1, i8::MAX];
2612 let array_u8: Vec<u8> = vec![u8::MIN, 0, 1, u8::MAX];
2613 let array_i16: Vec<i16> = vec![i16::MIN, -1, 0, 1, i16::MAX];
2614 let array_u16: Vec<u16> = vec![u16::MIN, 0, 1, u16::MAX];
2615 let array_i32: Vec<i32> = vec![i32::MIN, -1, 0, 1, i32::MAX];
2616 let array_u32: Vec<u32> = vec![u32::MIN, 0, 1, u32::MAX];
2617 let array_f32: Vec<f32> = vec![f32::MIN, 0.0, -0.0, 0.1, 0.99, f32::MAX];
2618
2619 test_record
2620 .push_aux(b"XA", Aux::ArrayI8((&array_i8).into()))
2621 .unwrap();
2622 test_record
2623 .push_aux(b"XB", Aux::ArrayU8((&array_u8).into()))
2624 .unwrap();
2625 test_record
2626 .push_aux(b"XC", Aux::ArrayI16((&array_i16).into()))
2627 .unwrap();
2628 test_record
2629 .push_aux(b"XD", Aux::ArrayU16((&array_u16).into()))
2630 .unwrap();
2631 test_record
2632 .push_aux(b"XE", Aux::ArrayI32((&array_i32).into()))
2633 .unwrap();
2634 test_record
2635 .push_aux(b"XF", Aux::ArrayU32((&array_u32).into()))
2636 .unwrap();
2637 test_record
2638 .push_aux(b"XG", Aux::ArrayFloat((&array_f32).into()))
2639 .unwrap();
2640
2641 {
2642 let tag = b"XA";
2643 if let Ok(Aux::ArrayI8(array)) = test_record.aux(tag) {
2644 let aux_array_content = array.iter().collect::<Vec<_>>();
2646 assert_eq!(aux_array_content, array_i8);
2647
2648 {
2650 let mut copy_test_record = test_record.clone();
2651
2652 assert!(copy_test_record.push_aux(tag, Aux::I8(3)).is_err());
2654
2655 copy_test_record.remove_aux(tag).unwrap();
2657 assert!(copy_test_record.aux(tag).is_err());
2658
2659 let src_aux = test_record.aux(tag).unwrap();
2661 assert!(copy_test_record.push_aux(tag, src_aux).is_ok());
2662 if let Ok(Aux::ArrayI8(array)) = copy_test_record.aux(tag) {
2663 let aux_array_content_copied = array.iter().collect::<Vec<_>>();
2664 assert_eq!(aux_array_content_copied, array_i8);
2665 } else {
2666 panic!("Aux tag not found");
2667 }
2668 }
2669 } else {
2670 panic!("Aux tag not found");
2671 }
2672 }
2673
2674 {
2675 let tag = b"XB";
2676 if let Ok(Aux::ArrayU8(array)) = test_record.aux(tag) {
2677 let aux_array_content = array.iter().collect::<Vec<_>>();
2679 assert_eq!(aux_array_content, array_u8);
2680
2681 {
2683 let mut copy_test_record = test_record.clone();
2684
2685 assert!(copy_test_record.push_aux(tag, Aux::U8(3)).is_err());
2687
2688 copy_test_record.remove_aux(tag).unwrap();
2690 assert!(copy_test_record.aux(tag).is_err());
2691
2692 let src_aux = test_record.aux(tag).unwrap();
2694 assert!(copy_test_record.push_aux(tag, src_aux).is_ok());
2695 if let Ok(Aux::ArrayU8(array)) = copy_test_record.aux(tag) {
2696 let aux_array_content_copied = array.iter().collect::<Vec<_>>();
2697 assert_eq!(aux_array_content_copied, array_u8);
2698 } else {
2699 panic!("Aux tag not found");
2700 }
2701 }
2702 } else {
2703 panic!("Aux tag not found");
2704 }
2705 }
2706
2707 {
2708 let tag = b"XC";
2709 if let Ok(Aux::ArrayI16(array)) = test_record.aux(tag) {
2710 let aux_array_content = array.iter().collect::<Vec<_>>();
2712 assert_eq!(aux_array_content, array_i16);
2713
2714 {
2716 let mut copy_test_record = test_record.clone();
2717
2718 assert!(copy_test_record.push_aux(tag, Aux::I16(3)).is_err());
2720
2721 copy_test_record.remove_aux(tag).unwrap();
2723 assert!(copy_test_record.aux(tag).is_err());
2724
2725 let src_aux = test_record.aux(tag).unwrap();
2727 assert!(copy_test_record.push_aux(tag, src_aux).is_ok());
2728 if let Ok(Aux::ArrayI16(array)) = copy_test_record.aux(tag) {
2729 let aux_array_content_copied = array.iter().collect::<Vec<_>>();
2730 assert_eq!(aux_array_content_copied, array_i16);
2731 } else {
2732 panic!("Aux tag not found");
2733 }
2734 }
2735 } else {
2736 panic!("Aux tag not found");
2737 }
2738 }
2739
2740 {
2741 let tag = b"XD";
2742 if let Ok(Aux::ArrayU16(array)) = test_record.aux(tag) {
2743 let aux_array_content = array.iter().collect::<Vec<_>>();
2745 assert_eq!(aux_array_content, array_u16);
2746
2747 {
2749 let mut copy_test_record = test_record.clone();
2750
2751 assert!(copy_test_record.push_aux(tag, Aux::U16(3)).is_err());
2753
2754 copy_test_record.remove_aux(tag).unwrap();
2756 assert!(copy_test_record.aux(tag).is_err());
2757
2758 let src_aux = test_record.aux(tag).unwrap();
2760 assert!(copy_test_record.push_aux(tag, src_aux).is_ok());
2761 if let Ok(Aux::ArrayU16(array)) = copy_test_record.aux(tag) {
2762 let aux_array_content_copied = array.iter().collect::<Vec<_>>();
2763 assert_eq!(aux_array_content_copied, array_u16);
2764 } else {
2765 panic!("Aux tag not found");
2766 }
2767 }
2768 } else {
2769 panic!("Aux tag not found");
2770 }
2771 }
2772
2773 {
2774 let tag = b"XE";
2775 if let Ok(Aux::ArrayI32(array)) = test_record.aux(tag) {
2776 let aux_array_content = array.iter().collect::<Vec<_>>();
2778 assert_eq!(aux_array_content, array_i32);
2779
2780 {
2782 let mut copy_test_record = test_record.clone();
2783
2784 assert!(copy_test_record.push_aux(tag, Aux::I32(3)).is_err());
2786
2787 copy_test_record.remove_aux(tag).unwrap();
2789 assert!(copy_test_record.aux(tag).is_err());
2790
2791 let src_aux = test_record.aux(tag).unwrap();
2793 assert!(copy_test_record.push_aux(tag, src_aux).is_ok());
2794 if let Ok(Aux::ArrayI32(array)) = copy_test_record.aux(tag) {
2795 let aux_array_content_copied = array.iter().collect::<Vec<_>>();
2796 assert_eq!(aux_array_content_copied, array_i32);
2797 } else {
2798 panic!("Aux tag not found");
2799 }
2800 }
2801 } else {
2802 panic!("Aux tag not found");
2803 }
2804 }
2805
2806 {
2807 let tag = b"XF";
2808 if let Ok(Aux::ArrayU32(array)) = test_record.aux(tag) {
2809 let aux_array_content = array.iter().collect::<Vec<_>>();
2811 assert_eq!(aux_array_content, array_u32);
2812
2813 {
2815 let mut copy_test_record = test_record.clone();
2816
2817 assert!(copy_test_record.push_aux(tag, Aux::U32(3)).is_err());
2819
2820 copy_test_record.remove_aux(tag).unwrap();
2822 assert!(copy_test_record.aux(tag).is_err());
2823
2824 let src_aux = test_record.aux(tag).unwrap();
2826 assert!(copy_test_record.push_aux(tag, src_aux).is_ok());
2827 if let Ok(Aux::ArrayU32(array)) = copy_test_record.aux(tag) {
2828 let aux_array_content_copied = array.iter().collect::<Vec<_>>();
2829 assert_eq!(aux_array_content_copied, array_u32);
2830 } else {
2831 panic!("Aux tag not found");
2832 }
2833 }
2834 } else {
2835 panic!("Aux tag not found");
2836 }
2837 }
2838
2839 {
2840 let tag = b"XG";
2841 if let Ok(Aux::ArrayFloat(array)) = test_record.aux(tag) {
2842 let aux_array_content = array.iter().collect::<Vec<_>>();
2844 assert_eq!(aux_array_content, array_f32);
2845
2846 {
2848 let mut copy_test_record = test_record.clone();
2849
2850 assert!(copy_test_record.push_aux(tag, Aux::Float(3.0)).is_err());
2852
2853 copy_test_record.remove_aux(tag).unwrap();
2855 assert!(copy_test_record.aux(tag).is_err());
2856
2857 let src_aux = test_record.aux(tag).unwrap();
2859 assert!(copy_test_record.push_aux(tag, src_aux).is_ok());
2860 if let Ok(Aux::ArrayFloat(array)) = copy_test_record.aux(tag) {
2861 let aux_array_content_copied = array.iter().collect::<Vec<_>>();
2862 assert_eq!(aux_array_content_copied, array_f32);
2863 } else {
2864 panic!("Aux tag not found");
2865 }
2866 }
2867 } else {
2868 panic!("Aux tag not found");
2869 }
2870 }
2871
2872 for item in test_record.aux_iter() {
2874 match item.unwrap() {
2875 (b"XA", Aux::ArrayI8(array)) => {
2876 assert_eq!(&array.iter().collect::<Vec<_>>(), &array_i8);
2877 }
2878 (b"XB", Aux::ArrayU8(array)) => {
2879 assert_eq!(&array.iter().collect::<Vec<_>>(), &array_u8);
2880 }
2881 (b"XC", Aux::ArrayI16(array)) => {
2882 assert_eq!(&array.iter().collect::<Vec<_>>(), &array_i16);
2883 }
2884 (b"XD", Aux::ArrayU16(array)) => {
2885 assert_eq!(&array.iter().collect::<Vec<_>>(), &array_u16);
2886 }
2887 (b"XE", Aux::ArrayI32(array)) => {
2888 assert_eq!(&array.iter().collect::<Vec<_>>(), &array_i32);
2889 }
2890 (b"XF", Aux::ArrayU32(array)) => {
2891 assert_eq!(&array.iter().collect::<Vec<_>>(), &array_u32);
2892 }
2893 (b"XG", Aux::ArrayFloat(array)) => {
2894 assert_eq!(&array.iter().collect::<Vec<_>>(), &array_f32);
2895 }
2896 _ => {
2897 panic!();
2898 }
2899 }
2900 }
2901
2902 assert_eq!(
2904 test_record.aux(b"XA").unwrap(),
2905 Aux::ArrayI8((&array_i8).into())
2906 );
2907 assert_eq!(
2908 test_record.aux(b"XB").unwrap(),
2909 Aux::ArrayU8((&array_u8).into())
2910 );
2911 assert_eq!(
2912 test_record.aux(b"XC").unwrap(),
2913 Aux::ArrayI16((&array_i16).into())
2914 );
2915 assert_eq!(
2916 test_record.aux(b"XD").unwrap(),
2917 Aux::ArrayU16((&array_u16).into())
2918 );
2919 assert_eq!(
2920 test_record.aux(b"XE").unwrap(),
2921 Aux::ArrayI32((&array_i32).into())
2922 );
2923 assert_eq!(
2924 test_record.aux(b"XF").unwrap(),
2925 Aux::ArrayU32((&array_u32).into())
2926 );
2927 assert_eq!(
2928 test_record.aux(b"XG").unwrap(),
2929 Aux::ArrayFloat((&array_f32).into())
2930 );
2931 }
2932
2933 #[test]
2934 fn test_aux_scalars() {
2935 let bam_header = Header::new();
2936 let mut test_record = Record::from_sam(
2937 &HeaderView::from_header(&bam_header),
2938 "ali1\t4\t*\t0\t0\t*\t*\t0\t0\tACGT\tFFFF".as_bytes(),
2939 )
2940 .unwrap();
2941
2942 test_record.push_aux(b"XA", Aux::I8(i8::MIN)).unwrap();
2943 test_record.push_aux(b"XB", Aux::I8(i8::MAX)).unwrap();
2944 test_record.push_aux(b"XC", Aux::U8(u8::MIN)).unwrap();
2945 test_record.push_aux(b"XD", Aux::U8(u8::MAX)).unwrap();
2946 test_record.push_aux(b"XE", Aux::I16(i16::MIN)).unwrap();
2947 test_record.push_aux(b"XF", Aux::I16(i16::MAX)).unwrap();
2948 test_record.push_aux(b"XG", Aux::U16(u16::MIN)).unwrap();
2949 test_record.push_aux(b"XH", Aux::U16(u16::MAX)).unwrap();
2950 test_record.push_aux(b"XI", Aux::I32(i32::MIN)).unwrap();
2951 test_record.push_aux(b"XJ", Aux::I32(i32::MAX)).unwrap();
2952 test_record.push_aux(b"XK", Aux::U32(u32::MIN)).unwrap();
2953 test_record.push_aux(b"XL", Aux::U32(u32::MAX)).unwrap();
2954 test_record
2955 .push_aux(b"XM", Aux::Float(std::f32::consts::PI))
2956 .unwrap();
2957 test_record
2958 .push_aux(b"XN", Aux::Double(std::f64::consts::PI))
2959 .unwrap();
2960 test_record
2961 .push_aux(b"XO", Aux::String("Test str"))
2962 .unwrap();
2963 test_record.push_aux(b"XP", Aux::I8(0)).unwrap();
2964
2965 let collected_aux_fields = test_record.aux_iter().collect::<Result<Vec<_>>>().unwrap();
2966 assert_eq!(
2967 collected_aux_fields,
2968 vec![
2969 (&b"XA"[..], Aux::I8(i8::MIN)),
2970 (&b"XB"[..], Aux::I8(i8::MAX)),
2971 (&b"XC"[..], Aux::U8(u8::MIN)),
2972 (&b"XD"[..], Aux::U8(u8::MAX)),
2973 (&b"XE"[..], Aux::I16(i16::MIN)),
2974 (&b"XF"[..], Aux::I16(i16::MAX)),
2975 (&b"XG"[..], Aux::U16(u16::MIN)),
2976 (&b"XH"[..], Aux::U16(u16::MAX)),
2977 (&b"XI"[..], Aux::I32(i32::MIN)),
2978 (&b"XJ"[..], Aux::I32(i32::MAX)),
2979 (&b"XK"[..], Aux::U32(u32::MIN)),
2980 (&b"XL"[..], Aux::U32(u32::MAX)),
2981 (&b"XM"[..], Aux::Float(std::f32::consts::PI)),
2982 (&b"XN"[..], Aux::Double(std::f64::consts::PI)),
2983 (&b"XO"[..], Aux::String("Test str")),
2984 (&b"XP"[..], Aux::I8(0)),
2985 ]
2986 );
2987 }
2988
2989 #[test]
2990 fn test_aux_array_partial_eq() {
2991 use record::AuxArray;
2992
2993 let one_data: Vec<i8> = vec![0, 1, 2, 3, 4, 5, 6];
2995 let one_aux_array = AuxArray::from(&one_data);
2996
2997 let two_data: Vec<i8> = vec![0, 1, 2, 3, 4, 5];
2998 let two_aux_array = AuxArray::from(&two_data);
2999
3000 assert_ne!(&one_data, &two_data);
3001 assert_ne!(&one_aux_array, &two_aux_array);
3002
3003 let one_aux = Aux::ArrayI8(one_aux_array);
3004 let two_aux = Aux::ArrayI8(two_aux_array);
3005 assert_ne!(&one_aux, &two_aux);
3006
3007 let bam_header = Header::new();
3009 let mut test_record = Record::from_sam(
3010 &HeaderView::from_header(&bam_header),
3011 "ali1\t4\t*\t0\t0\t*\t*\t0\t0\tACGT\tFFFF".as_bytes(),
3012 )
3013 .unwrap();
3014
3015 test_record.push_aux(b"XA", one_aux).unwrap();
3016 test_record.push_aux(b"XB", two_aux).unwrap();
3017
3018 assert_eq!(
3020 test_record.aux(b"XA").unwrap(),
3021 test_record.aux(b"XA").unwrap()
3022 );
3023 assert_ne!(
3025 test_record.aux(b"XA").unwrap(),
3026 test_record.aux(b"XB").unwrap()
3027 );
3028
3029 assert_eq!(
3031 test_record.aux(b"XA").unwrap(),
3032 Aux::ArrayI8((&one_data).into())
3033 );
3034 assert_eq!(
3035 test_record.aux(b"XB").unwrap(),
3036 Aux::ArrayI8((&two_data).into())
3037 );
3038 assert_ne!(
3040 test_record.aux(b"XA").unwrap(),
3041 Aux::ArrayI8((&two_data).into())
3042 );
3043 assert_ne!(
3044 test_record.aux(b"XB").unwrap(),
3045 Aux::ArrayI8((&one_data).into())
3046 );
3047 }
3048
3049 #[test]
3051 fn test_bam_header_sync() {
3052 let reader = Reader::from_path("test/test_issue_156_no_text.bam").unwrap();
3053 let header_hashmap = Header::from_template(reader.header()).to_hashmap();
3054 let header_refseqs = header_hashmap.get("SQ").unwrap();
3055 assert_eq!(header_refseqs[0].get("SN").unwrap(), "ref_1",);
3056 assert_eq!(header_refseqs[0].get("LN").unwrap(), "10000000",);
3057 }
3058
3059 #[test]
3060 fn test_bam_new() {
3061 let tmp = tempfile::Builder::new()
3063 .prefix("rust-htslib")
3064 .tempdir()
3065 .expect("Cannot create temp dir");
3066 let bampath = tmp.path().join("test.bam");
3067
3068 {
3070 let mut header = Header::new();
3072
3073 header.push_record(
3075 HeaderRecord::new(b"HD")
3076 .push_tag(b"VN", "1.6")
3077 .push_tag(b"SO", "unsorted"),
3078 );
3079
3080 let mut writer = Writer::from_path(&bampath, &header, Format::Bam).unwrap();
3082
3083 let record = Record::new();
3085
3086 assert!(writer.write(&record).is_ok());
3088 }
3089
3090 {
3092 let mut reader = Reader::from_path(bampath).expect("Error opening file.");
3094
3095 let mut rec = Record::new();
3097 match reader.read(&mut rec) {
3098 Some(r) => r.expect("Failed to read record."),
3099 None => panic!("No record read."),
3100 };
3101
3102 assert!(rec.is_unmapped());
3104 assert_eq!(rec.tid(), -1);
3105 assert_eq!(rec.pos(), -1);
3106 assert_eq!(rec.mtid(), -1);
3107 assert_eq!(rec.mpos(), -1);
3108 }
3109 }
3110
3111 #[test]
3112 fn test_idxstats_bam() {
3113 let mut reader = IndexedReader::from_path("test/test.bam").unwrap();
3114 let expected = vec![
3115 (0, 15072423, 6, 0),
3116 (1, 15279345, 0, 0),
3117 (2, 13783700, 0, 0),
3118 (3, 17493793, 0, 0),
3119 (4, 20924149, 0, 0),
3120 (-1, 0, 0, 0),
3121 ];
3122 let actual = reader.index_stats().unwrap();
3123 assert_eq!(expected, actual);
3124 }
3125
3126 #[test]
3127 fn test_number_mapped_and_unmapped_bam() {
3128 let reader = IndexedReader::from_path("test/test.bam").unwrap();
3129 let expected = (6, 0);
3130 let actual = reader.index().number_mapped_unmapped(0);
3131 assert_eq!(expected, actual);
3132 }
3133
3134 #[test]
3135 fn test_number_unmapped_global_bam() {
3136 let reader = IndexedReader::from_path("test/test_unmapped.bam").unwrap();
3137 let expected = 8;
3138 let actual = reader.index().number_unmapped();
3139 assert_eq!(expected, actual);
3140 }
3141
3142 #[test]
3143 fn test_idxstats_cram() {
3144 let mut reader = IndexedReader::from_path("test/test_cram.cram").unwrap();
3145 reader.set_reference("test/test_cram.fa").unwrap();
3146 let expected = vec![
3147 (0, 120, 2, 0),
3148 (1, 120, 2, 0),
3149 (2, 120, 2, 0),
3150 (-1, 0, 0, 0),
3151 ];
3152 let actual = reader.index_stats().unwrap();
3153 assert_eq!(expected, actual);
3154 }
3155
3156 #[test]
3157 fn test_slow_idxstats_cram() {
3158 let mut reader = IndexedReader::from_path("test/test_cram.cram").unwrap();
3159 reader.set_reference("test/test_cram.fa").unwrap();
3160 let expected = vec![
3161 (0, 120, 2, 0),
3162 (1, 120, 2, 0),
3163 (2, 120, 2, 0),
3164 (-1, 0, 0, 0),
3165 ];
3166 let actual = reader.index_stats().unwrap();
3167 assert_eq!(expected, actual);
3168 }
3169
3170 }