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