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