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 let count_idx = if tid == -1 { nref } else { tid as usize };
851
852 if tid != last_tid {
853 if (last_tid >= -1) && (counts[count_idx][0] + counts[count_idx][1]) > 0 {
854 return Err(Error::BamUnsorted);
855 }
856 last_tid = tid;
857 }
858
859 let idx = if ((*b).core.flag as u32 & hts_sys::BAM_FUNMAP) > 0 {
860 1
861 } else {
862 0
863 };
864 counts[count_idx][idx] += 1;
865 }
866
867 if ret == -1 {
868 let res = (0..nref)
869 .map(|i| {
870 (
871 i as i64,
872 header.target_len(i as u32).unwrap(),
873 counts[i][0],
874 counts[i][1],
875 )
876 })
877 .chain([(-1, 0, counts[nref][0], counts[nref][1])])
878 .collect();
879 Ok(res)
880 } else {
881 Err(Error::SlowIdxStats)
882 }
883 }
884
885 pub fn index_stats(&mut self) -> Result<Vec<(i64, u64, u64, u64)>> {
890 let header = self.header();
891 let index = self.index();
892 if index.inner_ptr().is_null() {
893 panic!("Index is null");
894 }
895 unsafe {
897 if (*self.htsfile()).format.format != htslib::htsExactFormat_bam {
898 return self.slow_idxstats();
899 }
900 }
901 Ok((0..header.target_count())
902 .map(|tid| {
903 let (mapped, unmapped) = index.number_mapped_unmapped(tid);
904 let tlen = header.target_len(tid).unwrap();
905 (tid as i64, tlen, mapped, unmapped)
906 })
907 .chain([(-1, 0, 0, index.number_unmapped())])
908 .collect::<_>())
909 }
910}
911
912#[derive(Debug)]
913pub struct IndexView {
914 inner: *mut hts_sys::hts_idx_t,
915}
916
917unsafe impl Send for IndexView {}
918unsafe impl Sync for IndexView {}
919
920impl IndexView {
921 fn new(hts_idx: *mut hts_sys::hts_idx_t) -> Self {
922 Self { inner: hts_idx }
923 }
924
925 #[inline]
926 pub fn inner(&self) -> &hts_sys::hts_idx_t {
927 unsafe { self.inner.as_ref().unwrap() }
928 }
929
930 #[inline]
931 pub fn inner_ptr(&self) -> *const hts_sys::hts_idx_t {
933 self.inner
934 }
935
936 #[inline]
937 pub fn inner_mut(&mut self) -> &mut hts_sys::hts_idx_t {
938 unsafe { self.inner.as_mut().unwrap() }
939 }
940
941 #[inline]
942 pub fn inner_ptr_mut(&mut self) -> *mut hts_sys::hts_idx_t {
944 self.inner
945 }
946
947 fn number_mapped_unmapped(&self, tid: u32) -> (u64, u64) {
950 let (mut mapped, mut unmapped) = (0, 0);
951 unsafe {
952 hts_sys::hts_idx_get_stat(self.inner, tid as i32, &mut mapped, &mut unmapped);
953 }
954 (mapped, unmapped)
955 }
956
957 fn number_unmapped(&self) -> u64 {
960 unsafe { hts_sys::hts_idx_get_n_no_coor(self.inner) }
961 }
962}
963
964impl Drop for IndexView {
965 fn drop(&mut self) {
966 unsafe {
967 htslib::hts_idx_destroy(self.inner);
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 let Some(itr) = self.itr {
1036 htslib::hts_itr_destroy(itr);
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 {
1435 None
1436 } else {
1437 Some(tid as u32)
1438 }
1439 }
1440
1441 pub fn tid2name(&self, tid: u32) -> &[u8] {
1442 unsafe { ffi::CStr::from_ptr(htslib::sam_hdr_tid2name(self.inner, tid as i32)).to_bytes() }
1443 }
1444
1445 pub fn target_count(&self) -> u32 {
1446 self.inner().n_targets as u32
1447 }
1448
1449 pub fn target_names(&self) -> Vec<&[u8]> {
1450 let names = unsafe {
1451 slice::from_raw_parts(self.inner().target_name, self.target_count() as usize)
1452 };
1453 names
1454 .iter()
1455 .map(|name| unsafe { ffi::CStr::from_ptr(*name).to_bytes() })
1456 .collect()
1457 }
1458
1459 pub fn target_len(&self, tid: u32) -> Option<u64> {
1460 let inner = unsafe { *self.inner };
1461 if (tid as i32) < inner.n_targets {
1462 let l: &[u32] =
1463 unsafe { slice::from_raw_parts(inner.target_len, inner.n_targets as usize) };
1464 Some(l[tid as usize] as u64)
1465 } else {
1466 None
1467 }
1468 }
1469
1470 pub fn as_bytes(&self) -> &[u8] {
1472 unsafe {
1473 let rebuilt_hdr = htslib::sam_hdr_str(self.inner);
1474 if rebuilt_hdr.is_null() {
1475 return b"";
1476 }
1477 ffi::CStr::from_ptr(rebuilt_hdr).to_bytes()
1478 }
1479 }
1480}
1481
1482impl Clone for HeaderView {
1483 fn clone(&self) -> Self {
1484 HeaderView {
1485 inner: unsafe { htslib::sam_hdr_dup(self.inner) },
1486 }
1487 }
1488}
1489
1490impl Drop for HeaderView {
1491 fn drop(&mut self) {
1492 unsafe {
1493 htslib::sam_hdr_destroy(self.inner);
1494 }
1495 }
1496}
1497
1498#[cfg(test)]
1499mod tests {
1500 use super::header::HeaderRecord;
1501 use super::record::{Aux, Cigar, CigarString};
1502 use super::*;
1503 use std::collections::HashMap;
1504 use std::fs;
1505 use std::path::Path;
1506 use std::str;
1507
1508 type GoldType = (
1509 [&'static [u8]; 6],
1510 [u16; 6],
1511 [&'static [u8]; 6],
1512 [&'static [u8]; 6],
1513 [CigarString; 6],
1514 );
1515 fn gold() -> GoldType {
1516 let names = [
1517 &b"I"[..],
1518 &b"II.14978392"[..],
1519 &b"III"[..],
1520 &b"IV"[..],
1521 &b"V"[..],
1522 &b"VI"[..],
1523 ];
1524 let flags = [16u16, 16u16, 16u16, 16u16, 16u16, 2048u16];
1525 let seqs = [
1526 &b"CCTAGCCCTAACCCTAACCCTAACCCTAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCC\
1527TAAGCCTAAGCCTAAGCCTAA"[..],
1528 &b"CCTAGCCCTAACCCTAACCCTAACCCTAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCC\
1529TAAGCCTAAGCCTAAGCCTAA"[..],
1530 &b"CCTAGCCCTAACCCTAACCCTAACCCTAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCC\
1531TAAGCCTAAGCCTAAGCCTAA"[..],
1532 &b"CCTAGCCCTAACCCTAACCCTAACCCTAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCC\
1533TAAGCCTAAGCCTAAGCCTAA"[..],
1534 &b"CCTAGCCCTAACCCTAACCCTAACCCTAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCC\
1535TAAGCCTAAGCCTAAGCCTAA"[..],
1536 &b"ACTAAGCCTAAGCCTAAGCCTAAGCCAATTATCGATTTCTGAAAAAATTATCGAATTTTCTAGAAATTTTGCAAATTTT\
1537TTCATAAAATTATCGATTTTA"[..],
1538 ];
1539 let quals = [
1540 &b"#############################@B?8B?BA@@DDBCDDCBC@CDCDCCCCCCCCCCCCCCCCCCCCCCCCCCCC\
1541CCCCCCCCCCCCCCCCCCC"[..],
1542 &b"#############################@B?8B?BA@@DDBCDDCBC@CDCDCCCCCCCCCCCCCCCCCCCCCCCCCCCC\
1543CCCCCCCCCCCCCCCCCCC"[..],
1544 &b"#############################@B?8B?BA@@DDBCDDCBC@CDCDCCCCCCCCCCCCCCCCCCCCCCCCCCCC\
1545CCCCCCCCCCCCCCCCCCC"[..],
1546 &b"#############################@B?8B?BA@@DDBCDDCBC@CDCDCCCCCCCCCCCCCCCCCCCCCCCCCCCC\
1547CCCCCCCCCCCCCCCCCCC"[..],
1548 &b"#############################@B?8B?BA@@DDBCDDCBC@CDCDCCCCCCCCCCCCCCCCCCCCCCCCCCCC\
1549CCCCCCCCCCCCCCCCCCC"[..],
1550 &b"#############################@B?8B?BA@@DDBCDDCBC@CDCDCCCCCCCCCCCCCCCCCCCCCCCCCCCC\
1551CCCCCCCCCCCCCCCCCCC"[..],
1552 ];
1553 let cigars = [
1554 CigarString(vec![Cigar::Match(27), Cigar::Del(1), Cigar::Match(73)]),
1555 CigarString(vec![Cigar::Match(27), Cigar::Del(1), Cigar::Match(73)]),
1556 CigarString(vec![Cigar::Match(27), Cigar::Del(1), Cigar::Match(73)]),
1557 CigarString(vec![Cigar::Match(27), Cigar::Del(1), Cigar::Match(73)]),
1558 CigarString(vec![Cigar::Match(27), Cigar::Del(1), Cigar::Match(73)]),
1559 CigarString(vec![Cigar::Match(27), Cigar::Del(100000), Cigar::Match(73)]),
1560 ];
1561 (names, flags, seqs, quals, cigars)
1562 }
1563
1564 fn compare_inner_bam_cram_records(cram_records: &[Record], bam_records: &[Record]) {
1565 for (c1, b1) in cram_records.iter().zip(bam_records.iter()) {
1567 assert_eq!(c1.cigar(), b1.cigar());
1570 assert_eq!(c1.inner().core.pos, b1.inner().core.pos);
1571 assert_eq!(c1.inner().core.mpos, b1.inner().core.mpos);
1572 assert_eq!(c1.inner().core.mtid, b1.inner().core.mtid);
1573 assert_eq!(c1.inner().core.tid, b1.inner().core.tid);
1574 assert_eq!(c1.inner().core.bin, b1.inner().core.bin);
1575 assert_eq!(c1.inner().core.qual, b1.inner().core.qual);
1576 assert_eq!(c1.inner().core.l_extranul, b1.inner().core.l_extranul);
1577 assert_eq!(c1.inner().core.flag, b1.inner().core.flag);
1578 assert_eq!(c1.inner().core.l_qname, b1.inner().core.l_qname);
1579 assert_eq!(c1.inner().core.n_cigar, b1.inner().core.n_cigar);
1580 assert_eq!(c1.inner().core.l_qseq, b1.inner().core.l_qseq);
1581 assert_eq!(c1.inner().core.isize_, b1.inner().core.isize_);
1582 }
1584 }
1585
1586 #[test]
1587 fn test_read() {
1588 let (names, flags, seqs, quals, cigars) = gold();
1589 let mut bam = Reader::from_path(Path::new("test/test.bam")).expect("Error opening file.");
1590 let del_len = [1, 1, 1, 1, 1, 100000];
1591
1592 for (i, record) in bam.records().enumerate() {
1593 let rec = record.expect("Expected valid record");
1594 assert_eq!(rec.qname(), names[i]);
1595 assert_eq!(rec.flags(), flags[i]);
1596 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1597
1598 let cigar = rec.cigar();
1599 assert_eq!(*cigar, cigars[i]);
1600
1601 let end_pos = cigar.end_pos();
1602 assert_eq!(end_pos, rec.pos() + 100 + del_len[i]);
1603 assert_eq!(
1604 cigar
1605 .read_pos(end_pos as u32 - 10, false, false)
1606 .unwrap()
1607 .unwrap(),
1608 90
1609 );
1610 assert_eq!(
1611 cigar
1612 .read_pos(rec.pos() as u32 + 20, false, false)
1613 .unwrap()
1614 .unwrap(),
1615 20
1616 );
1617 assert_eq!(cigar.read_pos(4000000, false, false).unwrap(), None);
1618 let qual: Vec<u8> = quals[i].iter().map(|&q| q - 33).collect();
1620 assert_eq!(rec.qual(), &qual[..]);
1621 }
1622 }
1623
1624 #[test]
1625 fn test_seek() {
1626 let mut bam = Reader::from_path(Path::new("test/test.bam")).expect("Error opening file.");
1627
1628 let mut names_by_voffset = HashMap::new();
1629
1630 let mut offset = bam.tell();
1631 let mut rec = Record::new();
1632 while let Some(r) = bam.read(&mut rec) {
1633 r.expect("error reading bam");
1634 let qname = str::from_utf8(rec.qname()).unwrap().to_string();
1635 println!("{} {}", offset, qname);
1636 names_by_voffset.insert(offset, qname);
1637 offset = bam.tell();
1638 }
1639
1640 for (offset, qname) in names_by_voffset.iter() {
1641 println!("{} {}", offset, qname);
1642 bam.seek(*offset).unwrap();
1643 if let Some(r) = bam.read(&mut rec) {
1644 r.unwrap();
1645 };
1646 let rec_qname = str::from_utf8(rec.qname()).unwrap().to_string();
1647 assert_eq!(qname, &rec_qname);
1648 }
1649 }
1650
1651 #[test]
1652 fn test_read_sam_header() {
1653 let bam = Reader::from_path("test/test.bam").expect("Error opening file.");
1654
1655 let true_header = "@SQ\tSN:CHROMOSOME_I\tLN:15072423\n@SQ\tSN:CHROMOSOME_II\tLN:15279345\
1656 \n@SQ\tSN:CHROMOSOME_III\tLN:13783700\n@SQ\tSN:CHROMOSOME_IV\tLN:17493793\n@SQ\t\
1657 SN:CHROMOSOME_V\tLN:20924149\n"
1658 .to_string();
1659 let header_text = String::from_utf8(bam.header.as_bytes().to_owned()).unwrap();
1660 assert_eq!(header_text, true_header);
1661 }
1662
1663 #[test]
1664 fn test_read_against_sam() {
1665 let mut bam = Reader::from_path("./test/bam2sam_out.sam").unwrap();
1666 for read in bam.records() {
1667 let _read = read.unwrap();
1668 }
1669 }
1670
1671 fn _test_read_indexed_common(mut bam: IndexedReader) {
1672 let (names, flags, seqs, quals, cigars) = gold();
1673 let sq_1 = b"CHROMOSOME_I";
1674 let sq_2 = b"CHROMOSOME_II";
1675 let tid_1 = bam.header.tid(sq_1).expect("Expected tid.");
1676 let tid_2 = bam.header.tid(sq_2).expect("Expected tid.");
1677 assert!(bam.header.target_len(tid_1).expect("Expected target len.") == 15072423);
1678
1679 bam.fetch((tid_1, 0, 2))
1681 .expect("Expected successful fetch.");
1682 assert!(bam.records().count() == 6);
1683
1684 bam.fetch((tid_1, 0, 2))
1686 .expect("Expected successful fetch.");
1687 for (i, record) in bam.records().enumerate() {
1688 let rec = record.expect("Expected valid record");
1689
1690 println!("{}", str::from_utf8(rec.qname()).unwrap());
1691 assert_eq!(rec.qname(), names[i]);
1692 assert_eq!(rec.flags(), flags[i]);
1693 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1694 assert_eq!(*rec.cigar(), cigars[i]);
1695 let qual: Vec<u8> = quals[i].iter().map(|&q| q - 33).collect();
1697 assert_eq!(rec.qual(), &qual[..]);
1698 assert_eq!(rec.aux(b"X"), Err(Error::BamAuxStringError));
1699 assert_eq!(rec.aux(b"NotAvailableAux"), Err(Error::BamAuxTagNotFound));
1700 }
1701
1702 bam.fetch((tid_2, 1, 1))
1704 .expect("Expected successful fetch.");
1705 assert!(bam.records().count() == 0);
1706
1707 bam.fetch(format!("{}:{}-{}", str::from_utf8(sq_1).unwrap(), 0, 2).as_bytes())
1712 .expect("Expected successful fetch.");
1713 assert!(bam.records().count() == 6);
1714 bam.fetch((str::from_utf8(sq_1).unwrap(), 0_u32, 2_u64))
1716 .expect("Expected successful fetch.");
1717 assert!(bam.records().count() == 6);
1718 bam.fetch((&sq_1[..], 0, 2))
1720 .expect("Expected successful fetch.");
1721 assert!(bam.records().count() == 6);
1722 bam.fetch((sq_1, 0, 2)).expect("Expected successful fetch.");
1724 assert!(bam.records().count() == 6);
1725
1726 bam.fetch((0i32, 0u32, 2i64))
1728 .expect("Expected successful fetch.");
1729 assert!(bam.records().count() == 6);
1730 bam.fetch((0u32, 0u32, 2i64))
1732 .expect("Expected successful fetch.");
1733 assert!(bam.records().count() == 6);
1734
1735 bam.fetch(format!("{}:{}-{}", str::from_utf8(sq_1).unwrap(), 0, 2).as_bytes())
1737 .expect("Expected successful fetch.");
1738 for (i, record) in bam.records().enumerate() {
1739 let rec = record.expect("Expected valid record");
1740
1741 println!("{}", str::from_utf8(rec.qname()).unwrap());
1742 assert_eq!(rec.qname(), names[i]);
1743 assert_eq!(rec.flags(), flags[i]);
1744 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1745 assert_eq!(*rec.cigar(), cigars[i]);
1746 let qual: Vec<u8> = quals[i].iter().map(|&q| q - 33).collect();
1748 assert_eq!(rec.qual(), &qual[..]);
1749 assert_eq!(rec.aux(b"NotAvailableAux"), Err(Error::BamAuxTagNotFound));
1750 }
1751
1752 bam.fetch(format!("{}:{}-{}", str::from_utf8(sq_2).unwrap(), 1, 1).as_bytes())
1754 .expect("Expected successful fetch.");
1755 assert!(bam.records().count() == 0);
1756
1757 bam.fetch(0).expect("Expected successful fetch.");
1759 assert!(bam.records().count() == 6);
1760 bam.fetch(0u32).expect("Expected successful fetch.");
1762 assert!(bam.records().count() == 6);
1763
1764 bam.fetch(sq_1).expect("Expected successful fetch.");
1766 assert!(bam.records().count() == 6);
1767 bam.fetch(str::from_utf8(sq_1).unwrap())
1769 .expect("Expected successful fetch.");
1770 assert!(bam.records().count() == 6);
1771
1772 bam.fetch(FetchDefinition::All)
1774 .expect("Expected successful fetch.");
1775 assert!(bam.records().count() == 6);
1776
1777 bam.fetch(".").expect("Expected successful fetch.");
1779 assert!(bam.records().count() == 6);
1780
1781 bam.fetch(FetchDefinition::Unmapped)
1783 .expect("Expected successful fetch.");
1784 assert_eq!(bam.records().count(), 1); bam.fetch("*").expect("Expected successful fetch.");
1787 assert_eq!(bam.records().count(), 1); }
1789
1790 #[test]
1791 fn test_read_indexed() {
1792 let bam = IndexedReader::from_path("test/test.bam").expect("Expected valid index.");
1793 _test_read_indexed_common(bam);
1794 }
1795
1796 #[test]
1797 fn test_read_indexed_different_index_name() {
1798 let bam = IndexedReader::from_path_and_index(
1799 &"test/test_different_index_name.bam",
1800 &"test/test.bam.bai",
1801 )
1802 .expect("Expected valid index.");
1803 _test_read_indexed_common(bam);
1804 }
1805
1806 #[test]
1807 fn test_set_record() {
1808 let (names, _, seqs, quals, cigars) = gold();
1809
1810 let mut rec = record::Record::new();
1811 rec.set_reverse();
1812 rec.set(names[0], Some(&cigars[0]), seqs[0], quals[0]);
1813 rec.push_aux(b"NM", Aux::I32(15)).unwrap();
1816
1817 assert_eq!(rec.qname(), names[0]);
1818 assert_eq!(*rec.cigar(), cigars[0]);
1819 assert_eq!(rec.seq().as_bytes(), seqs[0]);
1820 assert_eq!(rec.qual(), quals[0]);
1821 assert!(rec.is_reverse());
1822 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1823 }
1824
1825 #[test]
1826 fn test_set_repeated() {
1827 let mut rec = Record::new();
1828 rec.set(
1829 b"123",
1830 Some(&CigarString(vec![Cigar::Match(3)])),
1831 b"AAA",
1832 b"III",
1833 );
1834 rec.push_aux(b"AS", Aux::I32(12345)).unwrap();
1835 assert_eq!(rec.qname(), b"123");
1836 assert_eq!(rec.seq().as_bytes(), b"AAA");
1837 assert_eq!(rec.qual(), b"III");
1838 assert_eq!(rec.aux(b"AS").unwrap(), Aux::I32(12345));
1839
1840 rec.set(
1841 b"1234",
1842 Some(&CigarString(vec![Cigar::SoftClip(1), Cigar::Match(3)])),
1843 b"AAAA",
1844 b"IIII",
1845 );
1846 assert_eq!(rec.qname(), b"1234");
1847 assert_eq!(rec.seq().as_bytes(), b"AAAA");
1848 assert_eq!(rec.qual(), b"IIII");
1849 assert_eq!(rec.aux(b"AS").unwrap(), Aux::I32(12345));
1850
1851 rec.set(
1852 b"12",
1853 Some(&CigarString(vec![Cigar::Match(2)])),
1854 b"AA",
1855 b"II",
1856 );
1857 assert_eq!(rec.qname(), b"12");
1858 assert_eq!(rec.seq().as_bytes(), b"AA");
1859 assert_eq!(rec.qual(), b"II");
1860 assert_eq!(rec.aux(b"AS").unwrap(), Aux::I32(12345));
1861 }
1862
1863 #[test]
1864 fn test_set_qname() {
1865 let (names, _, seqs, quals, cigars) = gold();
1866
1867 assert!(names[0] != names[1]);
1868
1869 for i in 0..names.len() {
1870 let mut rec = record::Record::new();
1871 rec.set(names[i], Some(&cigars[i]), seqs[i], quals[i]);
1872 rec.push_aux(b"NM", Aux::I32(15)).unwrap();
1873
1874 assert_eq!(rec.qname(), names[i]);
1875 assert_eq!(*rec.cigar(), cigars[i]);
1876 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1877 assert_eq!(rec.qual(), quals[i]);
1878 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1879
1880 assert!(rec.qname()[0] != b'X');
1882 rec.set_qname(b"X");
1883 assert_eq!(rec.qname(), b"X");
1884
1885 let mut longer_name = names[i].to_owned().clone();
1887 let extension = b"BuffaloBUffaloBUFFaloBUFFAloBUFFALoBUFFALO";
1888 longer_name.extend(extension.iter());
1889 rec.set_qname(&longer_name);
1890
1891 assert_eq!(rec.qname(), longer_name.as_slice());
1892 assert_eq!(*rec.cigar(), cigars[i]);
1893 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1894 assert_eq!(rec.qual(), quals[i]);
1895 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1896
1897 let shorter_name = b"42";
1899 rec.set_qname(shorter_name);
1900
1901 assert_eq!(rec.qname(), shorter_name);
1902 assert_eq!(*rec.cigar(), cigars[i]);
1903 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1904 assert_eq!(rec.qual(), quals[i]);
1905 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1906
1907 rec.set_qname(b"");
1909
1910 assert_eq!(rec.qname(), b"");
1911 assert_eq!(*rec.cigar(), cigars[i]);
1912 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1913 assert_eq!(rec.qual(), quals[i]);
1914 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1915 }
1916 }
1917
1918 #[test]
1919 fn test_set_qname2() {
1920 let mut _header = Header::new();
1921 _header.push_record(
1922 HeaderRecord::new(b"SQ")
1923 .push_tag(b"SN", "1")
1924 .push_tag(b"LN", 10000000),
1925 );
1926 let header = HeaderView::from_header(&_header);
1927
1928 let line =
1929 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";
1930
1931 let mut rec = Record::from_sam(&header, line).unwrap();
1932 assert_eq!(rec.qname(), b"blah1");
1933 rec.set_qname(b"r0");
1934 assert_eq!(rec.qname(), b"r0");
1935 }
1936
1937 #[test]
1938 fn test_set_cigar() {
1939 let (names, _, seqs, quals, cigars) = gold();
1940
1941 assert!(names[0] != names[1]);
1942
1943 for i in 0..names.len() {
1944 let mut rec = record::Record::new();
1945 rec.set(names[i], Some(&cigars[i]), seqs[i], quals[i]);
1946 rec.push_aux(b"NM", Aux::I32(15)).unwrap();
1947
1948 assert_eq!(rec.qname(), names[i]);
1949 assert_eq!(*rec.cigar(), cigars[i]);
1950 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1951 assert_eq!(rec.qual(), quals[i]);
1952 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1953
1954 let new_cigar = CigarString(vec![Cigar::Match(rec.seq_len() as u32)]);
1956 assert_ne!(*rec.cigar(), new_cigar);
1957 rec.set_cigar(Some(&new_cigar));
1958 assert_eq!(*rec.cigar(), new_cigar);
1959
1960 assert_eq!(rec.qname(), names[i]);
1961 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1962 assert_eq!(rec.qual(), quals[i]);
1963 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1964
1965 let new_cigar = (0..rec.seq_len())
1967 .map(|i| {
1968 if i % 2 == 0 {
1969 Cigar::Match(1)
1970 } else {
1971 Cigar::Ins(1)
1972 }
1973 })
1974 .collect::<Vec<_>>();
1975 let new_cigar = CigarString(new_cigar);
1976 assert_ne!(*rec.cigar(), new_cigar);
1977 rec.set_cigar(Some(&new_cigar));
1978 assert_eq!(*rec.cigar(), new_cigar);
1979
1980 assert_eq!(rec.qname(), names[i]);
1981 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1982 assert_eq!(rec.qual(), quals[i]);
1983 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1984
1985 let new_cigar = CigarString(Vec::new());
1987 assert_ne!(*rec.cigar(), new_cigar);
1988 rec.set_cigar(None);
1989 assert_eq!(*rec.cigar(), new_cigar);
1990
1991 assert_eq!(rec.qname(), names[i]);
1992 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1993 assert_eq!(rec.qual(), quals[i]);
1994 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1995 }
1996 }
1997
1998 #[test]
1999 fn test_remove_aux() {
2000 let mut bam = Reader::from_path(Path::new("test/test.bam")).expect("Error opening file.");
2001
2002 for record in bam.records() {
2003 let mut rec = record.expect("Expected valid record");
2004
2005 if rec.aux(b"XS").is_ok() {
2006 rec.remove_aux(b"XS").unwrap();
2007 }
2008
2009 if rec.aux(b"YT").is_ok() {
2010 rec.remove_aux(b"YT").unwrap();
2011 }
2012
2013 assert_eq!(rec.remove_aux(b"X"), Err(Error::BamAuxStringError));
2014 assert_eq!(rec.remove_aux(b"ab"), Err(Error::BamAuxTagNotFound));
2015
2016 assert_eq!(rec.aux(b"XS"), Err(Error::BamAuxTagNotFound));
2017 assert_eq!(rec.aux(b"YT"), Err(Error::BamAuxTagNotFound));
2018 }
2019 }
2020
2021 #[test]
2022 fn test_write() {
2023 let (names, _, seqs, quals, cigars) = gold();
2024
2025 let tmp = tempfile::Builder::new()
2026 .prefix("rust-htslib")
2027 .tempdir()
2028 .expect("Cannot create temp dir");
2029 let bampath = tmp.path().join("test.bam");
2030 println!("{:?}", bampath);
2031 {
2032 let mut bam = Writer::from_path(
2033 &bampath,
2034 Header::new().push_record(
2035 HeaderRecord::new(b"SQ")
2036 .push_tag(b"SN", "chr1")
2037 .push_tag(b"LN", 15072423),
2038 ),
2039 Format::Bam,
2040 )
2041 .expect("Error opening file.");
2042
2043 for i in 0..names.len() {
2044 let mut rec = record::Record::new();
2045 rec.set(names[i], Some(&cigars[i]), seqs[i], quals[i]);
2046 rec.push_aux(b"NM", Aux::I32(15)).unwrap();
2047
2048 bam.write(&rec).expect("Failed to write record.");
2049 }
2050 }
2051
2052 {
2053 let mut bam = Reader::from_path(bampath).expect("Error opening file.");
2054
2055 for i in 0..names.len() {
2056 let mut rec = record::Record::new();
2057 if let Some(r) = bam.read(&mut rec) {
2058 r.expect("Failed to read record.");
2059 };
2060
2061 assert_eq!(rec.qname(), names[i]);
2062 assert_eq!(*rec.cigar(), cigars[i]);
2063 assert_eq!(rec.seq().as_bytes(), seqs[i]);
2064 assert_eq!(rec.qual(), quals[i]);
2065 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
2066 }
2067 }
2068
2069 tmp.close().expect("Failed to delete temp dir");
2070 }
2071
2072 #[test]
2073 fn test_write_threaded() {
2074 let (names, _, seqs, quals, cigars) = gold();
2075
2076 let tmp = tempfile::Builder::new()
2077 .prefix("rust-htslib")
2078 .tempdir()
2079 .expect("Cannot create temp dir");
2080 let bampath = tmp.path().join("test.bam");
2081 println!("{:?}", bampath);
2082 {
2083 let mut bam = Writer::from_path(
2084 &bampath,
2085 Header::new().push_record(
2086 HeaderRecord::new(b"SQ")
2087 .push_tag(b"SN", "chr1")
2088 .push_tag(b"LN", 15072423),
2089 ),
2090 Format::Bam,
2091 )
2092 .expect("Error opening file.");
2093 bam.set_threads(4).unwrap();
2094
2095 for i in 0..10000 {
2096 let mut rec = record::Record::new();
2097 let idx = i % names.len();
2098 rec.set(names[idx], Some(&cigars[idx]), seqs[idx], quals[idx]);
2099 rec.push_aux(b"NM", Aux::I32(15)).unwrap();
2100 rec.set_pos(i as i64);
2101
2102 bam.write(&rec).expect("Failed to write record.");
2103 }
2104 }
2105
2106 {
2107 let mut bam = Reader::from_path(bampath).expect("Error opening file.");
2108
2109 for (i, _rec) in bam.records().enumerate() {
2110 let idx = i % names.len();
2111
2112 let rec = _rec.expect("Failed to read record.");
2113
2114 assert_eq!(rec.pos(), i as i64);
2115 assert_eq!(rec.qname(), names[idx]);
2116 assert_eq!(*rec.cigar(), cigars[idx]);
2117 assert_eq!(rec.seq().as_bytes(), seqs[idx]);
2118 assert_eq!(rec.qual(), quals[idx]);
2119 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
2120 }
2121 }
2122
2123 tmp.close().expect("Failed to delete temp dir");
2124 }
2125
2126 #[test]
2127 fn test_write_shared_tpool() {
2128 let (names, _, seqs, quals, cigars) = gold();
2129
2130 let tmp = tempfile::Builder::new()
2131 .prefix("rust-htslib")
2132 .tempdir()
2133 .expect("Cannot create temp dir");
2134 let bampath1 = tmp.path().join("test1.bam");
2135 let bampath2 = tmp.path().join("test2.bam");
2136
2137 {
2138 let (mut bam1, mut bam2) = {
2139 let pool = crate::tpool::ThreadPool::new(4).unwrap();
2140
2141 let mut bam1 = Writer::from_path(
2142 &bampath1,
2143 Header::new().push_record(
2144 HeaderRecord::new(b"SQ")
2145 .push_tag(b"SN", "chr1")
2146 .push_tag(b"LN", 15072423),
2147 ),
2148 Format::Bam,
2149 )
2150 .expect("Error opening file.");
2151
2152 let mut bam2 = Writer::from_path(
2153 &bampath2,
2154 Header::new().push_record(
2155 HeaderRecord::new(b"SQ")
2156 .push_tag(b"SN", "chr1")
2157 .push_tag(b"LN", 15072423),
2158 ),
2159 Format::Bam,
2160 )
2161 .expect("Error opening file.");
2162
2163 bam1.set_thread_pool(&pool).unwrap();
2164 bam2.set_thread_pool(&pool).unwrap();
2165 (bam1, bam2)
2166 };
2167
2168 for i in 0..10000 {
2169 let mut rec = record::Record::new();
2170 let idx = i % names.len();
2171 rec.set(names[idx], Some(&cigars[idx]), seqs[idx], quals[idx]);
2172 rec.push_aux(b"NM", Aux::I32(15)).unwrap();
2173 rec.set_pos(i as i64);
2174
2175 bam1.write(&rec).expect("Failed to write record.");
2176 bam2.write(&rec).expect("Failed to write record.");
2177 }
2178 }
2179
2180 {
2181 let pool = crate::tpool::ThreadPool::new(2).unwrap();
2182
2183 for p in [bampath1, bampath2] {
2184 let mut bam = Reader::from_path(p).expect("Error opening file.");
2185 bam.set_thread_pool(&pool).unwrap();
2186
2187 for (i, _rec) in bam.iter_chunk(None, None).enumerate() {
2188 let idx = i % names.len();
2189
2190 let rec = _rec.expect("Failed to read record.");
2191
2192 assert_eq!(rec.pos(), i as i64);
2193 assert_eq!(rec.qname(), names[idx]);
2194 assert_eq!(*rec.cigar(), cigars[idx]);
2195 assert_eq!(rec.seq().as_bytes(), seqs[idx]);
2196 assert_eq!(rec.qual(), quals[idx]);
2197 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
2198 }
2199 }
2200 }
2201
2202 tmp.close().expect("Failed to delete temp dir");
2203 }
2204
2205 #[test]
2206 fn test_copy_template() {
2207 let tmp = tempfile::Builder::new()
2211 .prefix("rust-htslib")
2212 .tempdir()
2213 .expect("Cannot create temp dir");
2214 let bampath = tmp.path().join("test.bam");
2215 println!("{:?}", bampath);
2216
2217 let mut input_bam = Reader::from_path("test/test.bam").expect("Error opening file.");
2218
2219 {
2220 let mut bam = Writer::from_path(
2221 &bampath,
2222 &Header::from_template(input_bam.header()),
2223 Format::Bam,
2224 )
2225 .expect("Error opening file.");
2226
2227 for rec in input_bam.records() {
2228 bam.write(&rec.unwrap()).expect("Failed to write record.");
2229 }
2230 }
2231
2232 {
2233 let copy_bam = Reader::from_path(bampath).expect("Error opening file.");
2234
2235 assert_eq!(input_bam.header().as_bytes(), copy_bam.header().as_bytes());
2237 }
2238
2239 tmp.close().expect("Failed to delete temp dir");
2240 }
2241
2242 #[test]
2243 fn test_pileup() {
2244 let (_, _, seqs, quals, _) = gold();
2245
2246 let mut bam = Reader::from_path("test/test.bam").expect("Error opening file.");
2247 let pileups = bam.pileup();
2248 for pileup in pileups.take(26) {
2249 let _pileup = pileup.expect("Expected successful pileup.");
2250 let pos = _pileup.pos() as usize;
2251 assert_eq!(_pileup.depth(), 6);
2252 assert!(_pileup.tid() == 0);
2253 for (i, a) in _pileup.alignments().enumerate() {
2254 assert_eq!(a.indel(), pileup::Indel::None);
2255 let qpos = a.qpos().unwrap();
2256 assert_eq!(qpos, pos - 1);
2257 assert_eq!(a.record().seq()[qpos], seqs[i][qpos]);
2258 assert_eq!(a.record().qual()[qpos], quals[i][qpos] - 33);
2259 }
2260 }
2261 }
2262
2263 #[test]
2264 fn test_idx_pileup() {
2265 let mut bam = IndexedReader::from_path("test/test.bam").expect("Error opening file.");
2266 for pileup in bam.pileup() {
2268 pileup.unwrap();
2269 }
2270 let tid = bam.header().tid(b"CHROMOSOME_I").unwrap();
2272 bam.fetch((tid, 0, 5)).unwrap();
2273 for p in bam.pileup() {
2274 println!("{}", p.unwrap().pos())
2275 }
2276 }
2277
2278 #[test]
2279 fn parse_from_sam() {
2280 use std::fs::File;
2281 use std::io::Read;
2282
2283 let bamfile = "./test/bam2sam_test.bam";
2284 let samfile = "./test/bam2sam_expected.sam";
2285
2286 let mut rdr = Reader::from_path(bamfile).unwrap();
2288 let bam_recs: Vec<Record> = rdr.records().map(|v| v.unwrap()).collect();
2289
2290 let mut sam = Vec::new();
2291 assert!(File::open(samfile).unwrap().read_to_end(&mut sam).is_ok());
2292
2293 let sam_recs: Vec<Record> = sam
2294 .split(|x| *x == b'\n')
2295 .filter(|x| !x.is_empty() && x[0] != b'@')
2296 .map(|line| Record::from_sam(rdr.header(), line).unwrap())
2297 .collect();
2298
2299 for (b1, s1) in bam_recs.iter().zip(sam_recs.iter()) {
2300 assert!(b1 == s1);
2301 }
2302 }
2303
2304 #[test]
2305 fn test_cigar_modes() {
2306 let (_, _, _, _, cigars) = gold();
2309 let mut bam = Reader::from_path(Path::new("test/test.bam")).expect("Error opening file.");
2310
2311 for (i, record) in bam.records().enumerate() {
2312 let rec = record.expect("Expected valid record");
2313
2314 let cigar = rec.cigar();
2315 assert_eq!(*cigar, cigars[i]);
2316 }
2317
2318 for (i, record) in bam.records().enumerate() {
2319 let mut rec = record.expect("Expected valid record");
2320 rec.cache_cigar();
2321
2322 let cigar = rec.cigar_cached().unwrap();
2323 assert_eq!(**cigar, cigars[i]);
2324
2325 let cigar = rec.cigar();
2326 assert_eq!(*cigar, cigars[i]);
2327 }
2328 }
2329
2330 #[test]
2331 fn test_read_cram() {
2332 let cram_path = "./test/test_cram.cram";
2333 let bam_path = "./test/test_cram.bam";
2334 let ref_path = "./test/test_cram.fa";
2335
2336 let mut cram_reader = Reader::from_path(cram_path).unwrap();
2338 cram_reader.set_reference(ref_path).unwrap();
2339 let cram_records: Vec<Record> = cram_reader.records().map(|v| v.unwrap()).collect();
2340
2341 let mut bam_reader = Reader::from_path(bam_path).unwrap();
2343 let bam_records: Vec<Record> = bam_reader.records().map(|v| v.unwrap()).collect();
2344
2345 compare_inner_bam_cram_records(&cram_records, &bam_records);
2346 }
2347
2348 #[test]
2349 fn test_write_cram() {
2350 let bam_path = "./test/test_cram.bam";
2352 let ref_path = "./test/test_cram.fa";
2353 let mut bam_reader = Reader::from_path(bam_path).unwrap();
2354 let bam_records: Vec<Record> = bam_reader.records().map(|v| v.unwrap()).collect();
2355
2356 let tmp = tempfile::Builder::new()
2358 .prefix("rust-htslib")
2359 .tempdir()
2360 .expect("Cannot create temp dir");
2361 let cram_path = tmp.path().join("test.cram");
2362
2363 {
2365 let mut header = Header::new();
2366 header.push_record(
2367 HeaderRecord::new(b"HD")
2368 .push_tag(b"VN", "1.5")
2369 .push_tag(b"SO", "coordinate"),
2370 );
2371 header.push_record(
2372 HeaderRecord::new(b"SQ")
2373 .push_tag(b"SN", "chr1")
2374 .push_tag(b"LN", 120)
2375 .push_tag(b"M5", "20a9a0fb770814e6c5e49946750f9724")
2376 .push_tag(b"UR", "test/test_cram.fa"),
2377 );
2378 header.push_record(
2379 HeaderRecord::new(b"SQ")
2380 .push_tag(b"SN", "chr2")
2381 .push_tag(b"LN", 120)
2382 .push_tag(b"M5", "7a2006ccca94ea92b6dae5997e1b0d70")
2383 .push_tag(b"UR", "test/test_cram.fa"),
2384 );
2385 header.push_record(
2386 HeaderRecord::new(b"SQ")
2387 .push_tag(b"SN", "chr3")
2388 .push_tag(b"LN", 120)
2389 .push_tag(b"M5", "a66b336bfe3ee8801c744c9545c87e24")
2390 .push_tag(b"UR", "test/test_cram.fa"),
2391 );
2392
2393 let mut cram_writer = Writer::from_path(&cram_path, &header, Format::Cram)
2394 .expect("Error opening CRAM file.");
2395 cram_writer.set_reference(ref_path).unwrap();
2396
2397 for rec in bam_records.iter() {
2399 cram_writer
2400 .write(rec)
2401 .expect("Faied to write record to CRAM.");
2402 }
2403 }
2404
2405 {
2407 let mut cram_reader = Reader::from_path(cram_path).unwrap();
2409 cram_reader.set_reference(ref_path).unwrap();
2410 let cram_records: Vec<Record> = cram_reader.records().map(|v| v.unwrap()).collect();
2411
2412 compare_inner_bam_cram_records(&cram_records, &bam_records);
2414 }
2415
2416 tmp.close().expect("Failed to delete temp dir");
2417 }
2418
2419 #[test]
2420 fn test_compression_level_conversion() {
2421 assert_eq!(CompressionLevel::Uncompressed.convert().unwrap(), 0);
2423 assert_eq!(CompressionLevel::Fastest.convert().unwrap(), 1);
2424 assert_eq!(CompressionLevel::Maximum.convert().unwrap(), 9);
2425
2426 for level in 0..=9 {
2428 assert_eq!(CompressionLevel::Level(level).convert().unwrap(), level);
2429 }
2430 assert!(CompressionLevel::Level(10).convert().is_err());
2432 }
2433
2434 #[test]
2435 fn test_write_compression() {
2436 let tmp = tempfile::Builder::new()
2437 .prefix("rust-htslib")
2438 .tempdir()
2439 .expect("Cannot create temp dir");
2440 let input_bam_path = "test/test.bam";
2441
2442 let levels_to_test = vec![
2444 CompressionLevel::Maximum,
2445 CompressionLevel::Level(6),
2446 CompressionLevel::Fastest,
2447 CompressionLevel::Uncompressed,
2448 ];
2449 let file_sizes: Vec<_> = levels_to_test
2450 .iter()
2451 .map(|level| {
2452 let output_bam_path = tmp.path().join("test.bam");
2453 {
2454 let mut reader = Reader::from_path(input_bam_path).unwrap();
2455 let header = Header::from_template(reader.header());
2456 let mut writer =
2457 Writer::from_path(&output_bam_path, &header, Format::Bam).unwrap();
2458 writer.set_compression_level(*level).unwrap();
2459 for record in reader.records() {
2460 let r = record.unwrap();
2461 writer.write(&r).unwrap();
2462 }
2463 }
2464 fs::metadata(output_bam_path).unwrap().len()
2465 })
2466 .collect();
2467
2468 println!("testing compression leves: {:?}", levels_to_test);
2470 println!("got compressed sizes: {:?}", file_sizes);
2471
2472 #[cfg(feature = "libdeflate")]
2475 assert!(file_sizes[1..].windows(2).all(|size| size[0] <= size[1]));
2476
2477 #[cfg(not(feature = "libdeflate"))]
2478 assert!(file_sizes.windows(2).all(|size| size[0] <= size[1]));
2479
2480 tmp.close().expect("Failed to delete temp dir");
2481 }
2482
2483 #[test]
2484 fn test_bam_fails_on_vcf() {
2485 let bam_path = "./test/test_left.vcf";
2486 let bam_reader = Reader::from_path(bam_path);
2487 assert!(bam_reader.is_err());
2488 }
2489
2490 #[test]
2491 fn test_indexde_bam_fails_on_vcf() {
2492 let bam_path = "./test/test_left.vcf";
2493 let bam_reader = IndexedReader::from_path(bam_path);
2494 assert!(bam_reader.is_err());
2495 }
2496
2497 #[test]
2498 fn test_bam_fails_on_toml() {
2499 let bam_path = "./Cargo.toml";
2500 let bam_reader = Reader::from_path(bam_path);
2501 assert!(bam_reader.is_err());
2502 }
2503
2504 #[test]
2505 fn test_sam_writer_example() {
2506 fn from_bam_with_filter<F>(bamfile: &str, samfile: &str, f: F) -> bool
2507 where
2508 F: Fn(&record::Record) -> Option<bool>,
2509 {
2510 let mut bam_reader = Reader::from_path(bamfile).unwrap(); let header = header::Header::from_template(bam_reader.header());
2512 let mut sam_writer = Writer::from_path(samfile, &header, Format::Sam).unwrap();
2513 for record in bam_reader.records() {
2514 if record.is_err() {
2515 return false;
2516 }
2517 let parsed = record.unwrap();
2518 match f(&parsed) {
2519 None => return true,
2520 Some(false) => {}
2521 Some(true) => {
2522 if sam_writer.write(&parsed).is_err() {
2523 return false;
2524 }
2525 }
2526 }
2527 }
2528 true
2529 }
2530 use std::fs::File;
2531 use std::io::Read;
2532 let bamfile = "./test/bam2sam_test.bam";
2533 let samfile = "./test/bam2sam_out.sam";
2534 let expectedfile = "./test/bam2sam_expected.sam";
2535 let result = from_bam_with_filter(bamfile, samfile, |_| Some(true));
2536 assert!(result);
2537 let mut expected = Vec::new();
2538 let mut written = Vec::new();
2539 assert!(File::open(expectedfile)
2540 .unwrap()
2541 .read_to_end(&mut expected)
2542 .is_ok());
2543 assert!(File::open(samfile)
2544 .unwrap()
2545 .read_to_end(&mut written)
2546 .is_ok());
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 #[test]
3173 fn test_slow_idxstats_cram_unmapped() {
3174 let mut reader = IndexedReader::from_path("test/test_cram_unmapped.cram").unwrap();
3175 reader.set_reference("test/test_cram.fa").unwrap();
3176 let expected = vec![
3177 (0, 120, 2, 0),
3178 (1, 120, 2, 0),
3179 (2, 120, 2, 0),
3180 (-1, 0, 0, 2),
3181 ];
3182 let actual = reader.index_stats().unwrap();
3183 assert_eq!(expected, actual);
3184 }
3185
3186 }