1pub mod buffer;
9pub mod ext;
10pub mod header;
11pub mod index;
12pub mod pileup;
13pub mod record;
14#[cfg(feature = "serde_feature")]
15pub mod record_serde;
16
17use std::ffi;
18use std::os::raw::c_char;
19use std::path::Path;
20use std::rc::Rc;
21use std::slice;
22use std::str;
23use std::sync::Arc;
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: Arc<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: Arc::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(Arc::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, Clone)]
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: Arc<HeaderView>,
595 idx: Arc<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: Arc::new(HeaderView::new(header)),
641 idx: Arc::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: Arc::new(HeaderView::new(header)),
669 idx: Arc::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(Arc::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: Arc<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: Arc::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}
1368
1369unsafe impl Send for HeaderView {}
1370unsafe impl Sync for HeaderView {}
1371
1372impl HeaderView {
1373 pub fn from_header(header: &Header) -> Self {
1375 let mut header_string = header.to_bytes();
1376 if !header_string.is_empty() && header_string[header_string.len() - 1] != b'\n' {
1377 header_string.push(b'\n');
1378 }
1379 Self::from_bytes(&header_string)
1380 }
1381
1382 pub fn from_bytes(header_string: &[u8]) -> Self {
1384 let header_record = unsafe {
1385 let l_text = header_string.len();
1386 let text = ::libc::malloc(l_text + 1);
1387 ::libc::memset(text, 0, l_text + 1);
1388 ::libc::memcpy(
1389 text,
1390 header_string.as_ptr() as *const ::libc::c_void,
1391 header_string.len(),
1392 );
1393
1394 let rec = htslib::sam_hdr_parse(l_text + 1, text as *const c_char);
1395 (*rec).text = text as *mut c_char;
1396 (*rec).l_text = l_text;
1397 rec
1398 };
1399
1400 HeaderView::new(header_record)
1401 }
1402
1403 fn new(inner: *mut htslib::bam_hdr_t) -> Self {
1405 HeaderView { inner }
1406 }
1407
1408 #[inline]
1409 pub fn inner(&self) -> &htslib::bam_hdr_t {
1410 unsafe { self.inner.as_ref().unwrap() }
1411 }
1412
1413 #[inline]
1414 pub fn inner_ptr(&self) -> *const htslib::bam_hdr_t {
1416 self.inner
1417 }
1418
1419 #[inline]
1420 pub fn inner_mut(&mut self) -> &mut htslib::bam_hdr_t {
1421 unsafe { self.inner.as_mut().unwrap() }
1422 }
1423
1424 #[inline]
1425 pub fn inner_ptr_mut(&mut self) -> *mut htslib::bam_hdr_t {
1427 self.inner
1428 }
1429
1430 pub fn tid(&self, name: &[u8]) -> Option<u32> {
1431 let c_str = ffi::CString::new(name).expect("Expected valid name.");
1432 let tid = unsafe { htslib::sam_hdr_name2tid(self.inner, c_str.as_ptr()) };
1433 if tid < 0 { None } else { Some(tid as u32) }
1434 }
1435
1436 pub fn tid2name(&self, tid: u32) -> &[u8] {
1437 unsafe { ffi::CStr::from_ptr(htslib::sam_hdr_tid2name(self.inner, tid as i32)).to_bytes() }
1438 }
1439
1440 pub fn target_count(&self) -> u32 {
1441 self.inner().n_targets as u32
1442 }
1443
1444 pub fn target_names(&self) -> Vec<&[u8]> {
1445 let names = unsafe {
1446 slice::from_raw_parts(self.inner().target_name, self.target_count() as usize)
1447 };
1448 names
1449 .iter()
1450 .map(|name| unsafe { ffi::CStr::from_ptr(*name).to_bytes() })
1451 .collect()
1452 }
1453
1454 pub fn target_len(&self, tid: u32) -> Option<u64> {
1455 let inner = unsafe { *self.inner };
1456 if (tid as i32) < inner.n_targets {
1457 let l: &[u32] =
1458 unsafe { slice::from_raw_parts(inner.target_len, inner.n_targets as usize) };
1459 Some(l[tid as usize] as u64)
1460 } else {
1461 None
1462 }
1463 }
1464
1465 pub fn as_bytes(&self) -> &[u8] {
1467 unsafe {
1468 let rebuilt_hdr = htslib::sam_hdr_str(self.inner);
1469 if rebuilt_hdr.is_null() {
1470 return b"";
1471 }
1472 ffi::CStr::from_ptr(rebuilt_hdr).to_bytes()
1473 }
1474 }
1475}
1476
1477impl Clone for HeaderView {
1478 fn clone(&self) -> Self {
1479 HeaderView {
1480 inner: unsafe { htslib::sam_hdr_dup(self.inner) },
1481 }
1482 }
1483}
1484
1485impl Drop for HeaderView {
1486 fn drop(&mut self) {
1487 unsafe {
1488 htslib::sam_hdr_destroy(self.inner);
1489 }
1490 }
1491}
1492
1493#[cfg(test)]
1494mod tests {
1495 use super::header::HeaderRecord;
1496 use super::record::{Aux, Cigar, CigarString};
1497 use super::*;
1498 use std::collections::HashMap;
1499 use std::fs;
1500 use std::path::Path;
1501 use std::str;
1502
1503 type GoldType = (
1504 [&'static [u8]; 6],
1505 [u16; 6],
1506 [&'static [u8]; 6],
1507 [&'static [u8]; 6],
1508 [CigarString; 6],
1509 );
1510 fn gold() -> GoldType {
1511 let names = [
1512 &b"I"[..],
1513 &b"II.14978392"[..],
1514 &b"III"[..],
1515 &b"IV"[..],
1516 &b"V"[..],
1517 &b"VI"[..],
1518 ];
1519 let flags = [16u16, 16u16, 16u16, 16u16, 16u16, 2048u16];
1520 let seqs = [
1521 &b"CCTAGCCCTAACCCTAACCCTAACCCTAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCC\
1522TAAGCCTAAGCCTAAGCCTAA"[..],
1523 &b"CCTAGCCCTAACCCTAACCCTAACCCTAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCC\
1524TAAGCCTAAGCCTAAGCCTAA"[..],
1525 &b"CCTAGCCCTAACCCTAACCCTAACCCTAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCC\
1526TAAGCCTAAGCCTAAGCCTAA"[..],
1527 &b"CCTAGCCCTAACCCTAACCCTAACCCTAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCC\
1528TAAGCCTAAGCCTAAGCCTAA"[..],
1529 &b"CCTAGCCCTAACCCTAACCCTAACCCTAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCC\
1530TAAGCCTAAGCCTAAGCCTAA"[..],
1531 &b"ACTAAGCCTAAGCCTAAGCCTAAGCCAATTATCGATTTCTGAAAAAATTATCGAATTTTCTAGAAATTTTGCAAATTTT\
1532TTCATAAAATTATCGATTTTA"[..],
1533 ];
1534 let quals = [
1535 &b"#############################@B?8B?BA@@DDBCDDCBC@CDCDCCCCCCCCCCCCCCCCCCCCCCCCCCCC\
1536CCCCCCCCCCCCCCCCCCC"[..],
1537 &b"#############################@B?8B?BA@@DDBCDDCBC@CDCDCCCCCCCCCCCCCCCCCCCCCCCCCCCC\
1538CCCCCCCCCCCCCCCCCCC"[..],
1539 &b"#############################@B?8B?BA@@DDBCDDCBC@CDCDCCCCCCCCCCCCCCCCCCCCCCCCCCCC\
1540CCCCCCCCCCCCCCCCCCC"[..],
1541 &b"#############################@B?8B?BA@@DDBCDDCBC@CDCDCCCCCCCCCCCCCCCCCCCCCCCCCCCC\
1542CCCCCCCCCCCCCCCCCCC"[..],
1543 &b"#############################@B?8B?BA@@DDBCDDCBC@CDCDCCCCCCCCCCCCCCCCCCCCCCCCCCCC\
1544CCCCCCCCCCCCCCCCCCC"[..],
1545 &b"#############################@B?8B?BA@@DDBCDDCBC@CDCDCCCCCCCCCCCCCCCCCCCCCCCCCCCC\
1546CCCCCCCCCCCCCCCCCCC"[..],
1547 ];
1548 let cigars = [
1549 CigarString(vec![Cigar::Match(27), Cigar::Del(1), Cigar::Match(73)]),
1550 CigarString(vec![Cigar::Match(27), Cigar::Del(1), Cigar::Match(73)]),
1551 CigarString(vec![Cigar::Match(27), Cigar::Del(1), Cigar::Match(73)]),
1552 CigarString(vec![Cigar::Match(27), Cigar::Del(1), Cigar::Match(73)]),
1553 CigarString(vec![Cigar::Match(27), Cigar::Del(1), Cigar::Match(73)]),
1554 CigarString(vec![Cigar::Match(27), Cigar::Del(100000), Cigar::Match(73)]),
1555 ];
1556 (names, flags, seqs, quals, cigars)
1557 }
1558
1559 fn compare_inner_bam_cram_records(cram_records: &[Record], bam_records: &[Record]) {
1560 for (c1, b1) in cram_records.iter().zip(bam_records.iter()) {
1562 assert_eq!(c1.cigar(), b1.cigar());
1565 assert_eq!(c1.inner().core.pos, b1.inner().core.pos);
1566 assert_eq!(c1.inner().core.mpos, b1.inner().core.mpos);
1567 assert_eq!(c1.inner().core.mtid, b1.inner().core.mtid);
1568 assert_eq!(c1.inner().core.tid, b1.inner().core.tid);
1569 assert_eq!(c1.inner().core.bin, b1.inner().core.bin);
1570 assert_eq!(c1.inner().core.qual, b1.inner().core.qual);
1571 assert_eq!(c1.inner().core.l_extranul, b1.inner().core.l_extranul);
1572 assert_eq!(c1.inner().core.flag, b1.inner().core.flag);
1573 assert_eq!(c1.inner().core.l_qname, b1.inner().core.l_qname);
1574 assert_eq!(c1.inner().core.n_cigar, b1.inner().core.n_cigar);
1575 assert_eq!(c1.inner().core.l_qseq, b1.inner().core.l_qseq);
1576 assert_eq!(c1.inner().core.isize_, b1.inner().core.isize_);
1577 }
1579 }
1580
1581 #[test]
1582 fn test_read() {
1583 let (names, flags, seqs, quals, cigars) = gold();
1584 let mut bam = Reader::from_path(Path::new("test/test.bam")).expect("Error opening file.");
1585 let del_len = [1, 1, 1, 1, 1, 100000];
1586
1587 for (i, record) in bam.records().enumerate() {
1588 let rec = record.expect("Expected valid record");
1589 assert_eq!(rec.qname(), names[i]);
1590 assert_eq!(rec.flags(), flags[i]);
1591 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1592
1593 let cigar = rec.cigar();
1594 assert_eq!(*cigar, cigars[i]);
1595
1596 let end_pos = cigar.end_pos();
1597 assert_eq!(end_pos, rec.pos() + 100 + del_len[i]);
1598 assert_eq!(
1599 cigar
1600 .read_pos(end_pos as u32 - 10, false, false)
1601 .unwrap()
1602 .unwrap(),
1603 90
1604 );
1605 assert_eq!(
1606 cigar
1607 .read_pos(rec.pos() as u32 + 20, false, false)
1608 .unwrap()
1609 .unwrap(),
1610 20
1611 );
1612 assert_eq!(cigar.read_pos(4000000, false, false).unwrap(), None);
1613 let qual: Vec<u8> = quals[i].iter().map(|&q| q - 33).collect();
1615 assert_eq!(rec.qual(), &qual[..]);
1616 }
1617 }
1618
1619 #[test]
1620 fn test_seek() {
1621 let mut bam = Reader::from_path(Path::new("test/test.bam")).expect("Error opening file.");
1622
1623 let mut names_by_voffset = HashMap::new();
1624
1625 let mut offset = bam.tell();
1626 let mut rec = Record::new();
1627 while let Some(r) = bam.read(&mut rec) {
1628 r.expect("error reading bam");
1629 let qname = str::from_utf8(rec.qname()).unwrap().to_string();
1630 println!("{} {}", offset, qname);
1631 names_by_voffset.insert(offset, qname);
1632 offset = bam.tell();
1633 }
1634
1635 for (offset, qname) in names_by_voffset.iter() {
1636 println!("{} {}", offset, qname);
1637 bam.seek(*offset).unwrap();
1638 if let Some(r) = bam.read(&mut rec) {
1639 r.unwrap();
1640 };
1641 let rec_qname = str::from_utf8(rec.qname()).unwrap().to_string();
1642 assert_eq!(qname, &rec_qname);
1643 }
1644 }
1645
1646 #[test]
1647 fn test_read_sam_header() {
1648 let bam = Reader::from_path("test/test.bam").expect("Error opening file.");
1649
1650 let true_header = "@SQ\tSN:CHROMOSOME_I\tLN:15072423\n@SQ\tSN:CHROMOSOME_II\tLN:15279345\
1651 \n@SQ\tSN:CHROMOSOME_III\tLN:13783700\n@SQ\tSN:CHROMOSOME_IV\tLN:17493793\n@SQ\t\
1652 SN:CHROMOSOME_V\tLN:20924149\n"
1653 .to_string();
1654 let header_text = String::from_utf8(bam.header.as_bytes().to_owned()).unwrap();
1655 assert_eq!(header_text, true_header);
1656 }
1657
1658 #[test]
1659 fn test_read_against_sam() {
1660 let mut bam = Reader::from_path("./test/bam2sam_out.sam").unwrap();
1661 for read in bam.records() {
1662 let _read = read.unwrap();
1663 }
1664 }
1665
1666 fn _test_read_indexed_common(mut bam: IndexedReader) {
1667 let (names, flags, seqs, quals, cigars) = gold();
1668 let sq_1 = b"CHROMOSOME_I";
1669 let sq_2 = b"CHROMOSOME_II";
1670 let tid_1 = bam.header.tid(sq_1).expect("Expected tid.");
1671 let tid_2 = bam.header.tid(sq_2).expect("Expected tid.");
1672 assert!(bam.header.target_len(tid_1).expect("Expected target len.") == 15072423);
1673
1674 bam.fetch((tid_1, 0, 2))
1676 .expect("Expected successful fetch.");
1677 assert!(bam.records().count() == 6);
1678
1679 bam.fetch((tid_1, 0, 2))
1681 .expect("Expected successful fetch.");
1682 for (i, record) in bam.records().enumerate() {
1683 let rec = record.expect("Expected valid record");
1684
1685 println!("{}", str::from_utf8(rec.qname()).unwrap());
1686 assert_eq!(rec.qname(), names[i]);
1687 assert_eq!(rec.flags(), flags[i]);
1688 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1689 assert_eq!(*rec.cigar(), cigars[i]);
1690 let qual: Vec<u8> = quals[i].iter().map(|&q| q - 33).collect();
1692 assert_eq!(rec.qual(), &qual[..]);
1693 assert_eq!(rec.aux(b"X"), Err(Error::BamAuxStringError));
1694 assert_eq!(rec.aux(b"NotAvailableAux"), Err(Error::BamAuxTagNotFound));
1695 }
1696
1697 bam.fetch((tid_2, 1, 1))
1699 .expect("Expected successful fetch.");
1700 assert!(bam.records().count() == 0);
1701
1702 bam.fetch(format!("{}:{}-{}", str::from_utf8(sq_1).unwrap(), 0, 2).as_bytes())
1707 .expect("Expected successful fetch.");
1708 assert!(bam.records().count() == 6);
1709 bam.fetch((str::from_utf8(sq_1).unwrap(), 0_u32, 2_u64))
1711 .expect("Expected successful fetch.");
1712 assert!(bam.records().count() == 6);
1713 bam.fetch((&sq_1[..], 0, 2))
1715 .expect("Expected successful fetch.");
1716 assert!(bam.records().count() == 6);
1717 bam.fetch((sq_1, 0, 2)).expect("Expected successful fetch.");
1719 assert!(bam.records().count() == 6);
1720
1721 bam.fetch((0i32, 0u32, 2i64))
1723 .expect("Expected successful fetch.");
1724 assert!(bam.records().count() == 6);
1725 bam.fetch((0u32, 0u32, 2i64))
1727 .expect("Expected successful fetch.");
1728 assert!(bam.records().count() == 6);
1729
1730 bam.fetch(format!("{}:{}-{}", str::from_utf8(sq_1).unwrap(), 0, 2).as_bytes())
1732 .expect("Expected successful fetch.");
1733 for (i, record) in bam.records().enumerate() {
1734 let rec = record.expect("Expected valid record");
1735
1736 println!("{}", str::from_utf8(rec.qname()).unwrap());
1737 assert_eq!(rec.qname(), names[i]);
1738 assert_eq!(rec.flags(), flags[i]);
1739 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1740 assert_eq!(*rec.cigar(), cigars[i]);
1741 let qual: Vec<u8> = quals[i].iter().map(|&q| q - 33).collect();
1743 assert_eq!(rec.qual(), &qual[..]);
1744 assert_eq!(rec.aux(b"NotAvailableAux"), Err(Error::BamAuxTagNotFound));
1745 }
1746
1747 bam.fetch(format!("{}:{}-{}", str::from_utf8(sq_2).unwrap(), 1, 1).as_bytes())
1749 .expect("Expected successful fetch.");
1750 assert!(bam.records().count() == 0);
1751
1752 bam.fetch(0).expect("Expected successful fetch.");
1754 assert!(bam.records().count() == 6);
1755 bam.fetch(0u32).expect("Expected successful fetch.");
1757 assert!(bam.records().count() == 6);
1758
1759 bam.fetch(sq_1).expect("Expected successful fetch.");
1761 assert!(bam.records().count() == 6);
1762 bam.fetch(str::from_utf8(sq_1).unwrap())
1764 .expect("Expected successful fetch.");
1765 assert!(bam.records().count() == 6);
1766
1767 bam.fetch(FetchDefinition::All)
1769 .expect("Expected successful fetch.");
1770 assert!(bam.records().count() == 6);
1771
1772 bam.fetch(".").expect("Expected successful fetch.");
1774 assert!(bam.records().count() == 6);
1775
1776 bam.fetch(FetchDefinition::Unmapped)
1778 .expect("Expected successful fetch.");
1779 assert_eq!(bam.records().count(), 1); bam.fetch("*").expect("Expected successful fetch.");
1782 assert_eq!(bam.records().count(), 1); }
1784
1785 #[test]
1786 fn test_read_indexed() {
1787 let bam = IndexedReader::from_path("test/test.bam").expect("Expected valid index.");
1788 _test_read_indexed_common(bam);
1789 }
1790
1791 #[test]
1792 fn test_read_indexed_different_index_name() {
1793 let bam = IndexedReader::from_path_and_index(
1794 &"test/test_different_index_name.bam",
1795 &"test/test.bam.bai",
1796 )
1797 .expect("Expected valid index.");
1798 _test_read_indexed_common(bam);
1799 }
1800
1801 #[test]
1802 fn test_set_record() {
1803 let (names, _, seqs, quals, cigars) = gold();
1804
1805 let mut rec = record::Record::new();
1806 rec.set_reverse();
1807 rec.set(names[0], Some(&cigars[0]), seqs[0], quals[0]);
1808 rec.push_aux(b"NM", Aux::I32(15)).unwrap();
1811
1812 assert_eq!(rec.qname(), names[0]);
1813 assert_eq!(*rec.cigar(), cigars[0]);
1814 assert_eq!(rec.seq().as_bytes(), seqs[0]);
1815 assert_eq!(rec.qual(), quals[0]);
1816 assert!(rec.is_reverse());
1817 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1818 }
1819
1820 #[test]
1821 fn test_set_repeated() {
1822 let mut rec = Record::new();
1823 rec.set(
1824 b"123",
1825 Some(&CigarString(vec![Cigar::Match(3)])),
1826 b"AAA",
1827 b"III",
1828 );
1829 rec.push_aux(b"AS", Aux::I32(12345)).unwrap();
1830 assert_eq!(rec.qname(), b"123");
1831 assert_eq!(rec.seq().as_bytes(), b"AAA");
1832 assert_eq!(rec.qual(), b"III");
1833 assert_eq!(rec.aux(b"AS").unwrap(), Aux::I32(12345));
1834
1835 rec.set(
1836 b"1234",
1837 Some(&CigarString(vec![Cigar::SoftClip(1), Cigar::Match(3)])),
1838 b"AAAA",
1839 b"IIII",
1840 );
1841 assert_eq!(rec.qname(), b"1234");
1842 assert_eq!(rec.seq().as_bytes(), b"AAAA");
1843 assert_eq!(rec.qual(), b"IIII");
1844 assert_eq!(rec.aux(b"AS").unwrap(), Aux::I32(12345));
1845
1846 rec.set(
1847 b"12",
1848 Some(&CigarString(vec![Cigar::Match(2)])),
1849 b"AA",
1850 b"II",
1851 );
1852 assert_eq!(rec.qname(), b"12");
1853 assert_eq!(rec.seq().as_bytes(), b"AA");
1854 assert_eq!(rec.qual(), b"II");
1855 assert_eq!(rec.aux(b"AS").unwrap(), Aux::I32(12345));
1856 }
1857
1858 #[test]
1859 fn test_set_qname() {
1860 let (names, _, seqs, quals, cigars) = gold();
1861
1862 assert!(names[0] != names[1]);
1863
1864 for i in 0..names.len() {
1865 let mut rec = record::Record::new();
1866 rec.set(names[i], Some(&cigars[i]), seqs[i], quals[i]);
1867 rec.push_aux(b"NM", Aux::I32(15)).unwrap();
1868
1869 assert_eq!(rec.qname(), names[i]);
1870 assert_eq!(*rec.cigar(), cigars[i]);
1871 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1872 assert_eq!(rec.qual(), quals[i]);
1873 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1874
1875 assert!(rec.qname()[0] != b'X');
1877 rec.set_qname(b"X");
1878 assert_eq!(rec.qname(), b"X");
1879
1880 let mut longer_name = names[i].to_owned().clone();
1882 let extension = b"BuffaloBUffaloBUFFaloBUFFAloBUFFALoBUFFALO";
1883 longer_name.extend(extension.iter());
1884 rec.set_qname(&longer_name);
1885
1886 assert_eq!(rec.qname(), longer_name.as_slice());
1887 assert_eq!(*rec.cigar(), cigars[i]);
1888 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1889 assert_eq!(rec.qual(), quals[i]);
1890 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1891
1892 let shorter_name = b"42";
1894 rec.set_qname(shorter_name);
1895
1896 assert_eq!(rec.qname(), shorter_name);
1897 assert_eq!(*rec.cigar(), cigars[i]);
1898 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1899 assert_eq!(rec.qual(), quals[i]);
1900 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1901
1902 rec.set_qname(b"");
1904
1905 assert_eq!(rec.qname(), b"");
1906 assert_eq!(*rec.cigar(), cigars[i]);
1907 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1908 assert_eq!(rec.qual(), quals[i]);
1909 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1910 }
1911 }
1912
1913 #[test]
1914 fn test_set_qname2() {
1915 let mut _header = Header::new();
1916 _header.push_record(
1917 HeaderRecord::new(b"SQ")
1918 .push_tag(b"SN", "1")
1919 .push_tag(b"LN", 10000000),
1920 );
1921 let header = HeaderView::from_header(&_header);
1922
1923 let line =
1924 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";
1925
1926 let mut rec = Record::from_sam(&header, line).unwrap();
1927 assert_eq!(rec.qname(), b"blah1");
1928 rec.set_qname(b"r0");
1929 assert_eq!(rec.qname(), b"r0");
1930 }
1931
1932 #[test]
1933 fn test_set_cigar() {
1934 let (names, _, seqs, quals, cigars) = gold();
1935
1936 assert!(names[0] != names[1]);
1937
1938 for i in 0..names.len() {
1939 let mut rec = record::Record::new();
1940 rec.set(names[i], Some(&cigars[i]), seqs[i], quals[i]);
1941 rec.push_aux(b"NM", Aux::I32(15)).unwrap();
1942
1943 assert_eq!(rec.qname(), names[i]);
1944 assert_eq!(*rec.cigar(), cigars[i]);
1945 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1946 assert_eq!(rec.qual(), quals[i]);
1947 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1948
1949 let new_cigar = CigarString(vec![Cigar::Match(rec.seq_len() as u32)]);
1951 assert_ne!(*rec.cigar(), new_cigar);
1952 rec.set_cigar(Some(&new_cigar));
1953 assert_eq!(*rec.cigar(), new_cigar);
1954
1955 assert_eq!(rec.qname(), names[i]);
1956 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1957 assert_eq!(rec.qual(), quals[i]);
1958 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1959
1960 let new_cigar = (0..rec.seq_len())
1962 .map(|i| {
1963 if i % 2 == 0 {
1964 Cigar::Match(1)
1965 } else {
1966 Cigar::Ins(1)
1967 }
1968 })
1969 .collect::<Vec<_>>();
1970 let new_cigar = CigarString(new_cigar);
1971 assert_ne!(*rec.cigar(), new_cigar);
1972 rec.set_cigar(Some(&new_cigar));
1973 assert_eq!(*rec.cigar(), new_cigar);
1974
1975 assert_eq!(rec.qname(), names[i]);
1976 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1977 assert_eq!(rec.qual(), quals[i]);
1978 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1979
1980 let new_cigar = CigarString(Vec::new());
1982 assert_ne!(*rec.cigar(), new_cigar);
1983 rec.set_cigar(None);
1984 assert_eq!(*rec.cigar(), new_cigar);
1985
1986 assert_eq!(rec.qname(), names[i]);
1987 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1988 assert_eq!(rec.qual(), quals[i]);
1989 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1990 }
1991 }
1992
1993 #[test]
1994 fn test_remove_aux() {
1995 let mut bam = Reader::from_path(Path::new("test/test.bam")).expect("Error opening file.");
1996
1997 for record in bam.records() {
1998 let mut rec = record.expect("Expected valid record");
1999
2000 if rec.aux(b"XS").is_ok() {
2001 rec.remove_aux(b"XS").unwrap();
2002 }
2003
2004 if rec.aux(b"YT").is_ok() {
2005 rec.remove_aux(b"YT").unwrap();
2006 }
2007
2008 assert_eq!(rec.remove_aux(b"X"), Err(Error::BamAuxStringError));
2009 assert_eq!(rec.remove_aux(b"ab"), Err(Error::BamAuxTagNotFound));
2010
2011 assert_eq!(rec.aux(b"XS"), Err(Error::BamAuxTagNotFound));
2012 assert_eq!(rec.aux(b"YT"), Err(Error::BamAuxTagNotFound));
2013 }
2014 }
2015
2016 #[test]
2017 fn test_write() {
2018 let (names, _, seqs, quals, cigars) = gold();
2019
2020 let tmp = tempfile::Builder::new()
2021 .prefix("rust-htslib")
2022 .tempdir()
2023 .expect("Cannot create temp dir");
2024 let bampath = tmp.path().join("test.bam");
2025 println!("{:?}", bampath);
2026 {
2027 let mut bam = Writer::from_path(
2028 &bampath,
2029 Header::new().push_record(
2030 HeaderRecord::new(b"SQ")
2031 .push_tag(b"SN", "chr1")
2032 .push_tag(b"LN", 15072423),
2033 ),
2034 Format::Bam,
2035 )
2036 .expect("Error opening file.");
2037
2038 for i in 0..names.len() {
2039 let mut rec = record::Record::new();
2040 rec.set(names[i], Some(&cigars[i]), seqs[i], quals[i]);
2041 rec.push_aux(b"NM", Aux::I32(15)).unwrap();
2042
2043 bam.write(&rec).expect("Failed to write record.");
2044 }
2045 }
2046
2047 {
2048 let mut bam = Reader::from_path(bampath).expect("Error opening file.");
2049
2050 for i in 0..names.len() {
2051 let mut rec = record::Record::new();
2052 if let Some(r) = bam.read(&mut rec) {
2053 r.expect("Failed to read record.");
2054 };
2055
2056 assert_eq!(rec.qname(), names[i]);
2057 assert_eq!(*rec.cigar(), cigars[i]);
2058 assert_eq!(rec.seq().as_bytes(), seqs[i]);
2059 assert_eq!(rec.qual(), quals[i]);
2060 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
2061 }
2062 }
2063
2064 tmp.close().expect("Failed to delete temp dir");
2065 }
2066
2067 #[test]
2068 fn test_write_threaded() {
2069 let (names, _, seqs, quals, cigars) = gold();
2070
2071 let tmp = tempfile::Builder::new()
2072 .prefix("rust-htslib")
2073 .tempdir()
2074 .expect("Cannot create temp dir");
2075 let bampath = tmp.path().join("test.bam");
2076 println!("{:?}", bampath);
2077 {
2078 let mut bam = Writer::from_path(
2079 &bampath,
2080 Header::new().push_record(
2081 HeaderRecord::new(b"SQ")
2082 .push_tag(b"SN", "chr1")
2083 .push_tag(b"LN", 15072423),
2084 ),
2085 Format::Bam,
2086 )
2087 .expect("Error opening file.");
2088 bam.set_threads(4).unwrap();
2089
2090 for i in 0..10000 {
2091 let mut rec = record::Record::new();
2092 let idx = i % names.len();
2093 rec.set(names[idx], Some(&cigars[idx]), seqs[idx], quals[idx]);
2094 rec.push_aux(b"NM", Aux::I32(15)).unwrap();
2095 rec.set_pos(i as i64);
2096
2097 bam.write(&rec).expect("Failed to write record.");
2098 }
2099 }
2100
2101 {
2102 let mut bam = Reader::from_path(bampath).expect("Error opening file.");
2103
2104 for (i, _rec) in bam.records().enumerate() {
2105 let idx = i % names.len();
2106
2107 let rec = _rec.expect("Failed to read record.");
2108
2109 assert_eq!(rec.pos(), i as i64);
2110 assert_eq!(rec.qname(), names[idx]);
2111 assert_eq!(*rec.cigar(), cigars[idx]);
2112 assert_eq!(rec.seq().as_bytes(), seqs[idx]);
2113 assert_eq!(rec.qual(), quals[idx]);
2114 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
2115 }
2116 }
2117
2118 tmp.close().expect("Failed to delete temp dir");
2119 }
2120
2121 #[test]
2122 fn test_write_shared_tpool() {
2123 let (names, _, seqs, quals, cigars) = gold();
2124
2125 let tmp = tempfile::Builder::new()
2126 .prefix("rust-htslib")
2127 .tempdir()
2128 .expect("Cannot create temp dir");
2129 let bampath1 = tmp.path().join("test1.bam");
2130 let bampath2 = tmp.path().join("test2.bam");
2131
2132 {
2133 let (mut bam1, mut bam2) = {
2134 let pool = crate::tpool::ThreadPool::new(4).unwrap();
2135
2136 let mut bam1 = Writer::from_path(
2137 &bampath1,
2138 Header::new().push_record(
2139 HeaderRecord::new(b"SQ")
2140 .push_tag(b"SN", "chr1")
2141 .push_tag(b"LN", 15072423),
2142 ),
2143 Format::Bam,
2144 )
2145 .expect("Error opening file.");
2146
2147 let mut bam2 = Writer::from_path(
2148 &bampath2,
2149 Header::new().push_record(
2150 HeaderRecord::new(b"SQ")
2151 .push_tag(b"SN", "chr1")
2152 .push_tag(b"LN", 15072423),
2153 ),
2154 Format::Bam,
2155 )
2156 .expect("Error opening file.");
2157
2158 bam1.set_thread_pool(&pool).unwrap();
2159 bam2.set_thread_pool(&pool).unwrap();
2160 (bam1, bam2)
2161 };
2162
2163 for i in 0..10000 {
2164 let mut rec = record::Record::new();
2165 let idx = i % names.len();
2166 rec.set(names[idx], Some(&cigars[idx]), seqs[idx], quals[idx]);
2167 rec.push_aux(b"NM", Aux::I32(15)).unwrap();
2168 rec.set_pos(i as i64);
2169
2170 bam1.write(&rec).expect("Failed to write record.");
2171 bam2.write(&rec).expect("Failed to write record.");
2172 }
2173 }
2174
2175 {
2176 let pool = crate::tpool::ThreadPool::new(2).unwrap();
2177
2178 for p in [bampath1, bampath2] {
2179 let mut bam = Reader::from_path(p).expect("Error opening file.");
2180 bam.set_thread_pool(&pool).unwrap();
2181
2182 for (i, _rec) in bam.iter_chunk(None, None).enumerate() {
2183 let idx = i % names.len();
2184
2185 let rec = _rec.expect("Failed to read record.");
2186
2187 assert_eq!(rec.pos(), i as i64);
2188 assert_eq!(rec.qname(), names[idx]);
2189 assert_eq!(*rec.cigar(), cigars[idx]);
2190 assert_eq!(rec.seq().as_bytes(), seqs[idx]);
2191 assert_eq!(rec.qual(), quals[idx]);
2192 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
2193 }
2194 }
2195 }
2196
2197 tmp.close().expect("Failed to delete temp dir");
2198 }
2199
2200 #[test]
2201 fn test_copy_template() {
2202 let tmp = tempfile::Builder::new()
2206 .prefix("rust-htslib")
2207 .tempdir()
2208 .expect("Cannot create temp dir");
2209 let bampath = tmp.path().join("test.bam");
2210 println!("{:?}", bampath);
2211
2212 let mut input_bam = Reader::from_path("test/test.bam").expect("Error opening file.");
2213
2214 {
2215 let mut bam = Writer::from_path(
2216 &bampath,
2217 &Header::from_template(input_bam.header()),
2218 Format::Bam,
2219 )
2220 .expect("Error opening file.");
2221
2222 for rec in input_bam.records() {
2223 bam.write(&rec.unwrap()).expect("Failed to write record.");
2224 }
2225 }
2226
2227 {
2228 let copy_bam = Reader::from_path(bampath).expect("Error opening file.");
2229
2230 assert_eq!(input_bam.header().as_bytes(), copy_bam.header().as_bytes());
2232 }
2233
2234 tmp.close().expect("Failed to delete temp dir");
2235 }
2236
2237 #[test]
2238 fn test_pileup() {
2239 let (_, _, seqs, quals, _) = gold();
2240
2241 let mut bam = Reader::from_path("test/test.bam").expect("Error opening file.");
2242 let pileups = bam.pileup();
2243 for pileup in pileups.take(26) {
2244 let _pileup = pileup.expect("Expected successful pileup.");
2245 let pos = _pileup.pos() as usize;
2246 assert_eq!(_pileup.depth(), 6);
2247 assert!(_pileup.tid() == 0);
2248 for (i, a) in _pileup.alignments().enumerate() {
2249 assert_eq!(a.indel(), pileup::Indel::None);
2250 let qpos = a.qpos().unwrap();
2251 assert_eq!(qpos, pos - 1);
2252 assert_eq!(a.record().seq()[qpos], seqs[i][qpos]);
2253 assert_eq!(a.record().qual()[qpos], quals[i][qpos] - 33);
2254 }
2255 }
2256 }
2257
2258 #[test]
2259 fn test_idx_pileup() {
2260 let mut bam = IndexedReader::from_path("test/test.bam").expect("Error opening file.");
2261 for pileup in bam.pileup() {
2263 pileup.unwrap();
2264 }
2265 let tid = bam.header().tid(b"CHROMOSOME_I").unwrap();
2267 bam.fetch((tid, 0, 5)).unwrap();
2268 for p in bam.pileup() {
2269 println!("{}", p.unwrap().pos())
2270 }
2271 }
2272
2273 #[test]
2274 fn parse_from_sam() {
2275 use std::fs::File;
2276 use std::io::Read;
2277
2278 let bamfile = "./test/bam2sam_test.bam";
2279 let samfile = "./test/bam2sam_expected.sam";
2280
2281 let mut rdr = Reader::from_path(bamfile).unwrap();
2283 let bam_recs: Vec<Record> = rdr.records().map(|v| v.unwrap()).collect();
2284
2285 let mut sam = Vec::new();
2286 assert!(File::open(samfile).unwrap().read_to_end(&mut sam).is_ok());
2287
2288 let sam_recs: Vec<Record> = sam
2289 .split(|x| *x == b'\n')
2290 .filter(|x| !x.is_empty() && x[0] != b'@')
2291 .map(|line| Record::from_sam(rdr.header(), line).unwrap())
2292 .collect();
2293
2294 for (b1, s1) in bam_recs.iter().zip(sam_recs.iter()) {
2295 assert!(b1 == s1);
2296 }
2297 }
2298
2299 #[test]
2300 fn test_cigar_modes() {
2301 let (_, _, _, _, cigars) = gold();
2304 let mut bam = Reader::from_path(Path::new("test/test.bam")).expect("Error opening file.");
2305
2306 for (i, record) in bam.records().enumerate() {
2307 let rec = record.expect("Expected valid record");
2308
2309 let cigar = rec.cigar();
2310 assert_eq!(*cigar, cigars[i]);
2311 }
2312
2313 for (i, record) in bam.records().enumerate() {
2314 let mut rec = record.expect("Expected valid record");
2315 rec.cache_cigar();
2316
2317 let cigar = rec.cigar_cached().unwrap();
2318 assert_eq!(**cigar, cigars[i]);
2319
2320 let cigar = rec.cigar();
2321 assert_eq!(*cigar, cigars[i]);
2322 }
2323 }
2324
2325 #[test]
2326 fn test_read_cram() {
2327 let cram_path = "./test/test_cram.cram";
2328 let bam_path = "./test/test_cram.bam";
2329 let ref_path = "./test/test_cram.fa";
2330
2331 let mut cram_reader = Reader::from_path(cram_path).unwrap();
2333 cram_reader.set_reference(ref_path).unwrap();
2334 let cram_records: Vec<Record> = cram_reader.records().map(|v| v.unwrap()).collect();
2335
2336 let mut bam_reader = Reader::from_path(bam_path).unwrap();
2338 let bam_records: Vec<Record> = bam_reader.records().map(|v| v.unwrap()).collect();
2339
2340 compare_inner_bam_cram_records(&cram_records, &bam_records);
2341 }
2342
2343 #[test]
2344 fn test_write_cram() {
2345 let bam_path = "./test/test_cram.bam";
2347 let ref_path = "./test/test_cram.fa";
2348 let mut bam_reader = Reader::from_path(bam_path).unwrap();
2349 let bam_records: Vec<Record> = bam_reader.records().map(|v| v.unwrap()).collect();
2350
2351 let tmp = tempfile::Builder::new()
2353 .prefix("rust-htslib")
2354 .tempdir()
2355 .expect("Cannot create temp dir");
2356 let cram_path = tmp.path().join("test.cram");
2357
2358 {
2360 let mut header = Header::new();
2361 header.push_record(
2362 HeaderRecord::new(b"HD")
2363 .push_tag(b"VN", "1.5")
2364 .push_tag(b"SO", "coordinate"),
2365 );
2366 header.push_record(
2367 HeaderRecord::new(b"SQ")
2368 .push_tag(b"SN", "chr1")
2369 .push_tag(b"LN", 120)
2370 .push_tag(b"M5", "20a9a0fb770814e6c5e49946750f9724")
2371 .push_tag(b"UR", "test/test_cram.fa"),
2372 );
2373 header.push_record(
2374 HeaderRecord::new(b"SQ")
2375 .push_tag(b"SN", "chr2")
2376 .push_tag(b"LN", 120)
2377 .push_tag(b"M5", "7a2006ccca94ea92b6dae5997e1b0d70")
2378 .push_tag(b"UR", "test/test_cram.fa"),
2379 );
2380 header.push_record(
2381 HeaderRecord::new(b"SQ")
2382 .push_tag(b"SN", "chr3")
2383 .push_tag(b"LN", 120)
2384 .push_tag(b"M5", "a66b336bfe3ee8801c744c9545c87e24")
2385 .push_tag(b"UR", "test/test_cram.fa"),
2386 );
2387
2388 let mut cram_writer = Writer::from_path(&cram_path, &header, Format::Cram)
2389 .expect("Error opening CRAM file.");
2390 cram_writer.set_reference(ref_path).unwrap();
2391
2392 for rec in bam_records.iter() {
2394 cram_writer
2395 .write(rec)
2396 .expect("Faied to write record to CRAM.");
2397 }
2398 }
2399
2400 {
2402 let mut cram_reader = Reader::from_path(cram_path).unwrap();
2404 cram_reader.set_reference(ref_path).unwrap();
2405 let cram_records: Vec<Record> = cram_reader.records().map(|v| v.unwrap()).collect();
2406
2407 compare_inner_bam_cram_records(&cram_records, &bam_records);
2409 }
2410
2411 tmp.close().expect("Failed to delete temp dir");
2412 }
2413
2414 #[test]
2415 fn test_compression_level_conversion() {
2416 assert_eq!(CompressionLevel::Uncompressed.convert().unwrap(), 0);
2418 assert_eq!(CompressionLevel::Fastest.convert().unwrap(), 1);
2419 assert_eq!(CompressionLevel::Maximum.convert().unwrap(), 9);
2420
2421 for level in 0..=9 {
2423 assert_eq!(CompressionLevel::Level(level).convert().unwrap(), level);
2424 }
2425 assert!(CompressionLevel::Level(10).convert().is_err());
2427 }
2428
2429 #[test]
2430 fn test_write_compression() {
2431 let tmp = tempfile::Builder::new()
2432 .prefix("rust-htslib")
2433 .tempdir()
2434 .expect("Cannot create temp dir");
2435 let input_bam_path = "test/test.bam";
2436
2437 let levels_to_test = vec![
2439 CompressionLevel::Maximum,
2440 CompressionLevel::Level(6),
2441 CompressionLevel::Fastest,
2442 CompressionLevel::Uncompressed,
2443 ];
2444 let file_sizes: Vec<_> = levels_to_test
2445 .iter()
2446 .map(|level| {
2447 let output_bam_path = tmp.path().join("test.bam");
2448 {
2449 let mut reader = Reader::from_path(input_bam_path).unwrap();
2450 let header = Header::from_template(reader.header());
2451 let mut writer =
2452 Writer::from_path(&output_bam_path, &header, Format::Bam).unwrap();
2453 writer.set_compression_level(*level).unwrap();
2454 for record in reader.records() {
2455 let r = record.unwrap();
2456 writer.write(&r).unwrap();
2457 }
2458 }
2459 fs::metadata(output_bam_path).unwrap().len()
2460 })
2461 .collect();
2462
2463 println!("testing compression leves: {:?}", levels_to_test);
2465 println!("got compressed sizes: {:?}", file_sizes);
2466
2467 #[cfg(feature = "libdeflate")]
2470 assert!(file_sizes[1..].windows(2).all(|size| size[0] <= size[1]));
2471
2472 #[cfg(not(feature = "libdeflate"))]
2473 assert!(file_sizes.windows(2).all(|size| size[0] <= size[1]));
2474
2475 tmp.close().expect("Failed to delete temp dir");
2476 }
2477
2478 #[test]
2479 fn test_bam_fails_on_vcf() {
2480 let bam_path = "./test/test_left.vcf";
2481 let bam_reader = Reader::from_path(bam_path);
2482 assert!(bam_reader.is_err());
2483 }
2484
2485 #[test]
2486 fn test_indexde_bam_fails_on_vcf() {
2487 let bam_path = "./test/test_left.vcf";
2488 let bam_reader = IndexedReader::from_path(bam_path);
2489 assert!(bam_reader.is_err());
2490 }
2491
2492 #[test]
2493 fn test_bam_fails_on_toml() {
2494 let bam_path = "./Cargo.toml";
2495 let bam_reader = Reader::from_path(bam_path);
2496 assert!(bam_reader.is_err());
2497 }
2498
2499 #[test]
2500 fn test_sam_writer_example() {
2501 fn from_bam_with_filter<F>(bamfile: &str, samfile: &str, f: F) -> bool
2502 where
2503 F: Fn(&record::Record) -> Option<bool>,
2504 {
2505 let mut bam_reader = Reader::from_path(bamfile).unwrap(); let header = header::Header::from_template(bam_reader.header());
2507 let mut sam_writer = Writer::from_path(samfile, &header, Format::Sam).unwrap();
2508 for record in bam_reader.records() {
2509 if record.is_err() {
2510 return false;
2511 }
2512 let parsed = record.unwrap();
2513 match f(&parsed) {
2514 None => return true,
2515 Some(false) => {}
2516 Some(true) => {
2517 if sam_writer.write(&parsed).is_err() {
2518 return false;
2519 }
2520 }
2521 }
2522 }
2523 true
2524 }
2525 use std::fs::File;
2526 use std::io::Read;
2527 let bamfile = "./test/bam2sam_test.bam";
2528 let samfile = "./test/bam2sam_out.sam";
2529 let expectedfile = "./test/bam2sam_expected.sam";
2530 let result = from_bam_with_filter(bamfile, samfile, |_| Some(true));
2531 assert!(result);
2532 let mut expected = Vec::new();
2533 let mut written = Vec::new();
2534 assert!(
2535 File::open(expectedfile)
2536 .unwrap()
2537 .read_to_end(&mut expected)
2538 .is_ok()
2539 );
2540 assert!(
2541 File::open(samfile)
2542 .unwrap()
2543 .read_to_end(&mut written)
2544 .is_ok()
2545 );
2546 assert_eq!(expected, written);
2547 }
2548
2549 #[test]
2564 fn test_rc_records() {
2565 let (names, flags, seqs, quals, cigars) = gold();
2566 let mut bam = Reader::from_path(Path::new("test/test.bam")).expect("Error opening file.");
2567 let del_len = [1, 1, 1, 1, 1, 100000];
2568
2569 for (i, record) in bam.rc_records().enumerate() {
2570 let rec = record.unwrap();
2572 println!("{}", str::from_utf8(rec.qname()).ok().unwrap());
2573 assert_eq!(rec.qname(), names[i]);
2574 assert_eq!(rec.flags(), flags[i]);
2575 assert_eq!(rec.seq().as_bytes(), seqs[i]);
2576
2577 let cigar = rec.cigar();
2578 assert_eq!(*cigar, cigars[i]);
2579
2580 let end_pos = cigar.end_pos();
2581 assert_eq!(end_pos, rec.pos() + 100 + del_len[i]);
2582 assert_eq!(
2583 cigar
2584 .read_pos(end_pos as u32 - 10, false, false)
2585 .unwrap()
2586 .unwrap(),
2587 90
2588 );
2589 assert_eq!(
2590 cigar
2591 .read_pos(rec.pos() as u32 + 20, false, false)
2592 .unwrap()
2593 .unwrap(),
2594 20
2595 );
2596 assert_eq!(cigar.read_pos(4000000, false, false).unwrap(), None);
2597 let qual: Vec<u8> = quals[i].iter().map(|&q| q - 33).collect();
2599 assert_eq!(rec.qual(), &qual[..]);
2600 }
2601 }
2602
2603 #[test]
2604 fn test_aux_arrays() {
2605 let bam_header = Header::new();
2606 let mut test_record = Record::from_sam(
2607 &HeaderView::from_header(&bam_header),
2608 "ali1\t4\t*\t0\t0\t*\t*\t0\t0\tACGT\tFFFF".as_bytes(),
2609 )
2610 .unwrap();
2611
2612 let array_i8: Vec<i8> = vec![i8::MIN, -1, 0, 1, i8::MAX];
2613 let array_u8: Vec<u8> = vec![u8::MIN, 0, 1, u8::MAX];
2614 let array_i16: Vec<i16> = vec![i16::MIN, -1, 0, 1, i16::MAX];
2615 let array_u16: Vec<u16> = vec![u16::MIN, 0, 1, u16::MAX];
2616 let array_i32: Vec<i32> = vec![i32::MIN, -1, 0, 1, i32::MAX];
2617 let array_u32: Vec<u32> = vec![u32::MIN, 0, 1, u32::MAX];
2618 let array_f32: Vec<f32> = vec![f32::MIN, 0.0, -0.0, 0.1, 0.99, f32::MAX];
2619
2620 test_record
2621 .push_aux(b"XA", Aux::ArrayI8((&array_i8).into()))
2622 .unwrap();
2623 test_record
2624 .push_aux(b"XB", Aux::ArrayU8((&array_u8).into()))
2625 .unwrap();
2626 test_record
2627 .push_aux(b"XC", Aux::ArrayI16((&array_i16).into()))
2628 .unwrap();
2629 test_record
2630 .push_aux(b"XD", Aux::ArrayU16((&array_u16).into()))
2631 .unwrap();
2632 test_record
2633 .push_aux(b"XE", Aux::ArrayI32((&array_i32).into()))
2634 .unwrap();
2635 test_record
2636 .push_aux(b"XF", Aux::ArrayU32((&array_u32).into()))
2637 .unwrap();
2638 test_record
2639 .push_aux(b"XG", Aux::ArrayFloat((&array_f32).into()))
2640 .unwrap();
2641
2642 {
2643 let tag = b"XA";
2644 if let Ok(Aux::ArrayI8(array)) = test_record.aux(tag) {
2645 let aux_array_content = array.iter().collect::<Vec<_>>();
2647 assert_eq!(aux_array_content, array_i8);
2648
2649 {
2651 let mut copy_test_record = test_record.clone();
2652
2653 assert!(copy_test_record.push_aux(tag, Aux::I8(3)).is_err());
2655
2656 copy_test_record.remove_aux(tag).unwrap();
2658 assert!(copy_test_record.aux(tag).is_err());
2659
2660 let src_aux = test_record.aux(tag).unwrap();
2662 assert!(copy_test_record.push_aux(tag, src_aux).is_ok());
2663 if let Ok(Aux::ArrayI8(array)) = copy_test_record.aux(tag) {
2664 let aux_array_content_copied = array.iter().collect::<Vec<_>>();
2665 assert_eq!(aux_array_content_copied, array_i8);
2666 } else {
2667 panic!("Aux tag not found");
2668 }
2669 }
2670 } else {
2671 panic!("Aux tag not found");
2672 }
2673 }
2674
2675 {
2676 let tag = b"XB";
2677 if let Ok(Aux::ArrayU8(array)) = test_record.aux(tag) {
2678 let aux_array_content = array.iter().collect::<Vec<_>>();
2680 assert_eq!(aux_array_content, array_u8);
2681
2682 {
2684 let mut copy_test_record = test_record.clone();
2685
2686 assert!(copy_test_record.push_aux(tag, Aux::U8(3)).is_err());
2688
2689 copy_test_record.remove_aux(tag).unwrap();
2691 assert!(copy_test_record.aux(tag).is_err());
2692
2693 let src_aux = test_record.aux(tag).unwrap();
2695 assert!(copy_test_record.push_aux(tag, src_aux).is_ok());
2696 if let Ok(Aux::ArrayU8(array)) = copy_test_record.aux(tag) {
2697 let aux_array_content_copied = array.iter().collect::<Vec<_>>();
2698 assert_eq!(aux_array_content_copied, array_u8);
2699 } else {
2700 panic!("Aux tag not found");
2701 }
2702 }
2703 } else {
2704 panic!("Aux tag not found");
2705 }
2706 }
2707
2708 {
2709 let tag = b"XC";
2710 if let Ok(Aux::ArrayI16(array)) = test_record.aux(tag) {
2711 let aux_array_content = array.iter().collect::<Vec<_>>();
2713 assert_eq!(aux_array_content, array_i16);
2714
2715 {
2717 let mut copy_test_record = test_record.clone();
2718
2719 assert!(copy_test_record.push_aux(tag, Aux::I16(3)).is_err());
2721
2722 copy_test_record.remove_aux(tag).unwrap();
2724 assert!(copy_test_record.aux(tag).is_err());
2725
2726 let src_aux = test_record.aux(tag).unwrap();
2728 assert!(copy_test_record.push_aux(tag, src_aux).is_ok());
2729 if let Ok(Aux::ArrayI16(array)) = copy_test_record.aux(tag) {
2730 let aux_array_content_copied = array.iter().collect::<Vec<_>>();
2731 assert_eq!(aux_array_content_copied, array_i16);
2732 } else {
2733 panic!("Aux tag not found");
2734 }
2735 }
2736 } else {
2737 panic!("Aux tag not found");
2738 }
2739 }
2740
2741 {
2742 let tag = b"XD";
2743 if let Ok(Aux::ArrayU16(array)) = test_record.aux(tag) {
2744 let aux_array_content = array.iter().collect::<Vec<_>>();
2746 assert_eq!(aux_array_content, array_u16);
2747
2748 {
2750 let mut copy_test_record = test_record.clone();
2751
2752 assert!(copy_test_record.push_aux(tag, Aux::U16(3)).is_err());
2754
2755 copy_test_record.remove_aux(tag).unwrap();
2757 assert!(copy_test_record.aux(tag).is_err());
2758
2759 let src_aux = test_record.aux(tag).unwrap();
2761 assert!(copy_test_record.push_aux(tag, src_aux).is_ok());
2762 if let Ok(Aux::ArrayU16(array)) = copy_test_record.aux(tag) {
2763 let aux_array_content_copied = array.iter().collect::<Vec<_>>();
2764 assert_eq!(aux_array_content_copied, array_u16);
2765 } else {
2766 panic!("Aux tag not found");
2767 }
2768 }
2769 } else {
2770 panic!("Aux tag not found");
2771 }
2772 }
2773
2774 {
2775 let tag = b"XE";
2776 if let Ok(Aux::ArrayI32(array)) = test_record.aux(tag) {
2777 let aux_array_content = array.iter().collect::<Vec<_>>();
2779 assert_eq!(aux_array_content, array_i32);
2780
2781 {
2783 let mut copy_test_record = test_record.clone();
2784
2785 assert!(copy_test_record.push_aux(tag, Aux::I32(3)).is_err());
2787
2788 copy_test_record.remove_aux(tag).unwrap();
2790 assert!(copy_test_record.aux(tag).is_err());
2791
2792 let src_aux = test_record.aux(tag).unwrap();
2794 assert!(copy_test_record.push_aux(tag, src_aux).is_ok());
2795 if let Ok(Aux::ArrayI32(array)) = copy_test_record.aux(tag) {
2796 let aux_array_content_copied = array.iter().collect::<Vec<_>>();
2797 assert_eq!(aux_array_content_copied, array_i32);
2798 } else {
2799 panic!("Aux tag not found");
2800 }
2801 }
2802 } else {
2803 panic!("Aux tag not found");
2804 }
2805 }
2806
2807 {
2808 let tag = b"XF";
2809 if let Ok(Aux::ArrayU32(array)) = test_record.aux(tag) {
2810 let aux_array_content = array.iter().collect::<Vec<_>>();
2812 assert_eq!(aux_array_content, array_u32);
2813
2814 {
2816 let mut copy_test_record = test_record.clone();
2817
2818 assert!(copy_test_record.push_aux(tag, Aux::U32(3)).is_err());
2820
2821 copy_test_record.remove_aux(tag).unwrap();
2823 assert!(copy_test_record.aux(tag).is_err());
2824
2825 let src_aux = test_record.aux(tag).unwrap();
2827 assert!(copy_test_record.push_aux(tag, src_aux).is_ok());
2828 if let Ok(Aux::ArrayU32(array)) = copy_test_record.aux(tag) {
2829 let aux_array_content_copied = array.iter().collect::<Vec<_>>();
2830 assert_eq!(aux_array_content_copied, array_u32);
2831 } else {
2832 panic!("Aux tag not found");
2833 }
2834 }
2835 } else {
2836 panic!("Aux tag not found");
2837 }
2838 }
2839
2840 {
2841 let tag = b"XG";
2842 if let Ok(Aux::ArrayFloat(array)) = test_record.aux(tag) {
2843 let aux_array_content = array.iter().collect::<Vec<_>>();
2845 assert_eq!(aux_array_content, array_f32);
2846
2847 {
2849 let mut copy_test_record = test_record.clone();
2850
2851 assert!(copy_test_record.push_aux(tag, Aux::Float(3.0)).is_err());
2853
2854 copy_test_record.remove_aux(tag).unwrap();
2856 assert!(copy_test_record.aux(tag).is_err());
2857
2858 let src_aux = test_record.aux(tag).unwrap();
2860 assert!(copy_test_record.push_aux(tag, src_aux).is_ok());
2861 if let Ok(Aux::ArrayFloat(array)) = copy_test_record.aux(tag) {
2862 let aux_array_content_copied = array.iter().collect::<Vec<_>>();
2863 assert_eq!(aux_array_content_copied, array_f32);
2864 } else {
2865 panic!("Aux tag not found");
2866 }
2867 }
2868 } else {
2869 panic!("Aux tag not found");
2870 }
2871 }
2872
2873 for item in test_record.aux_iter() {
2875 match item.unwrap() {
2876 (b"XA", Aux::ArrayI8(array)) => {
2877 assert_eq!(&array.iter().collect::<Vec<_>>(), &array_i8);
2878 }
2879 (b"XB", Aux::ArrayU8(array)) => {
2880 assert_eq!(&array.iter().collect::<Vec<_>>(), &array_u8);
2881 }
2882 (b"XC", Aux::ArrayI16(array)) => {
2883 assert_eq!(&array.iter().collect::<Vec<_>>(), &array_i16);
2884 }
2885 (b"XD", Aux::ArrayU16(array)) => {
2886 assert_eq!(&array.iter().collect::<Vec<_>>(), &array_u16);
2887 }
2888 (b"XE", Aux::ArrayI32(array)) => {
2889 assert_eq!(&array.iter().collect::<Vec<_>>(), &array_i32);
2890 }
2891 (b"XF", Aux::ArrayU32(array)) => {
2892 assert_eq!(&array.iter().collect::<Vec<_>>(), &array_u32);
2893 }
2894 (b"XG", Aux::ArrayFloat(array)) => {
2895 assert_eq!(&array.iter().collect::<Vec<_>>(), &array_f32);
2896 }
2897 _ => {
2898 panic!();
2899 }
2900 }
2901 }
2902
2903 assert_eq!(
2905 test_record.aux(b"XA").unwrap(),
2906 Aux::ArrayI8((&array_i8).into())
2907 );
2908 assert_eq!(
2909 test_record.aux(b"XB").unwrap(),
2910 Aux::ArrayU8((&array_u8).into())
2911 );
2912 assert_eq!(
2913 test_record.aux(b"XC").unwrap(),
2914 Aux::ArrayI16((&array_i16).into())
2915 );
2916 assert_eq!(
2917 test_record.aux(b"XD").unwrap(),
2918 Aux::ArrayU16((&array_u16).into())
2919 );
2920 assert_eq!(
2921 test_record.aux(b"XE").unwrap(),
2922 Aux::ArrayI32((&array_i32).into())
2923 );
2924 assert_eq!(
2925 test_record.aux(b"XF").unwrap(),
2926 Aux::ArrayU32((&array_u32).into())
2927 );
2928 assert_eq!(
2929 test_record.aux(b"XG").unwrap(),
2930 Aux::ArrayFloat((&array_f32).into())
2931 );
2932 }
2933
2934 #[test]
2935 fn test_aux_scalars() {
2936 let bam_header = Header::new();
2937 let mut test_record = Record::from_sam(
2938 &HeaderView::from_header(&bam_header),
2939 "ali1\t4\t*\t0\t0\t*\t*\t0\t0\tACGT\tFFFF".as_bytes(),
2940 )
2941 .unwrap();
2942
2943 test_record.push_aux(b"XA", Aux::I8(i8::MIN)).unwrap();
2944 test_record.push_aux(b"XB", Aux::I8(i8::MAX)).unwrap();
2945 test_record.push_aux(b"XC", Aux::U8(u8::MIN)).unwrap();
2946 test_record.push_aux(b"XD", Aux::U8(u8::MAX)).unwrap();
2947 test_record.push_aux(b"XE", Aux::I16(i16::MIN)).unwrap();
2948 test_record.push_aux(b"XF", Aux::I16(i16::MAX)).unwrap();
2949 test_record.push_aux(b"XG", Aux::U16(u16::MIN)).unwrap();
2950 test_record.push_aux(b"XH", Aux::U16(u16::MAX)).unwrap();
2951 test_record.push_aux(b"XI", Aux::I32(i32::MIN)).unwrap();
2952 test_record.push_aux(b"XJ", Aux::I32(i32::MAX)).unwrap();
2953 test_record.push_aux(b"XK", Aux::U32(u32::MIN)).unwrap();
2954 test_record.push_aux(b"XL", Aux::U32(u32::MAX)).unwrap();
2955 test_record
2956 .push_aux(b"XM", Aux::Float(std::f32::consts::PI))
2957 .unwrap();
2958 test_record
2959 .push_aux(b"XN", Aux::Double(std::f64::consts::PI))
2960 .unwrap();
2961 test_record
2962 .push_aux(b"XO", Aux::String("Test str"))
2963 .unwrap();
2964 test_record.push_aux(b"XP", Aux::I8(0)).unwrap();
2965
2966 let collected_aux_fields = test_record.aux_iter().collect::<Result<Vec<_>>>().unwrap();
2967 assert_eq!(
2968 collected_aux_fields,
2969 vec![
2970 (&b"XA"[..], Aux::I8(i8::MIN)),
2971 (&b"XB"[..], Aux::I8(i8::MAX)),
2972 (&b"XC"[..], Aux::U8(u8::MIN)),
2973 (&b"XD"[..], Aux::U8(u8::MAX)),
2974 (&b"XE"[..], Aux::I16(i16::MIN)),
2975 (&b"XF"[..], Aux::I16(i16::MAX)),
2976 (&b"XG"[..], Aux::U16(u16::MIN)),
2977 (&b"XH"[..], Aux::U16(u16::MAX)),
2978 (&b"XI"[..], Aux::I32(i32::MIN)),
2979 (&b"XJ"[..], Aux::I32(i32::MAX)),
2980 (&b"XK"[..], Aux::U32(u32::MIN)),
2981 (&b"XL"[..], Aux::U32(u32::MAX)),
2982 (&b"XM"[..], Aux::Float(std::f32::consts::PI)),
2983 (&b"XN"[..], Aux::Double(std::f64::consts::PI)),
2984 (&b"XO"[..], Aux::String("Test str")),
2985 (&b"XP"[..], Aux::I8(0)),
2986 ]
2987 );
2988 }
2989
2990 #[test]
2991 fn test_aux_array_partial_eq() {
2992 use record::AuxArray;
2993
2994 let one_data: Vec<i8> = vec![0, 1, 2, 3, 4, 5, 6];
2996 let one_aux_array = AuxArray::from(&one_data);
2997
2998 let two_data: Vec<i8> = vec![0, 1, 2, 3, 4, 5];
2999 let two_aux_array = AuxArray::from(&two_data);
3000
3001 assert_ne!(&one_data, &two_data);
3002 assert_ne!(&one_aux_array, &two_aux_array);
3003
3004 let one_aux = Aux::ArrayI8(one_aux_array);
3005 let two_aux = Aux::ArrayI8(two_aux_array);
3006 assert_ne!(&one_aux, &two_aux);
3007
3008 let bam_header = Header::new();
3010 let mut test_record = Record::from_sam(
3011 &HeaderView::from_header(&bam_header),
3012 "ali1\t4\t*\t0\t0\t*\t*\t0\t0\tACGT\tFFFF".as_bytes(),
3013 )
3014 .unwrap();
3015
3016 test_record.push_aux(b"XA", one_aux).unwrap();
3017 test_record.push_aux(b"XB", two_aux).unwrap();
3018
3019 assert_eq!(
3021 test_record.aux(b"XA").unwrap(),
3022 test_record.aux(b"XA").unwrap()
3023 );
3024 assert_ne!(
3026 test_record.aux(b"XA").unwrap(),
3027 test_record.aux(b"XB").unwrap()
3028 );
3029
3030 assert_eq!(
3032 test_record.aux(b"XA").unwrap(),
3033 Aux::ArrayI8((&one_data).into())
3034 );
3035 assert_eq!(
3036 test_record.aux(b"XB").unwrap(),
3037 Aux::ArrayI8((&two_data).into())
3038 );
3039 assert_ne!(
3041 test_record.aux(b"XA").unwrap(),
3042 Aux::ArrayI8((&two_data).into())
3043 );
3044 assert_ne!(
3045 test_record.aux(b"XB").unwrap(),
3046 Aux::ArrayI8((&one_data).into())
3047 );
3048 }
3049
3050 #[test]
3052 fn test_bam_header_sync() {
3053 let reader = Reader::from_path("test/test_issue_156_no_text.bam").unwrap();
3054 let header_hashmap = Header::from_template(reader.header()).to_hashmap();
3055 let header_refseqs = header_hashmap.get("SQ").unwrap();
3056 assert_eq!(header_refseqs[0].get("SN").unwrap(), "ref_1",);
3057 assert_eq!(header_refseqs[0].get("LN").unwrap(), "10000000",);
3058 }
3059
3060 #[test]
3061 fn test_bam_new() {
3062 let tmp = tempfile::Builder::new()
3064 .prefix("rust-htslib")
3065 .tempdir()
3066 .expect("Cannot create temp dir");
3067 let bampath = tmp.path().join("test.bam");
3068
3069 {
3071 let mut header = Header::new();
3073
3074 header.push_record(
3076 HeaderRecord::new(b"HD")
3077 .push_tag(b"VN", "1.6")
3078 .push_tag(b"SO", "unsorted"),
3079 );
3080
3081 let mut writer = Writer::from_path(&bampath, &header, Format::Bam).unwrap();
3083
3084 let record = Record::new();
3086
3087 assert!(writer.write(&record).is_ok());
3089 }
3090
3091 {
3093 let mut reader = Reader::from_path(bampath).expect("Error opening file.");
3095
3096 let mut rec = Record::new();
3098 match reader.read(&mut rec) {
3099 Some(r) => r.expect("Failed to read record."),
3100 None => panic!("No record read."),
3101 };
3102
3103 assert!(rec.is_unmapped());
3105 assert_eq!(rec.tid(), -1);
3106 assert_eq!(rec.pos(), -1);
3107 assert_eq!(rec.mtid(), -1);
3108 assert_eq!(rec.mpos(), -1);
3109 }
3110 }
3111
3112 #[test]
3113 fn test_idxstats_bam() {
3114 let mut reader = IndexedReader::from_path("test/test.bam").unwrap();
3115 let expected = vec![
3116 (0, 15072423, 6, 0),
3117 (1, 15279345, 0, 0),
3118 (2, 13783700, 0, 0),
3119 (3, 17493793, 0, 0),
3120 (4, 20924149, 0, 0),
3121 (-1, 0, 0, 0),
3122 ];
3123 let actual = reader.index_stats().unwrap();
3124 assert_eq!(expected, actual);
3125 }
3126
3127 #[test]
3128 fn test_number_mapped_and_unmapped_bam() {
3129 let reader = IndexedReader::from_path("test/test.bam").unwrap();
3130 let expected = (6, 0);
3131 let actual = reader.index().number_mapped_unmapped(0);
3132 assert_eq!(expected, actual);
3133 }
3134
3135 #[test]
3136 fn test_number_unmapped_global_bam() {
3137 let reader = IndexedReader::from_path("test/test_unmapped.bam").unwrap();
3138 let expected = 8;
3139 let actual = reader.index().number_unmapped();
3140 assert_eq!(expected, actual);
3141 }
3142
3143 #[test]
3144 fn test_idxstats_cram() {
3145 let mut reader = IndexedReader::from_path("test/test_cram.cram").unwrap();
3146 reader.set_reference("test/test_cram.fa").unwrap();
3147 let expected = vec![
3148 (0, 120, 2, 0),
3149 (1, 120, 2, 0),
3150 (2, 120, 2, 0),
3151 (-1, 0, 0, 0),
3152 ];
3153 let actual = reader.index_stats().unwrap();
3154 assert_eq!(expected, actual);
3155 }
3156
3157 #[test]
3158 fn test_slow_idxstats_cram() {
3159 let mut reader = IndexedReader::from_path("test/test_cram.cram").unwrap();
3160 reader.set_reference("test/test_cram.fa").unwrap();
3161 let expected = vec![
3162 (0, 120, 2, 0),
3163 (1, 120, 2, 0),
3164 (2, 120, 2, 0),
3165 (-1, 0, 0, 0),
3166 ];
3167 let actual = reader.index_stats().unwrap();
3168 assert_eq!(expected, actual);
3169 }
3170
3171 }