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: Option<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: Some(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: Some(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.as_ref().unwrap()
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.take() {
1036 htslib::hts_itr_destroy(itr);
1037 }
1038
1039 drop(self.idx.take());
1042 htslib::hts_close(self.htsfile);
1043 }
1044 }
1045}
1046
1047#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1048pub enum Format {
1049 Sam,
1050 Bam,
1051 Cram,
1052}
1053
1054impl Format {
1055 fn write_mode(self) -> &'static [u8] {
1056 match self {
1057 Format::Sam => b"w",
1058 Format::Bam => b"wb",
1059 Format::Cram => b"wc",
1060 }
1061 }
1062}
1063
1064#[derive(Debug)]
1066pub struct Writer {
1067 f: *mut htslib::htsFile,
1068 header: Arc<HeaderView>,
1069 tpool: Option<ThreadPool>,
1070}
1071
1072unsafe impl Send for Writer {}
1073
1074impl Writer {
1075 pub fn from_path<P: AsRef<Path>>(
1083 path: P,
1084 header: &header::Header,
1085 format: Format,
1086 ) -> Result<Self> {
1087 Self::new(&path_as_bytes(path, false)?, format.write_mode(), header)
1088 }
1089
1090 pub fn from_stdout(header: &header::Header, format: Format) -> Result<Self> {
1097 Self::new(b"-", format.write_mode(), header)
1098 }
1099
1100 fn new(path: &[u8], mode: &[u8], header: &header::Header) -> Result<Self> {
1108 let f = hts_open(path, mode)?;
1109
1110 let header_record = unsafe {
1115 let mut header_string = header.to_bytes();
1116 if !header_string.is_empty() && header_string[header_string.len() - 1] != b'\n' {
1117 header_string.push(b'\n');
1118 }
1119 let l_text = header_string.len();
1120 let text = ::libc::malloc(l_text + 1);
1121 libc::memset(text, 0, l_text + 1);
1122 libc::memcpy(
1123 text,
1124 header_string.as_ptr() as *const ::libc::c_void,
1125 header_string.len(),
1126 );
1127
1128 let rec = htslib::sam_hdr_parse(l_text + 1, text as *const c_char);
1130
1131 (*rec).text = text as *mut c_char;
1132 (*rec).l_text = l_text;
1133 rec
1134 };
1135
1136 unsafe {
1137 htslib::sam_hdr_write(f, header_record);
1138 }
1139
1140 Ok(Writer {
1141 f,
1142 header: Arc::new(HeaderView::new(header_record)),
1143 tpool: None,
1144 })
1145 }
1146
1147 pub fn set_threads(&mut self, n_threads: usize) -> Result<()> {
1154 unsafe { set_threads(self.f, n_threads) }
1155 }
1156
1157 pub fn set_thread_pool(&mut self, tpool: &ThreadPool) -> Result<()> {
1165 unsafe { set_thread_pool(self.f, tpool)? }
1166 self.tpool = Some(tpool.clone());
1167 Ok(())
1168 }
1169
1170 pub fn write(&mut self, record: &record::Record) -> Result<()> {
1176 if unsafe { htslib::sam_write1(self.f, self.header.inner(), record.inner_ptr()) } == -1 {
1177 Err(Error::WriteRecord)
1178 } else {
1179 Ok(())
1180 }
1181 }
1182
1183 pub fn header(&self) -> &HeaderView {
1185 &self.header
1186 }
1187
1188 pub fn set_reference<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
1194 unsafe { set_fai_filename(self.f, path) }
1195 }
1196
1197 pub fn set_compression_level(&mut self, compression_level: CompressionLevel) -> Result<()> {
1203 let level = compression_level.convert()?;
1204 match unsafe {
1205 htslib::hts_set_opt(
1206 self.f,
1207 htslib::hts_fmt_option_HTS_OPT_COMPRESSION_LEVEL,
1208 level,
1209 )
1210 } {
1211 0 => Ok(()),
1212 _ => Err(Error::BamInvalidCompressionLevel { level }),
1213 }
1214 }
1215}
1216
1217#[derive(Debug, Clone, Copy)]
1224pub enum CompressionLevel {
1225 Uncompressed,
1226 Fastest,
1227 Maximum,
1228 Level(u32),
1229}
1230
1231impl CompressionLevel {
1232 fn convert(self) -> Result<u32> {
1234 match self {
1235 CompressionLevel::Uncompressed => Ok(0),
1236 CompressionLevel::Fastest => Ok(1),
1237 CompressionLevel::Maximum => Ok(9),
1238 CompressionLevel::Level(i @ 0..=9) => Ok(i),
1239 CompressionLevel::Level(i) => Err(Error::BamInvalidCompressionLevel { level: i }),
1240 }
1241 }
1242}
1243
1244impl Drop for Writer {
1245 fn drop(&mut self) {
1246 unsafe {
1247 htslib::hts_close(self.f);
1248 }
1249 }
1250}
1251
1252#[derive(Debug)]
1254pub struct Records<'a, R: Read> {
1255 reader: &'a mut R,
1256}
1257
1258impl<R: Read> Iterator for Records<'_, R> {
1259 type Item = Result<record::Record>;
1260
1261 fn next(&mut self) -> Option<Result<record::Record>> {
1262 let mut record = record::Record::new();
1263 match self.reader.read(&mut record) {
1264 None => None,
1265 Some(Ok(_)) => Some(Ok(record)),
1266 Some(Err(err)) => Some(Err(err)),
1267 }
1268 }
1269}
1270
1271#[derive(Debug)]
1275pub struct RcRecords<'a, R: Read> {
1276 reader: &'a mut R,
1277 record: Rc<record::Record>,
1278}
1279
1280impl<R: Read> Iterator for RcRecords<'_, R> {
1281 type Item = Result<Rc<record::Record>>;
1282
1283 fn next(&mut self) -> Option<Self::Item> {
1284 let record = match Rc::get_mut(&mut self.record) {
1285 Some(x) => x,
1287 None => {
1288 self.record = Rc::new(record::Record::new());
1289 Rc::get_mut(&mut self.record).unwrap()
1290 }
1291 };
1292
1293 match self.reader.read(record) {
1294 None => None,
1295 Some(Ok(_)) => Some(Ok(Rc::clone(&self.record))),
1296 Some(Err(err)) => Some(Err(err)),
1297 }
1298 }
1299}
1300
1301pub struct ChunkIterator<'a, R: Read> {
1303 reader: &'a mut R,
1304 end: Option<i64>,
1305}
1306
1307impl<R: Read> Iterator for ChunkIterator<'_, R> {
1308 type Item = Result<record::Record>;
1309 fn next(&mut self) -> Option<Result<record::Record>> {
1310 if let Some(pos) = self.end {
1311 if self.reader.tell() >= pos {
1312 return None;
1313 }
1314 }
1315 let mut record = record::Record::new();
1316 match self.reader.read(&mut record) {
1317 None => None,
1318 Some(Ok(_)) => Some(Ok(record)),
1319 Some(Err(err)) => Some(Err(err)),
1320 }
1321 }
1322}
1323
1324fn hts_open(path: &[u8], mode: &[u8]) -> Result<*mut htslib::htsFile> {
1326 let cpath = ffi::CString::new(path).unwrap();
1327 let path = str::from_utf8(path).unwrap();
1328 let c_str = ffi::CString::new(mode).unwrap();
1329 let ret = unsafe { htslib::hts_open(cpath.as_ptr(), c_str.as_ptr()) };
1330 if ret.is_null() {
1331 Err(Error::BamOpen {
1332 target: path.to_owned(),
1333 })
1334 } else {
1335 if !mode.contains(&b'w') {
1336 unsafe {
1337 if (*ret).format.format != htslib::htsExactFormat_sam
1340 && (*ret).format.format != htslib::htsExactFormat_bam
1341 && (*ret).format.format != htslib::htsExactFormat_cram
1342 {
1343 return Err(Error::BamOpen {
1344 target: path.to_owned(),
1345 });
1346 }
1347 }
1348 }
1349 Ok(ret)
1350 }
1351}
1352
1353fn itr_next(
1355 htsfile: *mut htslib::htsFile,
1356 itr: *mut htslib::hts_itr_t,
1357 record: *mut htslib::bam1_t,
1358) -> i32 {
1359 unsafe {
1360 htslib::hts_itr_next(
1361 (*htsfile).fp.bgzf,
1362 itr,
1363 record as *mut ::std::os::raw::c_void,
1364 htsfile as *mut ::std::os::raw::c_void,
1365 )
1366 }
1367}
1368
1369#[derive(Debug)]
1370pub struct HeaderView {
1371 inner: *mut htslib::bam_hdr_t,
1372}
1373
1374unsafe impl Send for HeaderView {}
1375unsafe impl Sync for HeaderView {}
1376
1377impl HeaderView {
1378 pub fn from_header(header: &Header) -> Self {
1380 let mut header_string = header.to_bytes();
1381 if !header_string.is_empty() && header_string[header_string.len() - 1] != b'\n' {
1382 header_string.push(b'\n');
1383 }
1384 Self::from_bytes(&header_string)
1385 }
1386
1387 pub fn from_bytes(header_string: &[u8]) -> Self {
1389 let header_record = unsafe {
1390 let l_text = header_string.len();
1391 let text = ::libc::malloc(l_text + 1);
1392 ::libc::memset(text, 0, l_text + 1);
1393 ::libc::memcpy(
1394 text,
1395 header_string.as_ptr() as *const ::libc::c_void,
1396 header_string.len(),
1397 );
1398
1399 let rec = htslib::sam_hdr_parse(l_text + 1, text as *const c_char);
1400 (*rec).text = text as *mut c_char;
1401 (*rec).l_text = l_text;
1402 rec
1403 };
1404
1405 HeaderView::new(header_record)
1406 }
1407
1408 fn new(inner: *mut htslib::bam_hdr_t) -> Self {
1410 HeaderView { inner }
1411 }
1412
1413 #[inline]
1414 pub fn inner(&self) -> &htslib::bam_hdr_t {
1415 unsafe { self.inner.as_ref().unwrap() }
1416 }
1417
1418 #[inline]
1419 pub fn inner_ptr(&self) -> *const htslib::bam_hdr_t {
1421 self.inner
1422 }
1423
1424 #[inline]
1425 pub fn inner_mut(&mut self) -> &mut htslib::bam_hdr_t {
1426 unsafe { self.inner.as_mut().unwrap() }
1427 }
1428
1429 #[inline]
1430 pub fn inner_ptr_mut(&mut self) -> *mut htslib::bam_hdr_t {
1432 self.inner
1433 }
1434
1435 pub fn tid(&self, name: &[u8]) -> Option<u32> {
1436 let c_str = ffi::CString::new(name).expect("Expected valid name.");
1437 let tid = unsafe { htslib::sam_hdr_name2tid(self.inner, c_str.as_ptr()) };
1438 if tid < 0 {
1439 None
1440 } else {
1441 Some(tid as u32)
1442 }
1443 }
1444
1445 pub fn tid2name(&self, tid: u32) -> &[u8] {
1446 let ptr = unsafe { htslib::sam_hdr_tid2name(self.inner, tid as i32) };
1447 if ptr.is_null() {
1448 b""
1449 } else {
1450 unsafe { ffi::CStr::from_ptr(ptr).to_bytes() }
1451 }
1452 }
1453
1454 pub fn target_count(&self) -> u32 {
1455 self.inner().n_targets as u32
1456 }
1457
1458 pub fn target_names(&self) -> Vec<&[u8]> {
1459 let names = unsafe {
1460 slice::from_raw_parts(self.inner().target_name, self.target_count() as usize)
1461 };
1462 names
1463 .iter()
1464 .map(|name| unsafe { ffi::CStr::from_ptr(*name).to_bytes() })
1465 .collect()
1466 }
1467
1468 pub fn target_len(&self, tid: u32) -> Option<u64> {
1469 let inner = unsafe { *self.inner };
1470 if (tid as i32) < inner.n_targets {
1471 let l: &[u32] =
1472 unsafe { slice::from_raw_parts(inner.target_len, inner.n_targets as usize) };
1473 Some(l[tid as usize] as u64)
1474 } else {
1475 None
1476 }
1477 }
1478
1479 pub fn as_bytes(&self) -> &[u8] {
1481 unsafe {
1482 let rebuilt_hdr = htslib::sam_hdr_str(self.inner);
1483 if rebuilt_hdr.is_null() {
1484 return b"";
1485 }
1486 ffi::CStr::from_ptr(rebuilt_hdr).to_bytes()
1487 }
1488 }
1489}
1490
1491impl Clone for HeaderView {
1492 fn clone(&self) -> Self {
1493 HeaderView {
1494 inner: unsafe { htslib::sam_hdr_dup(self.inner) },
1495 }
1496 }
1497}
1498
1499impl Drop for HeaderView {
1500 fn drop(&mut self) {
1501 unsafe {
1502 htslib::sam_hdr_destroy(self.inner);
1503 }
1504 }
1505}
1506
1507#[cfg(test)]
1508mod tests {
1509 use super::header::HeaderRecord;
1510 use super::record::{Aux, Cigar, CigarString};
1511 use super::*;
1512 use std::collections::HashMap;
1513 use std::fs;
1514 use std::path::Path;
1515 use std::str;
1516
1517 type GoldType = (
1518 [&'static [u8]; 6],
1519 [u16; 6],
1520 [&'static [u8]; 6],
1521 [&'static [u8]; 6],
1522 [CigarString; 6],
1523 );
1524 fn gold() -> GoldType {
1525 let names = [
1526 &b"I"[..],
1527 &b"II.14978392"[..],
1528 &b"III"[..],
1529 &b"IV"[..],
1530 &b"V"[..],
1531 &b"VI"[..],
1532 ];
1533 let flags = [16u16, 16u16, 16u16, 16u16, 16u16, 2048u16];
1534 let seqs = [
1535 &b"CCTAGCCCTAACCCTAACCCTAACCCTAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCC\
1536TAAGCCTAAGCCTAAGCCTAA"[..],
1537 &b"CCTAGCCCTAACCCTAACCCTAACCCTAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCC\
1538TAAGCCTAAGCCTAAGCCTAA"[..],
1539 &b"CCTAGCCCTAACCCTAACCCTAACCCTAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCC\
1540TAAGCCTAAGCCTAAGCCTAA"[..],
1541 &b"CCTAGCCCTAACCCTAACCCTAACCCTAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCC\
1542TAAGCCTAAGCCTAAGCCTAA"[..],
1543 &b"CCTAGCCCTAACCCTAACCCTAACCCTAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCCTAAGCC\
1544TAAGCCTAAGCCTAAGCCTAA"[..],
1545 &b"ACTAAGCCTAAGCCTAAGCCTAAGCCAATTATCGATTTCTGAAAAAATTATCGAATTTTCTAGAAATTTTGCAAATTTT\
1546TTCATAAAATTATCGATTTTA"[..],
1547 ];
1548 let quals = [
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 &b"#############################@B?8B?BA@@DDBCDDCBC@CDCDCCCCCCCCCCCCCCCCCCCCCCCCCCCC\
1560CCCCCCCCCCCCCCCCCCC"[..],
1561 ];
1562 let cigars = [
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(1), Cigar::Match(73)]),
1567 CigarString(vec![Cigar::Match(27), Cigar::Del(1), Cigar::Match(73)]),
1568 CigarString(vec![Cigar::Match(27), Cigar::Del(100000), Cigar::Match(73)]),
1569 ];
1570 (names, flags, seqs, quals, cigars)
1571 }
1572
1573 fn compare_inner_bam_cram_records(cram_records: &[Record], bam_records: &[Record]) {
1574 for (c1, b1) in cram_records.iter().zip(bam_records.iter()) {
1576 assert_eq!(c1.cigar(), b1.cigar());
1579 assert_eq!(c1.inner().core.pos, b1.inner().core.pos);
1580 assert_eq!(c1.inner().core.mpos, b1.inner().core.mpos);
1581 assert_eq!(c1.inner().core.mtid, b1.inner().core.mtid);
1582 assert_eq!(c1.inner().core.tid, b1.inner().core.tid);
1583 assert_eq!(c1.inner().core.bin, b1.inner().core.bin);
1584 assert_eq!(c1.inner().core.qual, b1.inner().core.qual);
1585 assert_eq!(c1.inner().core.l_extranul, b1.inner().core.l_extranul);
1586 assert_eq!(c1.inner().core.flag, b1.inner().core.flag);
1587 assert_eq!(c1.inner().core.l_qname, b1.inner().core.l_qname);
1588 assert_eq!(c1.inner().core.n_cigar, b1.inner().core.n_cigar);
1589 assert_eq!(c1.inner().core.l_qseq, b1.inner().core.l_qseq);
1590 assert_eq!(c1.inner().core.isize_, b1.inner().core.isize_);
1591 }
1593 }
1594
1595 #[test]
1596 fn test_read() {
1597 let (names, flags, seqs, quals, cigars) = gold();
1598 let mut bam = Reader::from_path(Path::new("test/test.bam")).expect("Error opening file.");
1599 let del_len = [1, 1, 1, 1, 1, 100000];
1600
1601 for (i, record) in bam.records().enumerate() {
1602 let rec = record.expect("Expected valid record");
1603 assert_eq!(rec.qname(), names[i]);
1604 assert_eq!(rec.flags(), flags[i]);
1605 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1606
1607 let cigar = rec.cigar();
1608 assert_eq!(*cigar, cigars[i]);
1609
1610 let end_pos = cigar.end_pos();
1611 assert_eq!(end_pos, rec.pos() + 100 + del_len[i]);
1612 assert_eq!(
1613 cigar
1614 .read_pos(end_pos as u32 - 10, false, false)
1615 .unwrap()
1616 .unwrap(),
1617 90
1618 );
1619 assert_eq!(
1620 cigar
1621 .read_pos(rec.pos() as u32 + 20, false, false)
1622 .unwrap()
1623 .unwrap(),
1624 20
1625 );
1626 assert_eq!(cigar.read_pos(4000000, false, false).unwrap(), None);
1627 let qual: Vec<u8> = quals[i].iter().map(|&q| q - 33).collect();
1629 assert_eq!(rec.qual(), &qual[..]);
1630 }
1631 }
1632
1633 #[test]
1634 fn test_seek() {
1635 let mut bam = Reader::from_path(Path::new("test/test.bam")).expect("Error opening file.");
1636
1637 let mut names_by_voffset = HashMap::new();
1638
1639 let mut offset = bam.tell();
1640 let mut rec = Record::new();
1641 while let Some(r) = bam.read(&mut rec) {
1642 r.expect("error reading bam");
1643 let qname = str::from_utf8(rec.qname()).unwrap().to_string();
1644 println!("{} {}", offset, qname);
1645 names_by_voffset.insert(offset, qname);
1646 offset = bam.tell();
1647 }
1648
1649 for (offset, qname) in names_by_voffset.iter() {
1650 println!("{} {}", offset, qname);
1651 bam.seek(*offset).unwrap();
1652 if let Some(r) = bam.read(&mut rec) {
1653 r.unwrap();
1654 };
1655 let rec_qname = str::from_utf8(rec.qname()).unwrap().to_string();
1656 assert_eq!(qname, &rec_qname);
1657 }
1658 }
1659
1660 #[test]
1661 fn test_read_sam_header() {
1662 let bam = Reader::from_path("test/test.bam").expect("Error opening file.");
1663
1664 let true_header = "@SQ\tSN:CHROMOSOME_I\tLN:15072423\n@SQ\tSN:CHROMOSOME_II\tLN:15279345\
1665 \n@SQ\tSN:CHROMOSOME_III\tLN:13783700\n@SQ\tSN:CHROMOSOME_IV\tLN:17493793\n@SQ\t\
1666 SN:CHROMOSOME_V\tLN:20924149\n"
1667 .to_string();
1668 let header_text = String::from_utf8(bam.header.as_bytes().to_owned()).unwrap();
1669 assert_eq!(header_text, true_header);
1670 }
1671
1672 #[test]
1673 fn test_read_against_sam() {
1674 let mut bam = Reader::from_path("./test/bam2sam_out.sam").unwrap();
1675 for read in bam.records() {
1676 let _read = read.unwrap();
1677 }
1678 }
1679
1680 fn _test_read_indexed_common(mut bam: IndexedReader) {
1681 let (names, flags, seqs, quals, cigars) = gold();
1682 let sq_1 = b"CHROMOSOME_I";
1683 let sq_2 = b"CHROMOSOME_II";
1684 let tid_1 = bam.header.tid(sq_1).expect("Expected tid.");
1685 let tid_2 = bam.header.tid(sq_2).expect("Expected tid.");
1686 assert!(bam.header.target_len(tid_1).expect("Expected target len.") == 15072423);
1687
1688 bam.fetch((tid_1, 0, 2))
1690 .expect("Expected successful fetch.");
1691 assert!(bam.records().count() == 6);
1692
1693 bam.fetch((tid_1, 0, 2))
1695 .expect("Expected successful fetch.");
1696 for (i, record) in bam.records().enumerate() {
1697 let rec = record.expect("Expected valid record");
1698
1699 println!("{}", str::from_utf8(rec.qname()).unwrap());
1700 assert_eq!(rec.qname(), names[i]);
1701 assert_eq!(rec.flags(), flags[i]);
1702 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1703 assert_eq!(*rec.cigar(), cigars[i]);
1704 let qual: Vec<u8> = quals[i].iter().map(|&q| q - 33).collect();
1706 assert_eq!(rec.qual(), &qual[..]);
1707 assert_eq!(rec.aux(b"X"), Err(Error::BamAuxStringError));
1708 assert_eq!(rec.aux(b"NotAvailableAux"), Err(Error::BamAuxTagNotFound));
1709 }
1710
1711 bam.fetch((tid_2, 1, 1))
1713 .expect("Expected successful fetch.");
1714 assert!(bam.records().count() == 0);
1715
1716 bam.fetch(format!("{}:{}-{}", str::from_utf8(sq_1).unwrap(), 0, 2).as_bytes())
1721 .expect("Expected successful fetch.");
1722 assert!(bam.records().count() == 6);
1723 bam.fetch((str::from_utf8(sq_1).unwrap(), 0_u32, 2_u64))
1725 .expect("Expected successful fetch.");
1726 assert!(bam.records().count() == 6);
1727 bam.fetch((&sq_1[..], 0, 2))
1729 .expect("Expected successful fetch.");
1730 assert!(bam.records().count() == 6);
1731 bam.fetch((sq_1, 0, 2)).expect("Expected successful fetch.");
1733 assert!(bam.records().count() == 6);
1734
1735 bam.fetch((0i32, 0u32, 2i64))
1737 .expect("Expected successful fetch.");
1738 assert!(bam.records().count() == 6);
1739 bam.fetch((0u32, 0u32, 2i64))
1741 .expect("Expected successful fetch.");
1742 assert!(bam.records().count() == 6);
1743
1744 bam.fetch(format!("{}:{}-{}", str::from_utf8(sq_1).unwrap(), 0, 2).as_bytes())
1746 .expect("Expected successful fetch.");
1747 for (i, record) in bam.records().enumerate() {
1748 let rec = record.expect("Expected valid record");
1749
1750 println!("{}", str::from_utf8(rec.qname()).unwrap());
1751 assert_eq!(rec.qname(), names[i]);
1752 assert_eq!(rec.flags(), flags[i]);
1753 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1754 assert_eq!(*rec.cigar(), cigars[i]);
1755 let qual: Vec<u8> = quals[i].iter().map(|&q| q - 33).collect();
1757 assert_eq!(rec.qual(), &qual[..]);
1758 assert_eq!(rec.aux(b"NotAvailableAux"), Err(Error::BamAuxTagNotFound));
1759 }
1760
1761 bam.fetch(format!("{}:{}-{}", str::from_utf8(sq_2).unwrap(), 1, 1).as_bytes())
1763 .expect("Expected successful fetch.");
1764 assert!(bam.records().count() == 0);
1765
1766 bam.fetch(0).expect("Expected successful fetch.");
1768 assert!(bam.records().count() == 6);
1769 bam.fetch(0u32).expect("Expected successful fetch.");
1771 assert!(bam.records().count() == 6);
1772
1773 bam.fetch(sq_1).expect("Expected successful fetch.");
1775 assert!(bam.records().count() == 6);
1776 bam.fetch(str::from_utf8(sq_1).unwrap())
1778 .expect("Expected successful fetch.");
1779 assert!(bam.records().count() == 6);
1780
1781 bam.fetch(FetchDefinition::All)
1783 .expect("Expected successful fetch.");
1784 assert!(bam.records().count() == 6);
1785
1786 bam.fetch(".").expect("Expected successful fetch.");
1788 assert!(bam.records().count() == 6);
1789
1790 bam.fetch(FetchDefinition::Unmapped)
1792 .expect("Expected successful fetch.");
1793 assert_eq!(bam.records().count(), 1); bam.fetch("*").expect("Expected successful fetch.");
1796 assert_eq!(bam.records().count(), 1); }
1798
1799 #[test]
1800 fn test_read_indexed() {
1801 let bam = IndexedReader::from_path("test/test.bam").expect("Expected valid index.");
1802 _test_read_indexed_common(bam);
1803 }
1804
1805 #[test]
1806 fn test_read_indexed_cram() {
1807 let mut reader = IndexedReader::from_path("test/test_cram.cram").unwrap();
1808 reader.set_reference("test/test_cram.fa").unwrap();
1809 reader.fetch(("chr1", 0, 120)).unwrap();
1810
1811 let mut record = Record::new();
1812 reader.read(&mut record).unwrap().unwrap();
1813 assert_eq!(record.qname(), b"chr1.1");
1814
1815 drop(reader);
1816 }
1817
1818 #[test]
1819 fn test_read_indexed_different_index_name() {
1820 let bam = IndexedReader::from_path_and_index(
1821 &"test/test_different_index_name.bam",
1822 &"test/test.bam.bai",
1823 )
1824 .expect("Expected valid index.");
1825 _test_read_indexed_common(bam);
1826 }
1827
1828 #[test]
1829 fn test_set_record() {
1830 let (names, _, seqs, quals, cigars) = gold();
1831
1832 let mut rec = record::Record::new();
1833 rec.set_reverse();
1834 rec.set(names[0], Some(&cigars[0]), seqs[0], quals[0]);
1835 rec.push_aux(b"NM", Aux::I32(15)).unwrap();
1838
1839 assert_eq!(rec.qname(), names[0]);
1840 assert_eq!(*rec.cigar(), cigars[0]);
1841 assert_eq!(rec.seq().as_bytes(), seqs[0]);
1842 assert_eq!(rec.qual(), quals[0]);
1843 assert!(rec.is_reverse());
1844 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1845 }
1846
1847 #[test]
1848 fn test_set_repeated() {
1849 let mut rec = Record::new();
1850 rec.set(
1851 b"123",
1852 Some(&CigarString(vec![Cigar::Match(3)])),
1853 b"AAA",
1854 b"III",
1855 );
1856 rec.push_aux(b"AS", Aux::I32(12345)).unwrap();
1857 assert_eq!(rec.qname(), b"123");
1858 assert_eq!(rec.seq().as_bytes(), b"AAA");
1859 assert_eq!(rec.qual(), b"III");
1860 assert_eq!(rec.aux(b"AS").unwrap(), Aux::I32(12345));
1861
1862 rec.set(
1863 b"1234",
1864 Some(&CigarString(vec![Cigar::SoftClip(1), Cigar::Match(3)])),
1865 b"AAAA",
1866 b"IIII",
1867 );
1868 assert_eq!(rec.qname(), b"1234");
1869 assert_eq!(rec.seq().as_bytes(), b"AAAA");
1870 assert_eq!(rec.qual(), b"IIII");
1871 assert_eq!(rec.aux(b"AS").unwrap(), Aux::I32(12345));
1872
1873 rec.set(
1874 b"12",
1875 Some(&CigarString(vec![Cigar::Match(2)])),
1876 b"AA",
1877 b"II",
1878 );
1879 assert_eq!(rec.qname(), b"12");
1880 assert_eq!(rec.seq().as_bytes(), b"AA");
1881 assert_eq!(rec.qual(), b"II");
1882 assert_eq!(rec.aux(b"AS").unwrap(), Aux::I32(12345));
1883 }
1884
1885 #[test]
1886 fn test_set_qname() {
1887 let (names, _, seqs, quals, cigars) = gold();
1888
1889 assert!(names[0] != names[1]);
1890
1891 for i in 0..names.len() {
1892 let mut rec = record::Record::new();
1893 rec.set(names[i], Some(&cigars[i]), seqs[i], quals[i]);
1894 rec.push_aux(b"NM", Aux::I32(15)).unwrap();
1895
1896 assert_eq!(rec.qname(), names[i]);
1897 assert_eq!(*rec.cigar(), cigars[i]);
1898 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1899 assert_eq!(rec.qual(), quals[i]);
1900 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1901
1902 assert!(rec.qname()[0] != b'X');
1904 rec.set_qname(b"X");
1905 assert_eq!(rec.qname(), b"X");
1906
1907 let mut longer_name = names[i].to_owned().clone();
1909 let extension = b"BuffaloBUffaloBUFFaloBUFFAloBUFFALoBUFFALO";
1910 longer_name.extend(extension.iter());
1911 rec.set_qname(&longer_name);
1912
1913 assert_eq!(rec.qname(), longer_name.as_slice());
1914 assert_eq!(*rec.cigar(), cigars[i]);
1915 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1916 assert_eq!(rec.qual(), quals[i]);
1917 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1918
1919 let shorter_name = b"42";
1921 rec.set_qname(shorter_name);
1922
1923 assert_eq!(rec.qname(), shorter_name);
1924 assert_eq!(*rec.cigar(), cigars[i]);
1925 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1926 assert_eq!(rec.qual(), quals[i]);
1927 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1928
1929 rec.set_qname(b"");
1931
1932 assert_eq!(rec.qname(), b"");
1933 assert_eq!(*rec.cigar(), cigars[i]);
1934 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1935 assert_eq!(rec.qual(), quals[i]);
1936 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1937 }
1938 }
1939
1940 #[test]
1941 fn test_set_qname2() {
1942 let mut _header = Header::new();
1943 _header.push_record(
1944 HeaderRecord::new(b"SQ")
1945 .push_tag(b"SN", "1")
1946 .push_tag(b"LN", 10000000),
1947 );
1948 let header = HeaderView::from_header(&_header);
1949
1950 let line =
1951 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";
1952
1953 let mut rec = Record::from_sam(&header, line).unwrap();
1954 assert_eq!(rec.qname(), b"blah1");
1955 rec.set_qname(b"r0");
1956 assert_eq!(rec.qname(), b"r0");
1957 }
1958
1959 #[test]
1960 fn test_set_cigar() {
1961 let (names, _, seqs, quals, cigars) = gold();
1962
1963 assert!(names[0] != names[1]);
1964
1965 for i in 0..names.len() {
1966 let mut rec = record::Record::new();
1967 rec.set(names[i], Some(&cigars[i]), seqs[i], quals[i]);
1968 rec.push_aux(b"NM", Aux::I32(15)).unwrap();
1969
1970 assert_eq!(rec.qname(), names[i]);
1971 assert_eq!(*rec.cigar(), cigars[i]);
1972 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1973 assert_eq!(rec.qual(), quals[i]);
1974 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1975
1976 let new_cigar = CigarString(vec![Cigar::Match(rec.seq_len() as u32)]);
1978 assert_ne!(*rec.cigar(), new_cigar);
1979 rec.set_cigar(Some(&new_cigar));
1980 assert_eq!(*rec.cigar(), new_cigar);
1981
1982 assert_eq!(rec.qname(), names[i]);
1983 assert_eq!(rec.seq().as_bytes(), seqs[i]);
1984 assert_eq!(rec.qual(), quals[i]);
1985 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
1986
1987 let new_cigar = (0..rec.seq_len())
1989 .map(|i| {
1990 if i % 2 == 0 {
1991 Cigar::Match(1)
1992 } else {
1993 Cigar::Ins(1)
1994 }
1995 })
1996 .collect::<Vec<_>>();
1997 let new_cigar = CigarString(new_cigar);
1998 assert_ne!(*rec.cigar(), new_cigar);
1999 rec.set_cigar(Some(&new_cigar));
2000 assert_eq!(*rec.cigar(), new_cigar);
2001
2002 assert_eq!(rec.qname(), names[i]);
2003 assert_eq!(rec.seq().as_bytes(), seqs[i]);
2004 assert_eq!(rec.qual(), quals[i]);
2005 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
2006
2007 let new_cigar = CigarString(Vec::new());
2009 assert_ne!(*rec.cigar(), new_cigar);
2010 rec.set_cigar(None);
2011 assert_eq!(*rec.cigar(), new_cigar);
2012
2013 assert_eq!(rec.qname(), names[i]);
2014 assert_eq!(rec.seq().as_bytes(), seqs[i]);
2015 assert_eq!(rec.qual(), quals[i]);
2016 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
2017 }
2018 }
2019
2020 #[test]
2021 fn test_remove_aux() {
2022 let mut bam = Reader::from_path(Path::new("test/test.bam")).expect("Error opening file.");
2023
2024 for record in bam.records() {
2025 let mut rec = record.expect("Expected valid record");
2026
2027 if rec.aux(b"XS").is_ok() {
2028 rec.remove_aux(b"XS").unwrap();
2029 }
2030
2031 if rec.aux(b"YT").is_ok() {
2032 rec.remove_aux(b"YT").unwrap();
2033 }
2034
2035 assert_eq!(rec.remove_aux(b"X"), Err(Error::BamAuxStringError));
2036 assert_eq!(rec.remove_aux(b"ab"), Err(Error::BamAuxTagNotFound));
2037
2038 assert_eq!(rec.aux(b"XS"), Err(Error::BamAuxTagNotFound));
2039 assert_eq!(rec.aux(b"YT"), Err(Error::BamAuxTagNotFound));
2040 }
2041 }
2042
2043 #[test]
2044 fn test_write() {
2045 let (names, _, seqs, quals, cigars) = gold();
2046
2047 let tmp = tempfile::Builder::new()
2048 .prefix("rust-htslib")
2049 .tempdir()
2050 .expect("Cannot create temp dir");
2051 let bampath = tmp.path().join("test.bam");
2052 println!("{:?}", bampath);
2053 {
2054 let mut bam = Writer::from_path(
2055 &bampath,
2056 Header::new().push_record(
2057 HeaderRecord::new(b"SQ")
2058 .push_tag(b"SN", "chr1")
2059 .push_tag(b"LN", 15072423),
2060 ),
2061 Format::Bam,
2062 )
2063 .expect("Error opening file.");
2064
2065 for i in 0..names.len() {
2066 let mut rec = record::Record::new();
2067 rec.set(names[i], Some(&cigars[i]), seqs[i], quals[i]);
2068 rec.push_aux(b"NM", Aux::I32(15)).unwrap();
2069
2070 bam.write(&rec).expect("Failed to write record.");
2071 }
2072 }
2073
2074 {
2075 let mut bam = Reader::from_path(bampath).expect("Error opening file.");
2076
2077 for i in 0..names.len() {
2078 let mut rec = record::Record::new();
2079 if let Some(r) = bam.read(&mut rec) {
2080 r.expect("Failed to read record.");
2081 };
2082
2083 assert_eq!(rec.qname(), names[i]);
2084 assert_eq!(*rec.cigar(), cigars[i]);
2085 assert_eq!(rec.seq().as_bytes(), seqs[i]);
2086 assert_eq!(rec.qual(), quals[i]);
2087 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
2088 }
2089 }
2090
2091 tmp.close().expect("Failed to delete temp dir");
2092 }
2093
2094 #[test]
2095 fn test_write_threaded() {
2096 let (names, _, seqs, quals, cigars) = gold();
2097
2098 let tmp = tempfile::Builder::new()
2099 .prefix("rust-htslib")
2100 .tempdir()
2101 .expect("Cannot create temp dir");
2102 let bampath = tmp.path().join("test.bam");
2103 println!("{:?}", bampath);
2104 {
2105 let mut bam = Writer::from_path(
2106 &bampath,
2107 Header::new().push_record(
2108 HeaderRecord::new(b"SQ")
2109 .push_tag(b"SN", "chr1")
2110 .push_tag(b"LN", 15072423),
2111 ),
2112 Format::Bam,
2113 )
2114 .expect("Error opening file.");
2115 bam.set_threads(4).unwrap();
2116
2117 for i in 0..10000 {
2118 let mut rec = record::Record::new();
2119 let idx = i % names.len();
2120 rec.set(names[idx], Some(&cigars[idx]), seqs[idx], quals[idx]);
2121 rec.push_aux(b"NM", Aux::I32(15)).unwrap();
2122 rec.set_pos(i as i64);
2123
2124 bam.write(&rec).expect("Failed to write record.");
2125 }
2126 }
2127
2128 {
2129 let mut bam = Reader::from_path(bampath).expect("Error opening file.");
2130
2131 for (i, _rec) in bam.records().enumerate() {
2132 let idx = i % names.len();
2133
2134 let rec = _rec.expect("Failed to read record.");
2135
2136 assert_eq!(rec.pos(), i as i64);
2137 assert_eq!(rec.qname(), names[idx]);
2138 assert_eq!(*rec.cigar(), cigars[idx]);
2139 assert_eq!(rec.seq().as_bytes(), seqs[idx]);
2140 assert_eq!(rec.qual(), quals[idx]);
2141 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
2142 }
2143 }
2144
2145 tmp.close().expect("Failed to delete temp dir");
2146 }
2147
2148 #[test]
2149 fn test_write_shared_tpool() {
2150 let (names, _, seqs, quals, cigars) = gold();
2151
2152 let tmp = tempfile::Builder::new()
2153 .prefix("rust-htslib")
2154 .tempdir()
2155 .expect("Cannot create temp dir");
2156 let bampath1 = tmp.path().join("test1.bam");
2157 let bampath2 = tmp.path().join("test2.bam");
2158
2159 {
2160 let (mut bam1, mut bam2) = {
2161 let pool = crate::tpool::ThreadPool::new(4).unwrap();
2162
2163 let mut bam1 = Writer::from_path(
2164 &bampath1,
2165 Header::new().push_record(
2166 HeaderRecord::new(b"SQ")
2167 .push_tag(b"SN", "chr1")
2168 .push_tag(b"LN", 15072423),
2169 ),
2170 Format::Bam,
2171 )
2172 .expect("Error opening file.");
2173
2174 let mut bam2 = Writer::from_path(
2175 &bampath2,
2176 Header::new().push_record(
2177 HeaderRecord::new(b"SQ")
2178 .push_tag(b"SN", "chr1")
2179 .push_tag(b"LN", 15072423),
2180 ),
2181 Format::Bam,
2182 )
2183 .expect("Error opening file.");
2184
2185 bam1.set_thread_pool(&pool).unwrap();
2186 bam2.set_thread_pool(&pool).unwrap();
2187 (bam1, bam2)
2188 };
2189
2190 for i in 0..10000 {
2191 let mut rec = record::Record::new();
2192 let idx = i % names.len();
2193 rec.set(names[idx], Some(&cigars[idx]), seqs[idx], quals[idx]);
2194 rec.push_aux(b"NM", Aux::I32(15)).unwrap();
2195 rec.set_pos(i as i64);
2196
2197 bam1.write(&rec).expect("Failed to write record.");
2198 bam2.write(&rec).expect("Failed to write record.");
2199 }
2200 }
2201
2202 {
2203 let pool = crate::tpool::ThreadPool::new(2).unwrap();
2204
2205 for p in [bampath1, bampath2] {
2206 let mut bam = Reader::from_path(p).expect("Error opening file.");
2207 bam.set_thread_pool(&pool).unwrap();
2208
2209 for (i, _rec) in bam.iter_chunk(None, None).enumerate() {
2210 let idx = i % names.len();
2211
2212 let rec = _rec.expect("Failed to read record.");
2213
2214 assert_eq!(rec.pos(), i as i64);
2215 assert_eq!(rec.qname(), names[idx]);
2216 assert_eq!(*rec.cigar(), cigars[idx]);
2217 assert_eq!(rec.seq().as_bytes(), seqs[idx]);
2218 assert_eq!(rec.qual(), quals[idx]);
2219 assert_eq!(rec.aux(b"NM").unwrap(), Aux::I32(15));
2220 }
2221 }
2222 }
2223
2224 tmp.close().expect("Failed to delete temp dir");
2225 }
2226
2227 #[test]
2228 fn test_copy_template() {
2229 let tmp = tempfile::Builder::new()
2233 .prefix("rust-htslib")
2234 .tempdir()
2235 .expect("Cannot create temp dir");
2236 let bampath = tmp.path().join("test.bam");
2237 println!("{:?}", bampath);
2238
2239 let mut input_bam = Reader::from_path("test/test.bam").expect("Error opening file.");
2240
2241 {
2242 let mut bam = Writer::from_path(
2243 &bampath,
2244 &Header::from_template(input_bam.header()),
2245 Format::Bam,
2246 )
2247 .expect("Error opening file.");
2248
2249 for rec in input_bam.records() {
2250 bam.write(&rec.unwrap()).expect("Failed to write record.");
2251 }
2252 }
2253
2254 {
2255 let copy_bam = Reader::from_path(bampath).expect("Error opening file.");
2256
2257 assert_eq!(input_bam.header().as_bytes(), copy_bam.header().as_bytes());
2259 }
2260
2261 tmp.close().expect("Failed to delete temp dir");
2262 }
2263
2264 #[test]
2265 fn test_pileup() {
2266 let (_, _, seqs, quals, _) = gold();
2267
2268 let mut bam = Reader::from_path("test/test.bam").expect("Error opening file.");
2269 let pileups = bam.pileup();
2270 for pileup in pileups.take(26) {
2271 let _pileup = pileup.expect("Expected successful pileup.");
2272 let pos = _pileup.pos() as usize;
2273 assert_eq!(_pileup.depth(), 6);
2274 assert!(_pileup.tid() == 0);
2275 for (i, a) in _pileup.alignments().enumerate() {
2276 assert_eq!(a.indel(), pileup::Indel::None);
2277 let qpos = a.qpos().unwrap();
2278 assert_eq!(qpos, pos - 1);
2279 assert_eq!(a.record().seq()[qpos], seqs[i][qpos]);
2280 assert_eq!(a.record().qual()[qpos], quals[i][qpos] - 33);
2281 }
2282 }
2283 }
2284
2285 #[test]
2286 fn test_idx_pileup() {
2287 let mut bam = IndexedReader::from_path("test/test.bam").expect("Error opening file.");
2288 for pileup in bam.pileup() {
2290 pileup.unwrap();
2291 }
2292 let tid = bam.header().tid(b"CHROMOSOME_I").unwrap();
2294 bam.fetch((tid, 0, 5)).unwrap();
2295 for p in bam.pileup() {
2296 println!("{}", p.unwrap().pos())
2297 }
2298 }
2299
2300 #[test]
2301 fn parse_from_sam() {
2302 use std::fs::File;
2303 use std::io::Read;
2304
2305 let bamfile = "./test/bam2sam_test.bam";
2306 let samfile = "./test/bam2sam_expected.sam";
2307
2308 let mut rdr = Reader::from_path(bamfile).unwrap();
2310 let bam_recs: Vec<Record> = rdr.records().map(|v| v.unwrap()).collect();
2311
2312 let mut sam = Vec::new();
2313 assert!(File::open(samfile).unwrap().read_to_end(&mut sam).is_ok());
2314
2315 let sam_recs: Vec<Record> = sam
2316 .split(|x| *x == b'\n')
2317 .filter(|x| !x.is_empty() && x[0] != b'@')
2318 .map(|line| Record::from_sam(rdr.header(), line).unwrap())
2319 .collect();
2320
2321 for (b1, s1) in bam_recs.iter().zip(sam_recs.iter()) {
2322 assert!(b1 == s1);
2323 }
2324 }
2325
2326 #[test]
2327 fn test_cigar_modes() {
2328 let (_, _, _, _, cigars) = gold();
2331 let mut bam = Reader::from_path(Path::new("test/test.bam")).expect("Error opening file.");
2332
2333 for (i, record) in bam.records().enumerate() {
2334 let rec = record.expect("Expected valid record");
2335
2336 let cigar = rec.cigar();
2337 assert_eq!(*cigar, cigars[i]);
2338 }
2339
2340 for (i, record) in bam.records().enumerate() {
2341 let mut rec = record.expect("Expected valid record");
2342 rec.cache_cigar();
2343
2344 let cigar = rec.cigar_cached().unwrap();
2345 assert_eq!(**cigar, cigars[i]);
2346
2347 let cigar = rec.cigar();
2348 assert_eq!(*cigar, cigars[i]);
2349 }
2350 }
2351
2352 #[test]
2353 fn test_read_cram() {
2354 let cram_path = "./test/test_cram.cram";
2355 let bam_path = "./test/test_cram.bam";
2356 let ref_path = "./test/test_cram.fa";
2357
2358 let mut cram_reader = Reader::from_path(cram_path).unwrap();
2360 cram_reader.set_reference(ref_path).unwrap();
2361 let cram_records: Vec<Record> = cram_reader.records().map(|v| v.unwrap()).collect();
2362
2363 let mut bam_reader = Reader::from_path(bam_path).unwrap();
2365 let bam_records: Vec<Record> = bam_reader.records().map(|v| v.unwrap()).collect();
2366
2367 compare_inner_bam_cram_records(&cram_records, &bam_records);
2368 }
2369
2370 #[test]
2371 fn test_write_cram() {
2372 let bam_path = "./test/test_cram.bam";
2374 let ref_path = "./test/test_cram.fa";
2375 let mut bam_reader = Reader::from_path(bam_path).unwrap();
2376 let bam_records: Vec<Record> = bam_reader.records().map(|v| v.unwrap()).collect();
2377
2378 let tmp = tempfile::Builder::new()
2380 .prefix("rust-htslib")
2381 .tempdir()
2382 .expect("Cannot create temp dir");
2383 let cram_path = tmp.path().join("test.cram");
2384
2385 {
2387 let mut header = Header::new();
2388 header.push_record(
2389 HeaderRecord::new(b"HD")
2390 .push_tag(b"VN", "1.5")
2391 .push_tag(b"SO", "coordinate"),
2392 );
2393 header.push_record(
2394 HeaderRecord::new(b"SQ")
2395 .push_tag(b"SN", "chr1")
2396 .push_tag(b"LN", 120)
2397 .push_tag(b"M5", "20a9a0fb770814e6c5e49946750f9724")
2398 .push_tag(b"UR", "test/test_cram.fa"),
2399 );
2400 header.push_record(
2401 HeaderRecord::new(b"SQ")
2402 .push_tag(b"SN", "chr2")
2403 .push_tag(b"LN", 120)
2404 .push_tag(b"M5", "7a2006ccca94ea92b6dae5997e1b0d70")
2405 .push_tag(b"UR", "test/test_cram.fa"),
2406 );
2407 header.push_record(
2408 HeaderRecord::new(b"SQ")
2409 .push_tag(b"SN", "chr3")
2410 .push_tag(b"LN", 120)
2411 .push_tag(b"M5", "a66b336bfe3ee8801c744c9545c87e24")
2412 .push_tag(b"UR", "test/test_cram.fa"),
2413 );
2414
2415 let mut cram_writer = Writer::from_path(&cram_path, &header, Format::Cram)
2416 .expect("Error opening CRAM file.");
2417 cram_writer.set_reference(ref_path).unwrap();
2418
2419 for rec in bam_records.iter() {
2421 cram_writer
2422 .write(rec)
2423 .expect("Faied to write record to CRAM.");
2424 }
2425 }
2426
2427 {
2429 let mut cram_reader = Reader::from_path(cram_path).unwrap();
2431 cram_reader.set_reference(ref_path).unwrap();
2432 let cram_records: Vec<Record> = cram_reader.records().map(|v| v.unwrap()).collect();
2433
2434 compare_inner_bam_cram_records(&cram_records, &bam_records);
2436 }
2437
2438 tmp.close().expect("Failed to delete temp dir");
2439 }
2440
2441 #[test]
2442 fn test_compression_level_conversion() {
2443 assert_eq!(CompressionLevel::Uncompressed.convert().unwrap(), 0);
2445 assert_eq!(CompressionLevel::Fastest.convert().unwrap(), 1);
2446 assert_eq!(CompressionLevel::Maximum.convert().unwrap(), 9);
2447
2448 for level in 0..=9 {
2450 assert_eq!(CompressionLevel::Level(level).convert().unwrap(), level);
2451 }
2452 assert!(CompressionLevel::Level(10).convert().is_err());
2454 }
2455
2456 #[test]
2457 fn test_write_compression() {
2458 let tmp = tempfile::Builder::new()
2459 .prefix("rust-htslib")
2460 .tempdir()
2461 .expect("Cannot create temp dir");
2462 let input_bam_path = "test/test.bam";
2463
2464 let levels_to_test = vec![
2466 CompressionLevel::Maximum,
2467 CompressionLevel::Level(6),
2468 CompressionLevel::Fastest,
2469 CompressionLevel::Uncompressed,
2470 ];
2471 let file_sizes: Vec<_> = levels_to_test
2472 .iter()
2473 .map(|level| {
2474 let output_bam_path = tmp.path().join("test.bam");
2475 {
2476 let mut reader = Reader::from_path(input_bam_path).unwrap();
2477 let header = Header::from_template(reader.header());
2478 let mut writer =
2479 Writer::from_path(&output_bam_path, &header, Format::Bam).unwrap();
2480 writer.set_compression_level(*level).unwrap();
2481 for record in reader.records() {
2482 let r = record.unwrap();
2483 writer.write(&r).unwrap();
2484 }
2485 }
2486 fs::metadata(output_bam_path).unwrap().len()
2487 })
2488 .collect();
2489
2490 println!("testing compression leves: {:?}", levels_to_test);
2492 println!("got compressed sizes: {:?}", file_sizes);
2493
2494 #[cfg(feature = "libdeflate")]
2497 assert!(file_sizes[1..].windows(2).all(|size| size[0] <= size[1]));
2498
2499 #[cfg(not(feature = "libdeflate"))]
2500 assert!(file_sizes.windows(2).all(|size| size[0] <= size[1]));
2501
2502 tmp.close().expect("Failed to delete temp dir");
2503 }
2504
2505 #[test]
2506 fn test_bam_fails_on_vcf() {
2507 let bam_path = "./test/test_left.vcf";
2508 let bam_reader = Reader::from_path(bam_path);
2509 assert!(bam_reader.is_err());
2510 }
2511
2512 #[test]
2513 fn test_indexde_bam_fails_on_vcf() {
2514 let bam_path = "./test/test_left.vcf";
2515 let bam_reader = IndexedReader::from_path(bam_path);
2516 assert!(bam_reader.is_err());
2517 }
2518
2519 #[test]
2520 fn test_bam_fails_on_toml() {
2521 let bam_path = "./Cargo.toml";
2522 let bam_reader = Reader::from_path(bam_path);
2523 assert!(bam_reader.is_err());
2524 }
2525
2526 #[test]
2527 fn test_sam_writer_example() {
2528 fn from_bam_with_filter<F>(bamfile: &str, samfile: &str, f: F) -> bool
2529 where
2530 F: Fn(&record::Record) -> Option<bool>,
2531 {
2532 let mut bam_reader = Reader::from_path(bamfile).unwrap(); let header = header::Header::from_template(bam_reader.header());
2534 let mut sam_writer = Writer::from_path(samfile, &header, Format::Sam).unwrap();
2535 for record in bam_reader.records() {
2536 if record.is_err() {
2537 return false;
2538 }
2539 let parsed = record.unwrap();
2540 match f(&parsed) {
2541 None => return true,
2542 Some(false) => {}
2543 Some(true) => {
2544 if sam_writer.write(&parsed).is_err() {
2545 return false;
2546 }
2547 }
2548 }
2549 }
2550 true
2551 }
2552 use std::fs::File;
2553 use std::io::Read;
2554 let bamfile = "./test/bam2sam_test.bam";
2555 let samfile = "./test/bam2sam_out.sam";
2556 let expectedfile = "./test/bam2sam_expected.sam";
2557 let result = from_bam_with_filter(bamfile, samfile, |_| Some(true));
2558 assert!(result);
2559 let mut expected = Vec::new();
2560 let mut written = Vec::new();
2561 assert!(File::open(expectedfile)
2562 .unwrap()
2563 .read_to_end(&mut expected)
2564 .is_ok());
2565 assert!(File::open(samfile)
2566 .unwrap()
2567 .read_to_end(&mut written)
2568 .is_ok());
2569 assert_eq!(expected, written);
2570 }
2571
2572 #[test]
2587 fn test_rc_records() {
2588 let (names, flags, seqs, quals, cigars) = gold();
2589 let mut bam = Reader::from_path(Path::new("test/test.bam")).expect("Error opening file.");
2590 let del_len = [1, 1, 1, 1, 1, 100000];
2591
2592 for (i, record) in bam.rc_records().enumerate() {
2593 let rec = record.unwrap();
2595 println!("{}", str::from_utf8(rec.qname()).ok().unwrap());
2596 assert_eq!(rec.qname(), names[i]);
2597 assert_eq!(rec.flags(), flags[i]);
2598 assert_eq!(rec.seq().as_bytes(), seqs[i]);
2599
2600 let cigar = rec.cigar();
2601 assert_eq!(*cigar, cigars[i]);
2602
2603 let end_pos = cigar.end_pos();
2604 assert_eq!(end_pos, rec.pos() + 100 + del_len[i]);
2605 assert_eq!(
2606 cigar
2607 .read_pos(end_pos as u32 - 10, false, false)
2608 .unwrap()
2609 .unwrap(),
2610 90
2611 );
2612 assert_eq!(
2613 cigar
2614 .read_pos(rec.pos() as u32 + 20, false, false)
2615 .unwrap()
2616 .unwrap(),
2617 20
2618 );
2619 assert_eq!(cigar.read_pos(4000000, false, false).unwrap(), None);
2620 let qual: Vec<u8> = quals[i].iter().map(|&q| q - 33).collect();
2622 assert_eq!(rec.qual(), &qual[..]);
2623 }
2624 }
2625
2626 #[test]
2627 fn test_aux_arrays() {
2628 let bam_header = Header::new();
2629 let mut test_record = Record::from_sam(
2630 &HeaderView::from_header(&bam_header),
2631 "ali1\t4\t*\t0\t0\t*\t*\t0\t0\tACGT\tFFFF".as_bytes(),
2632 )
2633 .unwrap();
2634
2635 let array_i8: Vec<i8> = vec![i8::MIN, -1, 0, 1, i8::MAX];
2636 let array_u8: Vec<u8> = vec![u8::MIN, 0, 1, u8::MAX];
2637 let array_i16: Vec<i16> = vec![i16::MIN, -1, 0, 1, i16::MAX];
2638 let array_u16: Vec<u16> = vec![u16::MIN, 0, 1, u16::MAX];
2639 let array_i32: Vec<i32> = vec![i32::MIN, -1, 0, 1, i32::MAX];
2640 let array_u32: Vec<u32> = vec![u32::MIN, 0, 1, u32::MAX];
2641 let array_f32: Vec<f32> = vec![f32::MIN, 0.0, -0.0, 0.1, 0.99, f32::MAX];
2642
2643 test_record
2644 .push_aux(b"XA", Aux::ArrayI8((&array_i8).into()))
2645 .unwrap();
2646 test_record
2647 .push_aux(b"XB", Aux::ArrayU8((&array_u8).into()))
2648 .unwrap();
2649 test_record
2650 .push_aux(b"XC", Aux::ArrayI16((&array_i16).into()))
2651 .unwrap();
2652 test_record
2653 .push_aux(b"XD", Aux::ArrayU16((&array_u16).into()))
2654 .unwrap();
2655 test_record
2656 .push_aux(b"XE", Aux::ArrayI32((&array_i32).into()))
2657 .unwrap();
2658 test_record
2659 .push_aux(b"XF", Aux::ArrayU32((&array_u32).into()))
2660 .unwrap();
2661 test_record
2662 .push_aux(b"XG", Aux::ArrayFloat((&array_f32).into()))
2663 .unwrap();
2664
2665 {
2666 let tag = b"XA";
2667 if let Ok(Aux::ArrayI8(array)) = test_record.aux(tag) {
2668 let aux_array_content = array.iter().collect::<Vec<_>>();
2670 assert_eq!(aux_array_content, array_i8);
2671
2672 {
2674 let mut copy_test_record = test_record.clone();
2675
2676 assert!(copy_test_record.push_aux(tag, Aux::I8(3)).is_err());
2678
2679 copy_test_record.remove_aux(tag).unwrap();
2681 assert!(copy_test_record.aux(tag).is_err());
2682
2683 let src_aux = test_record.aux(tag).unwrap();
2685 assert!(copy_test_record.push_aux(tag, src_aux).is_ok());
2686 if let Ok(Aux::ArrayI8(array)) = copy_test_record.aux(tag) {
2687 let aux_array_content_copied = array.iter().collect::<Vec<_>>();
2688 assert_eq!(aux_array_content_copied, array_i8);
2689 } else {
2690 panic!("Aux tag not found");
2691 }
2692 }
2693 } else {
2694 panic!("Aux tag not found");
2695 }
2696 }
2697
2698 {
2699 let tag = b"XB";
2700 if let Ok(Aux::ArrayU8(array)) = test_record.aux(tag) {
2701 let aux_array_content = array.iter().collect::<Vec<_>>();
2703 assert_eq!(aux_array_content, array_u8);
2704
2705 {
2707 let mut copy_test_record = test_record.clone();
2708
2709 assert!(copy_test_record.push_aux(tag, Aux::U8(3)).is_err());
2711
2712 copy_test_record.remove_aux(tag).unwrap();
2714 assert!(copy_test_record.aux(tag).is_err());
2715
2716 let src_aux = test_record.aux(tag).unwrap();
2718 assert!(copy_test_record.push_aux(tag, src_aux).is_ok());
2719 if let Ok(Aux::ArrayU8(array)) = copy_test_record.aux(tag) {
2720 let aux_array_content_copied = array.iter().collect::<Vec<_>>();
2721 assert_eq!(aux_array_content_copied, array_u8);
2722 } else {
2723 panic!("Aux tag not found");
2724 }
2725 }
2726 } else {
2727 panic!("Aux tag not found");
2728 }
2729 }
2730
2731 {
2732 let tag = b"XC";
2733 if let Ok(Aux::ArrayI16(array)) = test_record.aux(tag) {
2734 let aux_array_content = array.iter().collect::<Vec<_>>();
2736 assert_eq!(aux_array_content, array_i16);
2737
2738 {
2740 let mut copy_test_record = test_record.clone();
2741
2742 assert!(copy_test_record.push_aux(tag, Aux::I16(3)).is_err());
2744
2745 copy_test_record.remove_aux(tag).unwrap();
2747 assert!(copy_test_record.aux(tag).is_err());
2748
2749 let src_aux = test_record.aux(tag).unwrap();
2751 assert!(copy_test_record.push_aux(tag, src_aux).is_ok());
2752 if let Ok(Aux::ArrayI16(array)) = copy_test_record.aux(tag) {
2753 let aux_array_content_copied = array.iter().collect::<Vec<_>>();
2754 assert_eq!(aux_array_content_copied, array_i16);
2755 } else {
2756 panic!("Aux tag not found");
2757 }
2758 }
2759 } else {
2760 panic!("Aux tag not found");
2761 }
2762 }
2763
2764 {
2765 let tag = b"XD";
2766 if let Ok(Aux::ArrayU16(array)) = test_record.aux(tag) {
2767 let aux_array_content = array.iter().collect::<Vec<_>>();
2769 assert_eq!(aux_array_content, array_u16);
2770
2771 {
2773 let mut copy_test_record = test_record.clone();
2774
2775 assert!(copy_test_record.push_aux(tag, Aux::U16(3)).is_err());
2777
2778 copy_test_record.remove_aux(tag).unwrap();
2780 assert!(copy_test_record.aux(tag).is_err());
2781
2782 let src_aux = test_record.aux(tag).unwrap();
2784 assert!(copy_test_record.push_aux(tag, src_aux).is_ok());
2785 if let Ok(Aux::ArrayU16(array)) = copy_test_record.aux(tag) {
2786 let aux_array_content_copied = array.iter().collect::<Vec<_>>();
2787 assert_eq!(aux_array_content_copied, array_u16);
2788 } else {
2789 panic!("Aux tag not found");
2790 }
2791 }
2792 } else {
2793 panic!("Aux tag not found");
2794 }
2795 }
2796
2797 {
2798 let tag = b"XE";
2799 if let Ok(Aux::ArrayI32(array)) = test_record.aux(tag) {
2800 let aux_array_content = array.iter().collect::<Vec<_>>();
2802 assert_eq!(aux_array_content, array_i32);
2803
2804 {
2806 let mut copy_test_record = test_record.clone();
2807
2808 assert!(copy_test_record.push_aux(tag, Aux::I32(3)).is_err());
2810
2811 copy_test_record.remove_aux(tag).unwrap();
2813 assert!(copy_test_record.aux(tag).is_err());
2814
2815 let src_aux = test_record.aux(tag).unwrap();
2817 assert!(copy_test_record.push_aux(tag, src_aux).is_ok());
2818 if let Ok(Aux::ArrayI32(array)) = copy_test_record.aux(tag) {
2819 let aux_array_content_copied = array.iter().collect::<Vec<_>>();
2820 assert_eq!(aux_array_content_copied, array_i32);
2821 } else {
2822 panic!("Aux tag not found");
2823 }
2824 }
2825 } else {
2826 panic!("Aux tag not found");
2827 }
2828 }
2829
2830 {
2831 let tag = b"XF";
2832 if let Ok(Aux::ArrayU32(array)) = test_record.aux(tag) {
2833 let aux_array_content = array.iter().collect::<Vec<_>>();
2835 assert_eq!(aux_array_content, array_u32);
2836
2837 {
2839 let mut copy_test_record = test_record.clone();
2840
2841 assert!(copy_test_record.push_aux(tag, Aux::U32(3)).is_err());
2843
2844 copy_test_record.remove_aux(tag).unwrap();
2846 assert!(copy_test_record.aux(tag).is_err());
2847
2848 let src_aux = test_record.aux(tag).unwrap();
2850 assert!(copy_test_record.push_aux(tag, src_aux).is_ok());
2851 if let Ok(Aux::ArrayU32(array)) = copy_test_record.aux(tag) {
2852 let aux_array_content_copied = array.iter().collect::<Vec<_>>();
2853 assert_eq!(aux_array_content_copied, array_u32);
2854 } else {
2855 panic!("Aux tag not found");
2856 }
2857 }
2858 } else {
2859 panic!("Aux tag not found");
2860 }
2861 }
2862
2863 {
2864 let tag = b"XG";
2865 if let Ok(Aux::ArrayFloat(array)) = test_record.aux(tag) {
2866 let aux_array_content = array.iter().collect::<Vec<_>>();
2868 assert_eq!(aux_array_content, array_f32);
2869
2870 {
2872 let mut copy_test_record = test_record.clone();
2873
2874 assert!(copy_test_record.push_aux(tag, Aux::Float(3.0)).is_err());
2876
2877 copy_test_record.remove_aux(tag).unwrap();
2879 assert!(copy_test_record.aux(tag).is_err());
2880
2881 let src_aux = test_record.aux(tag).unwrap();
2883 assert!(copy_test_record.push_aux(tag, src_aux).is_ok());
2884 if let Ok(Aux::ArrayFloat(array)) = copy_test_record.aux(tag) {
2885 let aux_array_content_copied = array.iter().collect::<Vec<_>>();
2886 assert_eq!(aux_array_content_copied, array_f32);
2887 } else {
2888 panic!("Aux tag not found");
2889 }
2890 }
2891 } else {
2892 panic!("Aux tag not found");
2893 }
2894 }
2895
2896 for item in test_record.aux_iter() {
2898 match item.unwrap() {
2899 (b"XA", Aux::ArrayI8(array)) => {
2900 assert_eq!(&array.iter().collect::<Vec<_>>(), &array_i8);
2901 }
2902 (b"XB", Aux::ArrayU8(array)) => {
2903 assert_eq!(&array.iter().collect::<Vec<_>>(), &array_u8);
2904 }
2905 (b"XC", Aux::ArrayI16(array)) => {
2906 assert_eq!(&array.iter().collect::<Vec<_>>(), &array_i16);
2907 }
2908 (b"XD", Aux::ArrayU16(array)) => {
2909 assert_eq!(&array.iter().collect::<Vec<_>>(), &array_u16);
2910 }
2911 (b"XE", Aux::ArrayI32(array)) => {
2912 assert_eq!(&array.iter().collect::<Vec<_>>(), &array_i32);
2913 }
2914 (b"XF", Aux::ArrayU32(array)) => {
2915 assert_eq!(&array.iter().collect::<Vec<_>>(), &array_u32);
2916 }
2917 (b"XG", Aux::ArrayFloat(array)) => {
2918 assert_eq!(&array.iter().collect::<Vec<_>>(), &array_f32);
2919 }
2920 _ => {
2921 panic!();
2922 }
2923 }
2924 }
2925
2926 assert_eq!(
2928 test_record.aux(b"XA").unwrap(),
2929 Aux::ArrayI8((&array_i8).into())
2930 );
2931 assert_eq!(
2932 test_record.aux(b"XB").unwrap(),
2933 Aux::ArrayU8((&array_u8).into())
2934 );
2935 assert_eq!(
2936 test_record.aux(b"XC").unwrap(),
2937 Aux::ArrayI16((&array_i16).into())
2938 );
2939 assert_eq!(
2940 test_record.aux(b"XD").unwrap(),
2941 Aux::ArrayU16((&array_u16).into())
2942 );
2943 assert_eq!(
2944 test_record.aux(b"XE").unwrap(),
2945 Aux::ArrayI32((&array_i32).into())
2946 );
2947 assert_eq!(
2948 test_record.aux(b"XF").unwrap(),
2949 Aux::ArrayU32((&array_u32).into())
2950 );
2951 assert_eq!(
2952 test_record.aux(b"XG").unwrap(),
2953 Aux::ArrayFloat((&array_f32).into())
2954 );
2955 }
2956
2957 #[test]
2958 fn test_aux_scalars() {
2959 let bam_header = Header::new();
2960 let mut test_record = Record::from_sam(
2961 &HeaderView::from_header(&bam_header),
2962 "ali1\t4\t*\t0\t0\t*\t*\t0\t0\tACGT\tFFFF".as_bytes(),
2963 )
2964 .unwrap();
2965
2966 test_record.push_aux(b"XA", Aux::I8(i8::MIN)).unwrap();
2967 test_record.push_aux(b"XB", Aux::I8(i8::MAX)).unwrap();
2968 test_record.push_aux(b"XC", Aux::U8(u8::MIN)).unwrap();
2969 test_record.push_aux(b"XD", Aux::U8(u8::MAX)).unwrap();
2970 test_record.push_aux(b"XE", Aux::I16(i16::MIN)).unwrap();
2971 test_record.push_aux(b"XF", Aux::I16(i16::MAX)).unwrap();
2972 test_record.push_aux(b"XG", Aux::U16(u16::MIN)).unwrap();
2973 test_record.push_aux(b"XH", Aux::U16(u16::MAX)).unwrap();
2974 test_record.push_aux(b"XI", Aux::I32(i32::MIN)).unwrap();
2975 test_record.push_aux(b"XJ", Aux::I32(i32::MAX)).unwrap();
2976 test_record.push_aux(b"XK", Aux::U32(u32::MIN)).unwrap();
2977 test_record.push_aux(b"XL", Aux::U32(u32::MAX)).unwrap();
2978 test_record
2979 .push_aux(b"XM", Aux::Float(std::f32::consts::PI))
2980 .unwrap();
2981 test_record
2982 .push_aux(b"XN", Aux::Double(std::f64::consts::PI))
2983 .unwrap();
2984 test_record
2985 .push_aux(b"XO", Aux::String("Test str"))
2986 .unwrap();
2987 test_record.push_aux(b"XP", Aux::I8(0)).unwrap();
2988
2989 let collected_aux_fields = test_record.aux_iter().collect::<Result<Vec<_>>>().unwrap();
2990 assert_eq!(
2991 collected_aux_fields,
2992 vec![
2993 (&b"XA"[..], Aux::I8(i8::MIN)),
2994 (&b"XB"[..], Aux::I8(i8::MAX)),
2995 (&b"XC"[..], Aux::U8(u8::MIN)),
2996 (&b"XD"[..], Aux::U8(u8::MAX)),
2997 (&b"XE"[..], Aux::I16(i16::MIN)),
2998 (&b"XF"[..], Aux::I16(i16::MAX)),
2999 (&b"XG"[..], Aux::U16(u16::MIN)),
3000 (&b"XH"[..], Aux::U16(u16::MAX)),
3001 (&b"XI"[..], Aux::I32(i32::MIN)),
3002 (&b"XJ"[..], Aux::I32(i32::MAX)),
3003 (&b"XK"[..], Aux::U32(u32::MIN)),
3004 (&b"XL"[..], Aux::U32(u32::MAX)),
3005 (&b"XM"[..], Aux::Float(std::f32::consts::PI)),
3006 (&b"XN"[..], Aux::Double(std::f64::consts::PI)),
3007 (&b"XO"[..], Aux::String("Test str")),
3008 (&b"XP"[..], Aux::I8(0)),
3009 ]
3010 );
3011 }
3012
3013 #[test]
3014 fn test_aux_array_partial_eq() {
3015 use record::AuxArray;
3016
3017 let one_data: Vec<i8> = vec![0, 1, 2, 3, 4, 5, 6];
3019 let one_aux_array = AuxArray::from(&one_data);
3020
3021 let two_data: Vec<i8> = vec![0, 1, 2, 3, 4, 5];
3022 let two_aux_array = AuxArray::from(&two_data);
3023
3024 assert_ne!(&one_data, &two_data);
3025 assert_ne!(&one_aux_array, &two_aux_array);
3026
3027 let one_aux = Aux::ArrayI8(one_aux_array);
3028 let two_aux = Aux::ArrayI8(two_aux_array);
3029 assert_ne!(&one_aux, &two_aux);
3030
3031 let bam_header = Header::new();
3033 let mut test_record = Record::from_sam(
3034 &HeaderView::from_header(&bam_header),
3035 "ali1\t4\t*\t0\t0\t*\t*\t0\t0\tACGT\tFFFF".as_bytes(),
3036 )
3037 .unwrap();
3038
3039 test_record.push_aux(b"XA", one_aux).unwrap();
3040 test_record.push_aux(b"XB", two_aux).unwrap();
3041
3042 assert_eq!(
3044 test_record.aux(b"XA").unwrap(),
3045 test_record.aux(b"XA").unwrap()
3046 );
3047 assert_ne!(
3049 test_record.aux(b"XA").unwrap(),
3050 test_record.aux(b"XB").unwrap()
3051 );
3052
3053 assert_eq!(
3055 test_record.aux(b"XA").unwrap(),
3056 Aux::ArrayI8((&one_data).into())
3057 );
3058 assert_eq!(
3059 test_record.aux(b"XB").unwrap(),
3060 Aux::ArrayI8((&two_data).into())
3061 );
3062 assert_ne!(
3064 test_record.aux(b"XA").unwrap(),
3065 Aux::ArrayI8((&two_data).into())
3066 );
3067 assert_ne!(
3068 test_record.aux(b"XB").unwrap(),
3069 Aux::ArrayI8((&one_data).into())
3070 );
3071 }
3072
3073 #[test]
3075 fn test_bam_header_sync() {
3076 let reader = Reader::from_path("test/test_issue_156_no_text.bam").unwrap();
3077 let header_hashmap = Header::from_template(reader.header()).to_hashmap();
3078 let header_refseqs = header_hashmap.get("SQ").unwrap();
3079 assert_eq!(header_refseqs[0].get("SN").unwrap(), "ref_1",);
3080 assert_eq!(header_refseqs[0].get("LN").unwrap(), "10000000",);
3081 }
3082
3083 #[test]
3084 fn test_bam_new() {
3085 let tmp = tempfile::Builder::new()
3087 .prefix("rust-htslib")
3088 .tempdir()
3089 .expect("Cannot create temp dir");
3090 let bampath = tmp.path().join("test.bam");
3091
3092 {
3094 let mut header = Header::new();
3096
3097 header.push_record(
3099 HeaderRecord::new(b"HD")
3100 .push_tag(b"VN", "1.6")
3101 .push_tag(b"SO", "unsorted"),
3102 );
3103
3104 let mut writer = Writer::from_path(&bampath, &header, Format::Bam).unwrap();
3106
3107 let record = Record::new();
3109
3110 assert!(writer.write(&record).is_ok());
3112 }
3113
3114 {
3116 let mut reader = Reader::from_path(bampath).expect("Error opening file.");
3118
3119 let mut rec = Record::new();
3121 match reader.read(&mut rec) {
3122 Some(r) => r.expect("Failed to read record."),
3123 None => panic!("No record read."),
3124 };
3125
3126 assert!(rec.is_unmapped());
3128 assert_eq!(rec.tid(), -1);
3129 assert_eq!(rec.pos(), -1);
3130 assert_eq!(rec.mtid(), -1);
3131 assert_eq!(rec.mpos(), -1);
3132 }
3133 }
3134
3135 #[test]
3136 fn test_idxstats_bam() {
3137 let mut reader = IndexedReader::from_path("test/test.bam").unwrap();
3138 let expected = vec![
3139 (0, 15072423, 6, 0),
3140 (1, 15279345, 0, 0),
3141 (2, 13783700, 0, 0),
3142 (3, 17493793, 0, 0),
3143 (4, 20924149, 0, 0),
3144 (-1, 0, 0, 0),
3145 ];
3146 let actual = reader.index_stats().unwrap();
3147 assert_eq!(expected, actual);
3148 }
3149
3150 #[test]
3151 fn test_number_mapped_and_unmapped_bam() {
3152 let reader = IndexedReader::from_path("test/test.bam").unwrap();
3153 let expected = (6, 0);
3154 let actual = reader.index().number_mapped_unmapped(0);
3155 assert_eq!(expected, actual);
3156 }
3157
3158 #[test]
3159 fn test_number_unmapped_global_bam() {
3160 let reader = IndexedReader::from_path("test/test_unmapped.bam").unwrap();
3161 let expected = 8;
3162 let actual = reader.index().number_unmapped();
3163 assert_eq!(expected, actual);
3164 }
3165
3166 #[test]
3167 fn test_idxstats_cram() {
3168 let mut reader = IndexedReader::from_path("test/test_cram.cram").unwrap();
3169 reader.set_reference("test/test_cram.fa").unwrap();
3170 let expected = vec![
3171 (0, 120, 2, 0),
3172 (1, 120, 2, 0),
3173 (2, 120, 2, 0),
3174 (-1, 0, 0, 0),
3175 ];
3176 let actual = reader.index_stats().unwrap();
3177 assert_eq!(expected, actual);
3178 }
3179
3180 #[test]
3181 fn test_slow_idxstats_cram() {
3182 let mut reader = IndexedReader::from_path("test/test_cram.cram").unwrap();
3183 reader.set_reference("test/test_cram.fa").unwrap();
3184 let expected = vec![
3185 (0, 120, 2, 0),
3186 (1, 120, 2, 0),
3187 (2, 120, 2, 0),
3188 (-1, 0, 0, 0),
3189 ];
3190 let actual = reader.index_stats().unwrap();
3191 assert_eq!(expected, actual);
3192 }
3193
3194 #[test]
3195 fn test_slow_idxstats_cram_unmapped() {
3196 let mut reader = IndexedReader::from_path("test/test_cram_unmapped.cram").unwrap();
3197 reader.set_reference("test/test_cram.fa").unwrap();
3198 let expected = vec![
3199 (0, 120, 2, 0),
3200 (1, 120, 2, 0),
3201 (2, 120, 2, 0),
3202 (-1, 0, 0, 2),
3203 ];
3204 let actual = reader.index_stats().unwrap();
3205 assert_eq!(expected, actual);
3206 }
3207
3208 #[test]
3209 fn test_nonexistent_tidname() {
3210 let header = Header::new();
3211 let header_view = HeaderView::from_header(&header);
3212 assert_eq!(b"", header_view.tid2name(0));
3213 }
3214
3215 }