1use std::borrow::Cow;
7use std::convert::TryFrom;
8use std::convert::TryInto;
9use std::ffi;
10use std::fmt;
11use std::fmt::Display;
12use std::marker::PhantomData;
13use std::mem::{MaybeUninit, size_of};
14use std::ops;
15use std::ops::RangeBounds;
16use std::os::raw::c_char;
17use std::slice;
18use std::slice::SliceIndex;
19use std::str;
20use std::str::FromStr;
21use std::sync::Arc;
22
23use byteorder::{LittleEndian, ReadBytesExt};
24
25use crate::bam::Error;
26use crate::bam::HeaderView;
27use crate::errors::Result;
28use crate::htslib;
29use crate::utils;
30#[cfg(feature = "serde_feature")]
31use serde::{self, Deserialize, Serialize};
32
33use bio_types::alignment::{Alignment, AlignmentMode, AlignmentOperation};
34use bio_types::genome;
35use bio_types::sequence::SequenceRead;
36use bio_types::sequence::SequenceReadPairOrientation;
37use bio_types::strand::ReqStrand;
38
39macro_rules! flag {
41 ($get:ident, $set:ident, $unset:ident, $bit:expr) => {
42 pub fn $get(&self) -> bool {
43 self.inner().core.flag & $bit != 0
44 }
45
46 pub fn $set(&mut self) {
47 self.inner_mut().core.flag |= $bit;
48 }
49
50 pub fn $unset(&mut self) {
51 self.inner_mut().core.flag &= !$bit;
52 }
53 };
54}
55
56pub struct Record {
58 pub inner: htslib::bam1_t,
59 own: bool,
60 cigar: Option<CigarStringView>,
61 header: Option<Arc<HeaderView>>,
62}
63
64unsafe impl Send for Record {}
65unsafe impl Sync for Record {}
66
67impl Clone for Record {
68 fn clone(&self) -> Self {
69 let mut copy = Record::new();
70 unsafe { htslib::bam_copy1(copy.inner_ptr_mut(), self.inner_ptr()) };
71 copy
72 }
73}
74
75impl PartialEq for Record {
76 fn eq(&self, other: &Record) -> bool {
77 self.tid() == other.tid()
78 && self.pos() == other.pos()
79 && self.bin() == other.bin()
80 && self.mapq() == other.mapq()
81 && self.flags() == other.flags()
82 && self.mtid() == other.mtid()
83 && self.mpos() == other.mpos()
84 && self.insert_size() == other.insert_size()
85 && self.data() == other.data()
86 && self.inner().core.l_extranul == other.inner().core.l_extranul
87 }
88}
89
90impl Eq for Record {}
91
92impl fmt::Debug for Record {
93 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
94 fmt.write_fmt(format_args!(
95 "Record(tid: {}, pos: {})",
96 self.tid(),
97 self.pos()
98 ))
99 }
100}
101
102impl Default for Record {
103 fn default() -> Self {
104 Self::new()
105 }
106}
107
108#[inline]
109fn extranul_from_qname(qname: &[u8]) -> usize {
110 let qlen = qname.len() + 1;
111 if !qlen.is_multiple_of(4) {
112 4 - qlen % 4
113 } else {
114 0
115 }
116}
117
118impl Record {
119 pub fn new() -> Self {
121 let mut record = Record {
122 inner: unsafe { MaybeUninit::zeroed().assume_init() },
123 own: true,
124 cigar: None,
125 header: None,
126 };
127 record.set_qname(b"");
130 record.set_unmapped();
133 record.set_tid(-1);
134 record.set_pos(-1);
135 record.set_mpos(-1);
136 record.set_mtid(-1);
137 record
138 }
139 pub fn cigarhasequal(&self) -> bool {
141 self.cigar()
142 .take()
143 .0
144 .iter()
145 .any(|f| matches!(f, Cigar::Equal(_)) || matches!(f, Cigar::Diff(_)))
146 }
147 pub fn from_inner(from: *mut htslib::bam1_t) -> Self {
148 Record {
149 inner: {
150 #[allow(clippy::uninit_assumed_init, invalid_value)]
151 let mut inner = unsafe { MaybeUninit::uninit().assume_init() };
152 unsafe {
153 ::libc::memcpy(
154 &mut inner as *mut htslib::bam1_t as *mut ::libc::c_void,
155 from as *const ::libc::c_void,
156 size_of::<htslib::bam1_t>(),
157 );
158 }
159 inner
160 },
161 own: false,
162 cigar: None,
163 header: None,
164 }
165 }
166
167 pub fn from_sam(header_view: &HeaderView, sam: &[u8]) -> Result<Record> {
169 let mut record = Self::new();
170
171 let mut sam_copy = Vec::with_capacity(sam.len() + 1);
172 sam_copy.extend(sam);
173 sam_copy.push(0);
174
175 let mut sam_string = htslib::kstring_t {
176 s: sam_copy.as_ptr() as *mut c_char,
177 l: sam_copy.len(),
178 m: sam_copy.len(),
179 };
180
181 let succ = unsafe {
182 htslib::sam_parse1(
183 &mut sam_string,
184 header_view.inner_ptr() as *mut htslib::bam_hdr_t,
185 record.inner_ptr_mut(),
186 )
187 };
188
189 if succ == 0 {
190 Ok(record)
191 } else {
192 Err(Error::BamParseSAM {
193 rec: str::from_utf8(&sam_copy)
194 .map_err(|e| Error::BamParseSAM { rec: e.to_string() })?
195 .to_owned(),
196 })
197 }
198 }
199
200 pub fn set_header(&mut self, header: Arc<HeaderView>) {
201 self.header = Some(header);
202 }
203
204 pub(super) fn data(&self) -> &[u8] {
205 unsafe { slice::from_raw_parts(self.inner().data, self.inner().l_data as usize) }
206 }
207
208 #[inline]
209 pub fn inner_mut(&mut self) -> &mut htslib::bam1_t {
210 &mut self.inner
211 }
212
213 #[inline]
214 pub(super) fn inner_ptr_mut(&mut self) -> *mut htslib::bam1_t {
215 &mut self.inner as *mut htslib::bam1_t
216 }
217
218 #[inline]
219 pub fn inner(&self) -> &htslib::bam1_t {
220 &self.inner
221 }
222
223 #[inline]
224 pub(super) fn inner_ptr(&self) -> *const htslib::bam1_t {
225 &self.inner as *const htslib::bam1_t
226 }
227
228 pub fn tid(&self) -> i32 {
230 self.inner().core.tid
231 }
232
233 pub fn set_tid(&mut self, tid: i32) {
235 self.inner_mut().core.tid = tid;
236 }
237
238 pub fn pos(&self) -> i64 {
240 self.inner().core.pos
241 }
242
243 pub fn set_pos(&mut self, pos: i64) {
245 self.inner_mut().core.pos = pos;
246 }
247
248 pub fn bin(&self) -> u16 {
249 self.inner().core.bin
250 }
251
252 pub fn set_bin(&mut self, bin: u16) {
253 self.inner_mut().core.bin = bin;
254 }
255
256 pub fn mapq(&self) -> u8 {
258 self.inner().core.qual
259 }
260
261 pub fn set_mapq(&mut self, mapq: u8) {
263 self.inner_mut().core.qual = mapq;
264 }
265
266 pub fn strand(&self) -> ReqStrand {
268 let reverse = self.flags() & 0x10 != 0;
269 if reverse {
270 ReqStrand::Reverse
271 } else {
272 ReqStrand::Forward
273 }
274 }
275
276 pub fn flags(&self) -> u16 {
278 self.inner().core.flag
279 }
280
281 pub fn set_flags(&mut self, flags: u16) {
283 self.inner_mut().core.flag = flags;
284 }
285
286 pub fn unset_flags(&mut self) {
288 self.inner_mut().core.flag = 0;
289 }
290
291 pub fn mtid(&self) -> i32 {
293 self.inner().core.mtid
294 }
295
296 pub fn set_mtid(&mut self, mtid: i32) {
298 self.inner_mut().core.mtid = mtid;
299 }
300
301 pub fn mpos(&self) -> i64 {
303 self.inner().core.mpos
304 }
305
306 pub fn set_mpos(&mut self, mpos: i64) {
308 self.inner_mut().core.mpos = mpos;
309 }
310
311 pub fn insert_size(&self) -> i64 {
313 self.inner().core.isize_
314 }
315
316 pub fn set_insert_size(&mut self, insert_size: i64) {
318 self.inner_mut().core.isize_ = insert_size;
319 }
320
321 fn qname_capacity(&self) -> usize {
322 self.inner().core.l_qname as usize
323 }
324
325 fn qname_len(&self) -> usize {
326 self.qname_capacity() - 1 - self.inner().core.l_extranul as usize
328 }
329
330 pub fn qname(&self) -> &[u8] {
332 &self.data()[..self.qname_len()]
333 }
334
335 pub fn set_data(&mut self, new_data: &[u8]) {
337 self.cigar = None;
338
339 self.inner_mut().l_data = new_data.len() as i32;
340 if (self.inner().m_data as i32) < self.inner().l_data {
341 let l_data = self.inner().l_data;
343 self.realloc_var_data(l_data as usize);
344 }
345
346 let data =
348 unsafe { slice::from_raw_parts_mut(self.inner.data, self.inner().l_data as usize) };
349 utils::copy_memory(new_data, data);
350 }
351
352 pub fn set(&mut self, qname: &[u8], cigar: Option<&CigarString>, seq: &[u8], qual: &[u8]) {
360 assert!(qname.len() < 255);
361 assert_eq!(seq.len(), qual.len(), "seq.len() must equal qual.len()");
362
363 self.cigar = None;
364
365 let cigar_width = if let Some(cigar_string) = cigar {
366 cigar_string.len()
367 } else {
368 0
369 } * 4;
370 let q_len = qname.len() + 1;
371 let extranul = extranul_from_qname(qname);
372
373 let orig_aux_offset = self.qname_capacity()
374 + 4 * self.cigar_len()
375 + self.seq_len().div_ceil(2)
376 + self.seq_len();
377 let new_aux_offset = q_len + extranul + cigar_width + seq.len().div_ceil(2) + qual.len();
378 assert!(orig_aux_offset <= self.inner.l_data as usize);
379 let aux_len = self.inner.l_data as usize - orig_aux_offset;
380 self.inner_mut().l_data = (new_aux_offset + aux_len) as i32;
381 if (self.inner().m_data as i32) < self.inner().l_data {
382 let l_data = self.inner().l_data;
384 self.realloc_var_data(l_data as usize);
385 }
386
387 if aux_len > 0 && orig_aux_offset != new_aux_offset {
389 let data =
390 unsafe { slice::from_raw_parts_mut(self.inner.data, self.inner().m_data as usize) };
391 data.copy_within(orig_aux_offset..orig_aux_offset + aux_len, new_aux_offset);
392 }
393
394 let data =
395 unsafe { slice::from_raw_parts_mut(self.inner.data, self.inner().l_data as usize) };
396
397 utils::copy_memory(qname, data);
399 for i in 0..=extranul {
400 data[qname.len() + i] = b'\0';
401 }
402 let mut i = q_len + extranul;
403 self.inner_mut().core.l_qname = i as u16;
404 self.inner_mut().core.l_extranul = extranul as u8;
405
406 if let Some(cigar_string) = cigar {
408 let cigar_data = unsafe {
409 #[allow(clippy::cast_ptr_alignment)]
411 slice::from_raw_parts_mut(data[i..].as_ptr() as *mut u32, cigar_string.len())
412 };
413 for (i, c) in cigar_string.iter().enumerate() {
414 cigar_data[i] = c.encode();
415 }
416 self.inner_mut().core.n_cigar = cigar_string.len() as u32;
417 i += cigar_string.len() * 4;
418 } else {
419 self.inner_mut().core.n_cigar = 0;
420 };
421
422 {
424 for j in (0..seq.len()).step_by(2) {
425 data[i + j / 2] = (ENCODE_BASE[seq[j] as usize] << 4)
426 | (if j + 1 < seq.len() {
427 ENCODE_BASE[seq[j + 1] as usize]
428 } else {
429 0
430 });
431 }
432 self.inner_mut().core.l_qseq = seq.len() as i32;
433 i += seq.len().div_ceil(2);
434 }
435
436 utils::copy_memory(qual, &mut data[i..]);
438 }
439
440 pub fn set_qname(&mut self, new_qname: &[u8]) {
442 assert!(new_qname.len() < 252);
444
445 let old_q_len = self.qname_capacity();
446 let extranul = extranul_from_qname(new_qname);
448 let new_q_len = new_qname.len() + 1 + extranul;
449
450 let other_len = self.inner_mut().l_data - old_q_len as i32;
452
453 if new_q_len < old_q_len && self.inner().l_data > (old_q_len as i32) {
454 self.inner_mut().l_data -= (old_q_len - new_q_len) as i32;
455 } else if new_q_len > old_q_len {
456 self.inner_mut().l_data += (new_q_len - old_q_len) as i32;
457
458 if (self.inner().m_data as i32) < self.inner().l_data {
460 let l_data = self.inner().l_data;
462 self.realloc_var_data(l_data as usize);
463 }
464 }
465
466 if new_q_len != old_q_len {
467 unsafe {
469 let data = slice::from_raw_parts_mut(self.inner.data, self.inner().l_data as usize);
470
471 ::libc::memmove(
472 data.as_mut_ptr().add(new_q_len) as *mut ::libc::c_void,
473 data.as_mut_ptr().add(old_q_len) as *mut ::libc::c_void,
474 other_len as usize,
475 );
476 }
477 }
478
479 let data =
481 unsafe { slice::from_raw_parts_mut(self.inner.data, self.inner().l_data as usize) };
482 utils::copy_memory(new_qname, data);
483 for i in 0..=extranul {
484 data[new_q_len - i - 1] = b'\0';
485 }
486 self.inner_mut().core.l_qname = new_q_len as u16;
487 self.inner_mut().core.l_extranul = extranul as u8;
488 }
489
490 pub fn set_cigar(&mut self, new_cigar: Option<&CigarString>) {
492 self.cigar = None;
493
494 let qname_data_len = self.qname_capacity();
495 let old_cigar_data_len = self.cigar_len() * 4;
496
497 let other_data_len = self.inner_mut().l_data - (qname_data_len + old_cigar_data_len) as i32;
499
500 let new_cigar_len = match new_cigar {
501 Some(x) => x.len(),
502 None => 0,
503 };
504 let new_cigar_data_len = new_cigar_len * 4;
505
506 if new_cigar_data_len < old_cigar_data_len {
507 self.inner_mut().l_data -= (old_cigar_data_len - new_cigar_data_len) as i32;
508 } else if new_cigar_data_len > old_cigar_data_len {
509 self.inner_mut().l_data += (new_cigar_data_len - old_cigar_data_len) as i32;
510
511 if (self.inner().m_data as i32) < self.inner().l_data {
513 let l_data = self.inner().l_data;
515 self.realloc_var_data(l_data as usize);
516 }
517 }
518
519 if new_cigar_data_len != old_cigar_data_len {
520 unsafe {
522 ::libc::memmove(
523 self.inner.data.add(qname_data_len + new_cigar_data_len) as *mut ::libc::c_void,
524 self.inner.data.add(qname_data_len + old_cigar_data_len) as *mut ::libc::c_void,
525 other_data_len as usize,
526 );
527 }
528 }
529
530 if let Some(cigar_string) = new_cigar {
532 let cigar_data = unsafe {
533 #[allow(clippy::cast_ptr_alignment)]
534 slice::from_raw_parts_mut(
535 self.inner.data.add(qname_data_len) as *mut u32,
536 cigar_string.len(),
537 )
538 };
539 for (i, c) in cigar_string.iter().enumerate() {
540 cigar_data[i] = c.encode();
541 }
542 }
543 self.inner_mut().core.n_cigar = new_cigar_len as u32;
544 }
545
546 fn realloc_var_data(&mut self, new_len: usize) {
547 let new_len = new_len as u32;
549 let new_request = new_len + 32 - (new_len % 32);
550
551 let ptr = unsafe {
552 ::libc::realloc(
553 self.inner().data as *mut ::libc::c_void,
554 new_request as usize,
555 ) as *mut u8
556 };
557
558 if ptr.is_null() {
559 panic!("ran out of memory in rust_htslib trying to realloc");
560 }
561
562 self.inner_mut().m_data = new_request;
565 self.inner_mut().data = ptr;
566
567 self.own = true;
569 }
570
571 pub fn cigar_len(&self) -> usize {
572 self.inner().core.n_cigar as usize
573 }
574
575 pub fn raw_cigar(&self) -> &[u32] {
578 #[allow(clippy::cast_ptr_alignment)]
580 unsafe {
581 slice::from_raw_parts(
582 self.data()[self.qname_capacity()..].as_ptr() as *const u32,
583 self.cigar_len(),
584 )
585 }
586 }
587
588 pub fn cigar(&self) -> CigarStringView {
590 match self.cigar {
591 Some(ref c) => c.clone(),
592 None => self.unpack_cigar(),
593 }
594 }
595
596 pub fn cigar_cached(&self) -> Option<&CigarStringView> {
598 self.cigar.as_ref()
599 }
600
601 pub fn cache_cigar(&mut self) {
603 self.cigar = Some(self.unpack_cigar())
604 }
605
606 fn unpack_cigar(&self) -> CigarStringView {
608 CigarString(
609 self.raw_cigar()
610 .iter()
611 .map(|&c| {
612 let len = c >> 4;
613 match c & 0b1111 {
614 0 => Cigar::Match(len),
615 1 => Cigar::Ins(len),
616 2 => Cigar::Del(len),
617 3 => Cigar::RefSkip(len),
618 4 => Cigar::SoftClip(len),
619 5 => Cigar::HardClip(len),
620 6 => Cigar::Pad(len),
621 7 => Cigar::Equal(len),
622 8 => Cigar::Diff(len),
623 _ => panic!("Unexpected cigar operation"),
624 }
625 })
626 .collect(),
627 )
628 .into_view(self.pos())
629 }
630
631 pub fn seq_len(&self) -> usize {
632 self.inner().core.l_qseq as usize
633 }
634
635 fn seq_data(&self) -> &[u8] {
636 let offset = self.qname_capacity() + self.cigar_len() * 4;
637 &self.data()[offset..][..self.seq_len().div_ceil(2)]
638 }
639
640 pub fn seq(&self) -> Seq<'_> {
642 Seq {
643 encoded: self.seq_data(),
644 len: self.seq_len(),
645 }
646 }
647
648 pub fn qual(&self) -> &[u8] {
652 &self.data()[self.qname_capacity() + self.cigar_len() * 4 + self.seq_len().div_ceil(2)..]
653 [..self.seq_len()]
654 }
655
656 pub fn aux(&self, tag: &[u8]) -> Result<Aux<'_>> {
661 if tag.len() < 2 {
662 return Err(Error::BamAuxStringError);
663 }
664 let aux = unsafe {
665 htslib::bam_aux_get(
666 &self.inner as *const htslib::bam1_t,
667 tag.as_ptr() as *const c_char,
668 )
669 };
670 unsafe { Self::read_aux_field(aux).map(|(aux_field, _length)| aux_field) }
671 }
672
673 unsafe fn read_aux_field<'a>(aux: *const u8) -> Result<(Aux<'a>, usize)> {
674 const TAG_LEN: isize = 2;
675 const TYPE_ID_LEN: isize = 1;
677
678 if aux.is_null() {
679 return Err(Error::BamAuxTagNotFound);
680 }
681
682 let (data, type_size) = match *aux {
683 b'A' => {
684 let type_size = size_of::<u8>();
685 (Aux::Char(*aux.offset(TYPE_ID_LEN)), type_size)
686 }
687 b'c' => {
688 let type_size = size_of::<i8>();
689 (Aux::I8(*aux.offset(TYPE_ID_LEN).cast::<i8>()), type_size)
690 }
691 b'C' => {
692 let type_size = size_of::<u8>();
693 (Aux::U8(*aux.offset(TYPE_ID_LEN)), type_size)
694 }
695 b's' => {
696 let type_size = size_of::<i16>();
697 (
698 Aux::I16(
699 slice::from_raw_parts(aux.offset(TYPE_ID_LEN), type_size)
700 .read_i16::<LittleEndian>()
701 .map_err(|_| Error::BamAuxParsingError)?,
702 ),
703 type_size,
704 )
705 }
706 b'S' => {
707 let type_size = size_of::<u16>();
708 (
709 Aux::U16(
710 slice::from_raw_parts(aux.offset(TYPE_ID_LEN), type_size)
711 .read_u16::<LittleEndian>()
712 .map_err(|_| Error::BamAuxParsingError)?,
713 ),
714 type_size,
715 )
716 }
717 b'i' => {
718 let type_size = size_of::<i32>();
719 (
720 Aux::I32(
721 slice::from_raw_parts(aux.offset(TYPE_ID_LEN), type_size)
722 .read_i32::<LittleEndian>()
723 .map_err(|_| Error::BamAuxParsingError)?,
724 ),
725 type_size,
726 )
727 }
728 b'I' => {
729 let type_size = size_of::<u32>();
730 (
731 Aux::U32(
732 slice::from_raw_parts(aux.offset(TYPE_ID_LEN), type_size)
733 .read_u32::<LittleEndian>()
734 .map_err(|_| Error::BamAuxParsingError)?,
735 ),
736 type_size,
737 )
738 }
739 b'f' => {
740 let type_size = size_of::<f32>();
741 (
742 Aux::Float(
743 slice::from_raw_parts(aux.offset(TYPE_ID_LEN), type_size)
744 .read_f32::<LittleEndian>()
745 .map_err(|_| Error::BamAuxParsingError)?,
746 ),
747 type_size,
748 )
749 }
750 b'd' => {
751 let type_size = size_of::<f64>();
752 (
753 Aux::Double(
754 slice::from_raw_parts(aux.offset(TYPE_ID_LEN), type_size)
755 .read_f64::<LittleEndian>()
756 .map_err(|_| Error::BamAuxParsingError)?,
757 ),
758 type_size,
759 )
760 }
761 b'Z' | b'H' => {
762 let c_str = ffi::CStr::from_ptr(aux.offset(TYPE_ID_LEN).cast::<c_char>());
763 let rust_str = c_str.to_str().map_err(|_| Error::BamAuxParsingError)?;
764 (Aux::String(rust_str), c_str.to_bytes_with_nul().len())
765 }
766 b'B' => {
767 const ARRAY_INNER_TYPE_LEN: isize = 1;
768 const ARRAY_COUNT_LEN: isize = 4;
769
770 let array_data_offset = TYPE_ID_LEN + ARRAY_INNER_TYPE_LEN + ARRAY_COUNT_LEN;
772
773 let length =
774 slice::from_raw_parts(aux.offset(TYPE_ID_LEN + ARRAY_INNER_TYPE_LEN), 4)
775 .read_u32::<LittleEndian>()
776 .map_err(|_| Error::BamAuxParsingError)? as usize;
777
778 let (array_data, array_size) = match *aux.offset(TYPE_ID_LEN) {
780 b'c' => (
781 Aux::ArrayI8(AuxArray::<'a, i8>::from_bytes(slice::from_raw_parts(
782 aux.offset(array_data_offset),
783 length,
784 ))),
785 length,
786 ),
787 b'C' => (
788 Aux::ArrayU8(AuxArray::<'a, u8>::from_bytes(slice::from_raw_parts(
789 aux.offset(array_data_offset),
790 length,
791 ))),
792 length,
793 ),
794 b's' => (
795 Aux::ArrayI16(AuxArray::<'a, i16>::from_bytes(slice::from_raw_parts(
796 aux.offset(array_data_offset),
797 length * size_of::<i16>(),
798 ))),
799 length * std::mem::size_of::<i16>(),
800 ),
801 b'S' => (
802 Aux::ArrayU16(AuxArray::<'a, u16>::from_bytes(slice::from_raw_parts(
803 aux.offset(array_data_offset),
804 length * size_of::<u16>(),
805 ))),
806 length * std::mem::size_of::<u16>(),
807 ),
808 b'i' => (
809 Aux::ArrayI32(AuxArray::<'a, i32>::from_bytes(slice::from_raw_parts(
810 aux.offset(array_data_offset),
811 length * size_of::<i32>(),
812 ))),
813 length * std::mem::size_of::<i32>(),
814 ),
815 b'I' => (
816 Aux::ArrayU32(AuxArray::<'a, u32>::from_bytes(slice::from_raw_parts(
817 aux.offset(array_data_offset),
818 length * size_of::<u32>(),
819 ))),
820 length * std::mem::size_of::<u32>(),
821 ),
822 b'f' => (
823 Aux::ArrayFloat(AuxArray::<f32>::from_bytes(slice::from_raw_parts(
824 aux.offset(array_data_offset),
825 length * size_of::<f32>(),
826 ))),
827 length * std::mem::size_of::<f32>(),
828 ),
829 _ => {
830 return Err(Error::BamAuxUnknownType);
831 }
832 };
833 (
834 array_data,
835 ARRAY_INNER_TYPE_LEN as usize + ARRAY_COUNT_LEN as usize + array_size,
837 )
838 }
839 _ => {
840 return Err(Error::BamAuxUnknownType);
841 }
842 };
843
844 Ok((data, TAG_LEN as usize + TYPE_ID_LEN as usize + type_size))
846 }
847
848 pub fn aux_iter(&'_ self) -> AuxIter<'_> {
853 AuxIter {
854 aux: &self.data()[
857 self.qname_capacity()
859 + self.cigar_len() * std::mem::size_of::<u32>()
861 + self.seq_len().div_ceil(2)
863 + self.seq_len()..],
865 }
866 }
867
868 pub fn push_aux(&mut self, tag: &[u8], value: Aux<'_>) -> Result<()> {
870 if self.aux(tag).is_ok() {
874 return Err(Error::BamAuxTagAlreadyPresent);
875 }
876 self.push_aux_unchecked(tag, value)
877 }
878
879 pub fn push_aux_unchecked(&mut self, tag: &[u8], value: Aux<'_>) -> Result<()> {
884 let ctag = tag.as_ptr() as *mut c_char;
885 let ret = unsafe {
886 match value {
887 Aux::Char(v) => htslib::bam_aux_append(
888 self.inner_ptr_mut(),
889 ctag,
890 b'A' as c_char,
891 size_of::<u8>() as i32,
892 [v].as_mut_ptr(),
893 ),
894 Aux::I8(v) => htslib::bam_aux_append(
895 self.inner_ptr_mut(),
896 ctag,
897 b'c' as c_char,
898 size_of::<i8>() as i32,
899 [v].as_mut_ptr() as *mut u8,
900 ),
901 Aux::U8(v) => htslib::bam_aux_append(
902 self.inner_ptr_mut(),
903 ctag,
904 b'C' as c_char,
905 size_of::<u8>() as i32,
906 [v].as_mut_ptr(),
907 ),
908 Aux::I16(v) => htslib::bam_aux_append(
909 self.inner_ptr_mut(),
910 ctag,
911 b's' as c_char,
912 size_of::<i16>() as i32,
913 [v].as_mut_ptr() as *mut u8,
914 ),
915 Aux::U16(v) => htslib::bam_aux_append(
916 self.inner_ptr_mut(),
917 ctag,
918 b'S' as c_char,
919 size_of::<u16>() as i32,
920 [v].as_mut_ptr() as *mut u8,
921 ),
922 Aux::I32(v) => htslib::bam_aux_append(
923 self.inner_ptr_mut(),
924 ctag,
925 b'i' as c_char,
926 size_of::<i32>() as i32,
927 [v].as_mut_ptr() as *mut u8,
928 ),
929 Aux::U32(v) => htslib::bam_aux_append(
930 self.inner_ptr_mut(),
931 ctag,
932 b'I' as c_char,
933 size_of::<u32>() as i32,
934 [v].as_mut_ptr() as *mut u8,
935 ),
936 Aux::Float(v) => htslib::bam_aux_append(
937 self.inner_ptr_mut(),
938 ctag,
939 b'f' as c_char,
940 size_of::<f32>() as i32,
941 [v].as_mut_ptr() as *mut u8,
942 ),
943 Aux::Double(v) => htslib::bam_aux_append(
945 self.inner_ptr_mut(),
946 ctag,
947 b'd' as c_char,
948 size_of::<f64>() as i32,
949 [v].as_mut_ptr() as *mut u8,
950 ),
951 Aux::String(v) => {
952 let c_str = ffi::CString::new(v).map_err(|_| Error::BamAuxStringError)?;
953 htslib::bam_aux_append(
954 self.inner_ptr_mut(),
955 ctag,
956 b'Z' as c_char,
957 (v.len() + 1) as i32,
958 c_str.as_ptr() as *mut u8,
959 )
960 }
961 Aux::HexByteArray(v) => {
962 let c_str = ffi::CString::new(v).map_err(|_| Error::BamAuxStringError)?;
963 htslib::bam_aux_append(
964 self.inner_ptr_mut(),
965 ctag,
966 b'H' as c_char,
967 (v.len() + 1) as i32,
968 c_str.as_ptr() as *mut u8,
969 )
970 }
971 Aux::ArrayI8(aux_array) => match aux_array {
973 AuxArray::TargetType(inner) => htslib::bam_aux_update_array(
974 self.inner_ptr_mut(),
975 ctag,
976 b'c',
977 inner.len() as u32,
978 inner.slice.as_ptr() as *mut ::libc::c_void,
979 ),
980 AuxArray::RawLeBytes(inner) => htslib::bam_aux_update_array(
981 self.inner_ptr_mut(),
982 ctag,
983 b'c',
984 inner.len() as u32,
985 inner.slice.as_ptr() as *mut ::libc::c_void,
986 ),
987 },
988 Aux::ArrayU8(aux_array) => match aux_array {
989 AuxArray::TargetType(inner) => htslib::bam_aux_update_array(
990 self.inner_ptr_mut(),
991 ctag,
992 b'C',
993 inner.len() as u32,
994 inner.slice.as_ptr() as *mut ::libc::c_void,
995 ),
996 AuxArray::RawLeBytes(inner) => htslib::bam_aux_update_array(
997 self.inner_ptr_mut(),
998 ctag,
999 b'C',
1000 inner.len() as u32,
1001 inner.slice.as_ptr() as *mut ::libc::c_void,
1002 ),
1003 },
1004 Aux::ArrayI16(aux_array) => match aux_array {
1005 AuxArray::TargetType(inner) => htslib::bam_aux_update_array(
1006 self.inner_ptr_mut(),
1007 ctag,
1008 b's',
1009 inner.len() as u32,
1010 inner.slice.as_ptr() as *mut ::libc::c_void,
1011 ),
1012 AuxArray::RawLeBytes(inner) => htslib::bam_aux_update_array(
1013 self.inner_ptr_mut(),
1014 ctag,
1015 b's',
1016 inner.len() as u32,
1017 inner.slice.as_ptr() as *mut ::libc::c_void,
1018 ),
1019 },
1020 Aux::ArrayU16(aux_array) => match aux_array {
1021 AuxArray::TargetType(inner) => htslib::bam_aux_update_array(
1022 self.inner_ptr_mut(),
1023 ctag,
1024 b'S',
1025 inner.len() as u32,
1026 inner.slice.as_ptr() as *mut ::libc::c_void,
1027 ),
1028 AuxArray::RawLeBytes(inner) => htslib::bam_aux_update_array(
1029 self.inner_ptr_mut(),
1030 ctag,
1031 b'S',
1032 inner.len() as u32,
1033 inner.slice.as_ptr() as *mut ::libc::c_void,
1034 ),
1035 },
1036 Aux::ArrayI32(aux_array) => match aux_array {
1037 AuxArray::TargetType(inner) => htslib::bam_aux_update_array(
1038 self.inner_ptr_mut(),
1039 ctag,
1040 b'i',
1041 inner.len() as u32,
1042 inner.slice.as_ptr() as *mut ::libc::c_void,
1043 ),
1044 AuxArray::RawLeBytes(inner) => htslib::bam_aux_update_array(
1045 self.inner_ptr_mut(),
1046 ctag,
1047 b'i',
1048 inner.len() as u32,
1049 inner.slice.as_ptr() as *mut ::libc::c_void,
1050 ),
1051 },
1052 Aux::ArrayU32(aux_array) => match aux_array {
1053 AuxArray::TargetType(inner) => htslib::bam_aux_update_array(
1054 self.inner_ptr_mut(),
1055 ctag,
1056 b'I',
1057 inner.len() as u32,
1058 inner.slice.as_ptr() as *mut ::libc::c_void,
1059 ),
1060 AuxArray::RawLeBytes(inner) => htslib::bam_aux_update_array(
1061 self.inner_ptr_mut(),
1062 ctag,
1063 b'I',
1064 inner.len() as u32,
1065 inner.slice.as_ptr() as *mut ::libc::c_void,
1066 ),
1067 },
1068 Aux::ArrayFloat(aux_array) => match aux_array {
1069 AuxArray::TargetType(inner) => htslib::bam_aux_update_array(
1070 self.inner_ptr_mut(),
1071 ctag,
1072 b'f',
1073 inner.len() as u32,
1074 inner.slice.as_ptr() as *mut ::libc::c_void,
1075 ),
1076 AuxArray::RawLeBytes(inner) => htslib::bam_aux_update_array(
1077 self.inner_ptr_mut(),
1078 ctag,
1079 b'f',
1080 inner.len() as u32,
1081 inner.slice.as_ptr() as *mut ::libc::c_void,
1082 ),
1083 },
1084 }
1085 };
1086
1087 if ret < 0 { Err(Error::BamAux) } else { Ok(()) }
1088 }
1089
1090 pub fn update_aux(&mut self, tag: &[u8], value: Aux<'_>) -> Result<()> {
1092 let ctag = tag.as_ptr() as *mut c_char;
1097 let ret = unsafe {
1098 match value {
1099 Aux::Char(_v) => return Err(Error::BamAuxTagUpdatingNotSupported),
1100 Aux::I8(v) => htslib::bam_aux_update_int(self.inner_ptr_mut(), ctag, v as i64),
1101 Aux::U8(v) => htslib::bam_aux_update_int(self.inner_ptr_mut(), ctag, v as i64),
1102 Aux::I16(v) => htslib::bam_aux_update_int(self.inner_ptr_mut(), ctag, v as i64),
1103 Aux::U16(v) => htslib::bam_aux_update_int(self.inner_ptr_mut(), ctag, v as i64),
1104 Aux::I32(v) => htslib::bam_aux_update_int(self.inner_ptr_mut(), ctag, v as i64),
1105 Aux::U32(v) => htslib::bam_aux_update_int(self.inner_ptr_mut(), ctag, v as i64),
1106 Aux::Float(v) => htslib::bam_aux_update_float(self.inner_ptr_mut(), ctag, v),
1107 Aux::Double(v) => {
1109 htslib::bam_aux_update_float(self.inner_ptr_mut(), ctag, v as f32)
1110 }
1111 Aux::String(v) => {
1112 let c_str = ffi::CString::new(v).map_err(|_| Error::BamAuxStringError)?;
1113 htslib::bam_aux_update_str(
1114 self.inner_ptr_mut(),
1115 ctag,
1116 (v.len() + 1) as i32,
1117 c_str.as_ptr() as *const c_char,
1118 )
1119 }
1120 Aux::HexByteArray(_v) => return Err(Error::BamAuxTagUpdatingNotSupported),
1121 Aux::ArrayI8(aux_array) => match aux_array {
1123 AuxArray::TargetType(inner) => htslib::bam_aux_update_array(
1124 self.inner_ptr_mut(),
1125 ctag,
1126 b'c',
1127 inner.len() as u32,
1128 inner.slice.as_ptr() as *mut ::libc::c_void,
1129 ),
1130 AuxArray::RawLeBytes(inner) => htslib::bam_aux_update_array(
1131 self.inner_ptr_mut(),
1132 ctag,
1133 b'c',
1134 inner.len() as u32,
1135 inner.slice.as_ptr() as *mut ::libc::c_void,
1136 ),
1137 },
1138 Aux::ArrayU8(aux_array) => match aux_array {
1139 AuxArray::TargetType(inner) => htslib::bam_aux_update_array(
1140 self.inner_ptr_mut(),
1141 ctag,
1142 b'C',
1143 inner.len() as u32,
1144 inner.slice.as_ptr() as *mut ::libc::c_void,
1145 ),
1146 AuxArray::RawLeBytes(inner) => htslib::bam_aux_update_array(
1147 self.inner_ptr_mut(),
1148 ctag,
1149 b'C',
1150 inner.len() as u32,
1151 inner.slice.as_ptr() as *mut ::libc::c_void,
1152 ),
1153 },
1154 Aux::ArrayI16(aux_array) => match aux_array {
1155 AuxArray::TargetType(inner) => htslib::bam_aux_update_array(
1156 self.inner_ptr_mut(),
1157 ctag,
1158 b's',
1159 inner.len() as u32,
1160 inner.slice.as_ptr() as *mut ::libc::c_void,
1161 ),
1162 AuxArray::RawLeBytes(inner) => htslib::bam_aux_update_array(
1163 self.inner_ptr_mut(),
1164 ctag,
1165 b's',
1166 inner.len() as u32,
1167 inner.slice.as_ptr() as *mut ::libc::c_void,
1168 ),
1169 },
1170 Aux::ArrayU16(aux_array) => match aux_array {
1171 AuxArray::TargetType(inner) => htslib::bam_aux_update_array(
1172 self.inner_ptr_mut(),
1173 ctag,
1174 b'S',
1175 inner.len() as u32,
1176 inner.slice.as_ptr() as *mut ::libc::c_void,
1177 ),
1178 AuxArray::RawLeBytes(inner) => htslib::bam_aux_update_array(
1179 self.inner_ptr_mut(),
1180 ctag,
1181 b'S',
1182 inner.len() as u32,
1183 inner.slice.as_ptr() as *mut ::libc::c_void,
1184 ),
1185 },
1186 Aux::ArrayI32(aux_array) => match aux_array {
1187 AuxArray::TargetType(inner) => htslib::bam_aux_update_array(
1188 self.inner_ptr_mut(),
1189 ctag,
1190 b'i',
1191 inner.len() as u32,
1192 inner.slice.as_ptr() as *mut ::libc::c_void,
1193 ),
1194 AuxArray::RawLeBytes(inner) => htslib::bam_aux_update_array(
1195 self.inner_ptr_mut(),
1196 ctag,
1197 b'i',
1198 inner.len() as u32,
1199 inner.slice.as_ptr() as *mut ::libc::c_void,
1200 ),
1201 },
1202 Aux::ArrayU32(aux_array) => match aux_array {
1203 AuxArray::TargetType(inner) => htslib::bam_aux_update_array(
1204 self.inner_ptr_mut(),
1205 ctag,
1206 b'I',
1207 inner.len() as u32,
1208 inner.slice.as_ptr() as *mut ::libc::c_void,
1209 ),
1210 AuxArray::RawLeBytes(inner) => htslib::bam_aux_update_array(
1211 self.inner_ptr_mut(),
1212 ctag,
1213 b'I',
1214 inner.len() as u32,
1215 inner.slice.as_ptr() as *mut ::libc::c_void,
1216 ),
1217 },
1218 Aux::ArrayFloat(aux_array) => match aux_array {
1219 AuxArray::TargetType(inner) => htslib::bam_aux_update_array(
1220 self.inner_ptr_mut(),
1221 ctag,
1222 b'f',
1223 inner.len() as u32,
1224 inner.slice.as_ptr() as *mut ::libc::c_void,
1225 ),
1226 AuxArray::RawLeBytes(inner) => htslib::bam_aux_update_array(
1227 self.inner_ptr_mut(),
1228 ctag,
1229 b'f',
1230 inner.len() as u32,
1231 inner.slice.as_ptr() as *mut ::libc::c_void,
1232 ),
1233 },
1234 }
1235 };
1236
1237 if ret < 0 { Err(Error::BamAux) } else { Ok(()) }
1238 }
1239
1240 pub fn remove_aux(&mut self, tag: &[u8]) -> Result<()> {
1242 if tag.len() < 2 {
1243 return Err(Error::BamAuxStringError);
1244 }
1245 let aux = unsafe {
1246 htslib::bam_aux_get(
1247 &self.inner as *const htslib::bam1_t,
1248 tag.as_ptr() as *const c_char,
1249 )
1250 };
1251 unsafe {
1252 if aux.is_null() {
1253 Err(Error::BamAuxTagNotFound)
1254 } else {
1255 htslib::bam_aux_del(self.inner_ptr_mut(), aux);
1256 Ok(())
1257 }
1258 }
1259 }
1260
1261 pub fn basemods_iter(&'_ self) -> Result<BaseModificationsIter<'_>> {
1293 BaseModificationsIter::new(self)
1294 }
1295 pub fn is_primary(&self) -> bool {
1314 self.flags() & 0x900 == 0
1315 }
1316 pub fn basemods_position_iter(&'_ self) -> Result<BaseModificationsPositionIter<'_>> {
1320 BaseModificationsPositionIter::new(self)
1321 }
1322
1323 pub fn read_pair_orientation(&self) -> SequenceReadPairOrientation {
1327 if self.is_paired()
1328 && !self.is_unmapped()
1329 && !self.is_mate_unmapped()
1330 && self.tid() == self.mtid()
1331 {
1332 if self.pos() == self.mpos() {
1333 return SequenceReadPairOrientation::None;
1335 }
1336
1337 let (pos_1, pos_2, fwd_1, fwd_2) = if self.is_first_in_template() {
1338 (
1339 self.pos(),
1340 self.mpos(),
1341 !self.is_reverse(),
1342 !self.is_mate_reverse(),
1343 )
1344 } else {
1345 (
1346 self.mpos(),
1347 self.pos(),
1348 !self.is_mate_reverse(),
1349 !self.is_reverse(),
1350 )
1351 };
1352
1353 if pos_1 < pos_2 {
1354 match (fwd_1, fwd_2) {
1355 (true, true) => SequenceReadPairOrientation::F1F2,
1356 (true, false) => SequenceReadPairOrientation::F1R2,
1357 (false, true) => SequenceReadPairOrientation::R1F2,
1358 (false, false) => SequenceReadPairOrientation::R1R2,
1359 }
1360 } else {
1361 match (fwd_2, fwd_1) {
1362 (true, true) => SequenceReadPairOrientation::F2F1,
1363 (true, false) => SequenceReadPairOrientation::F2R1,
1364 (false, true) => SequenceReadPairOrientation::R2F1,
1365 (false, false) => SequenceReadPairOrientation::R2R1,
1366 }
1367 }
1368 } else {
1369 SequenceReadPairOrientation::None
1370 }
1371 }
1372
1373 flag!(is_paired, set_paired, unset_paired, 1u16);
1374 flag!(is_proper_pair, set_proper_pair, unset_proper_pair, 2u16);
1375 flag!(is_unmapped, set_unmapped, unset_unmapped, 4u16);
1376 flag!(
1377 is_mate_unmapped,
1378 set_mate_unmapped,
1379 unset_mate_unmapped,
1380 8u16
1381 );
1382 flag!(is_reverse, set_reverse, unset_reverse, 16u16);
1383 flag!(is_mate_reverse, set_mate_reverse, unset_mate_reverse, 32u16);
1384 flag!(
1385 is_first_in_template,
1386 set_first_in_template,
1387 unset_first_in_template,
1388 64u16
1389 );
1390 flag!(
1391 is_last_in_template,
1392 set_last_in_template,
1393 unset_last_in_template,
1394 128u16
1395 );
1396 flag!(is_secondary, set_secondary, unset_secondary, 256u16);
1397 flag!(
1398 is_quality_check_failed,
1399 set_quality_check_failed,
1400 unset_quality_check_failed,
1401 512u16
1402 );
1403 flag!(is_duplicate, set_duplicate, unset_duplicate, 1024u16);
1404 flag!(
1405 is_supplementary,
1406 set_supplementary,
1407 unset_supplementary,
1408 2048u16
1409 );
1410}
1411
1412impl Drop for Record {
1413 fn drop(&mut self) {
1414 if self.own {
1415 unsafe { ::libc::free(self.inner.data as *mut ::libc::c_void) }
1416 }
1417 }
1418}
1419
1420impl SequenceRead for Record {
1421 fn name(&self) -> &[u8] {
1422 self.qname()
1423 }
1424
1425 fn base(&self, i: usize) -> u8 {
1426 *decode_base_unchecked(encoded_base(self.seq_data(), i))
1427 }
1428
1429 fn base_qual(&self, i: usize) -> u8 {
1430 self.qual()[i]
1431 }
1432
1433 fn len(&self) -> usize {
1434 self.seq_len()
1435 }
1436
1437 fn is_empty(&self) -> bool {
1438 self.len() == 0
1439 }
1440}
1441
1442impl genome::AbstractInterval for Record {
1443 fn contig(&self) -> &str {
1445 let tid = self.tid();
1446 if tid < 0 {
1447 panic!("invalid tid, must be at least zero");
1448 }
1449 str::from_utf8(
1450 self.header
1451 .as_ref()
1452 .expect(
1453 "header must be set (this is the case if the record has been read from a file)",
1454 )
1455 .tid2name(tid as u32),
1456 )
1457 .expect("unable to interpret contig name as UTF-8")
1458 }
1459
1460 fn range(&self) -> ops::Range<genome::Position> {
1462 let end_pos = self
1463 .cigar_cached()
1464 .expect("cigar has not been cached yet, call cache_cigar() first")
1465 .end_pos() as u64;
1466
1467 if self.pos() < 0 {
1468 panic!("invalid position, must be positive")
1469 }
1470
1471 self.pos() as u64..end_pos
1472 }
1473}
1474
1475#[derive(Debug, PartialEq)]
1530pub enum Aux<'a> {
1531 Char(u8),
1532 I8(i8),
1533 U8(u8),
1534 I16(i16),
1535 U16(u16),
1536 I32(i32),
1537 U32(u32),
1538 Float(f32),
1539 Double(f64), String(&'a str),
1541 HexByteArray(&'a str),
1542 ArrayI8(AuxArray<'a, i8>),
1543 ArrayU8(AuxArray<'a, u8>),
1544 ArrayI16(AuxArray<'a, i16>),
1545 ArrayU16(AuxArray<'a, u16>),
1546 ArrayI32(AuxArray<'a, i32>),
1547 ArrayU32(AuxArray<'a, u32>),
1548 ArrayFloat(AuxArray<'a, f32>),
1549}
1550
1551unsafe impl Send for Aux<'_> {}
1552unsafe impl Sync for Aux<'_> {}
1553
1554pub trait AuxArrayElement: Copy {
1556 fn from_le_bytes(bytes: &[u8]) -> Option<Self>;
1557}
1558
1559impl AuxArrayElement for i8 {
1560 fn from_le_bytes(bytes: &[u8]) -> Option<Self> {
1561 std::io::Cursor::new(bytes).read_i8().ok()
1562 }
1563}
1564impl AuxArrayElement for u8 {
1565 fn from_le_bytes(bytes: &[u8]) -> Option<Self> {
1566 std::io::Cursor::new(bytes).read_u8().ok()
1567 }
1568}
1569impl AuxArrayElement for i16 {
1570 fn from_le_bytes(bytes: &[u8]) -> Option<Self> {
1571 std::io::Cursor::new(bytes).read_i16::<LittleEndian>().ok()
1572 }
1573}
1574impl AuxArrayElement for u16 {
1575 fn from_le_bytes(bytes: &[u8]) -> Option<Self> {
1576 std::io::Cursor::new(bytes).read_u16::<LittleEndian>().ok()
1577 }
1578}
1579impl AuxArrayElement for i32 {
1580 fn from_le_bytes(bytes: &[u8]) -> Option<Self> {
1581 std::io::Cursor::new(bytes).read_i32::<LittleEndian>().ok()
1582 }
1583}
1584impl AuxArrayElement for u32 {
1585 fn from_le_bytes(bytes: &[u8]) -> Option<Self> {
1586 std::io::Cursor::new(bytes).read_u32::<LittleEndian>().ok()
1587 }
1588}
1589impl AuxArrayElement for f32 {
1590 fn from_le_bytes(bytes: &[u8]) -> Option<Self> {
1591 std::io::Cursor::new(bytes).read_f32::<LittleEndian>().ok()
1592 }
1593}
1594
1595#[derive(Debug)]
1639pub enum AuxArray<'a, T> {
1640 TargetType(AuxArrayTargetType<'a, T>),
1641 RawLeBytes(AuxArrayRawLeBytes<'a, T>),
1642}
1643
1644impl<T> PartialEq<AuxArray<'_, T>> for AuxArray<'_, T>
1645where
1646 T: AuxArrayElement + PartialEq,
1647{
1648 fn eq(&self, other: &AuxArray<'_, T>) -> bool {
1649 use AuxArray::*;
1650 match (self, other) {
1651 (TargetType(v), TargetType(v_other)) => v == v_other,
1652 (RawLeBytes(v), RawLeBytes(v_other)) => v == v_other,
1653 (TargetType(_), RawLeBytes(_)) => self.iter().eq(other.iter()),
1654 (RawLeBytes(_), TargetType(_)) => self.iter().eq(other.iter()),
1655 }
1656 }
1657}
1658
1659impl<'a, I, T> From<&'a T> for AuxArray<'a, I>
1661where
1662 I: AuxArrayElement,
1663 T: AsRef<[I]> + ?Sized,
1664{
1665 fn from(src: &'a T) -> Self {
1666 AuxArray::TargetType(AuxArrayTargetType {
1667 slice: src.as_ref(),
1668 })
1669 }
1670}
1671
1672impl<'a, T> AuxArray<'a, T>
1673where
1674 T: AuxArrayElement,
1675{
1676 pub fn get(&self, index: usize) -> Option<T> {
1678 match self {
1679 AuxArray::TargetType(v) => v.get(index),
1680 AuxArray::RawLeBytes(v) => v.get(index),
1681 }
1682 }
1683
1684 pub fn len(&self) -> usize {
1686 match self {
1687 AuxArray::TargetType(a) => a.len(),
1688 AuxArray::RawLeBytes(a) => a.len(),
1689 }
1690 }
1691
1692 pub fn is_empty(&self) -> bool {
1694 self.len() == 0
1695 }
1696
1697 pub fn iter(&'_ self) -> AuxArrayIter<'_, T> {
1699 AuxArrayIter {
1700 index: 0,
1701 array: self,
1702 }
1703 }
1704
1705 fn from_bytes(bytes: &'a [u8]) -> Self {
1707 Self::RawLeBytes(AuxArrayRawLeBytes {
1708 slice: bytes,
1709 phantom_data: PhantomData,
1710 })
1711 }
1712}
1713
1714#[doc(hidden)]
1716#[derive(Debug, PartialEq)]
1717pub struct AuxArrayTargetType<'a, T> {
1718 slice: &'a [T],
1719}
1720
1721impl<T> AuxArrayTargetType<'_, T>
1722where
1723 T: AuxArrayElement,
1724{
1725 fn get(&self, index: usize) -> Option<T> {
1726 self.slice.get(index).copied()
1727 }
1728
1729 fn len(&self) -> usize {
1730 self.slice.len()
1731 }
1732}
1733
1734#[doc(hidden)]
1736#[derive(Debug, PartialEq)]
1737pub struct AuxArrayRawLeBytes<'a, T> {
1738 slice: &'a [u8],
1739 phantom_data: PhantomData<T>,
1740}
1741
1742impl<T> AuxArrayRawLeBytes<'_, T>
1743where
1744 T: AuxArrayElement,
1745{
1746 fn get(&self, index: usize) -> Option<T> {
1747 let type_size = std::mem::size_of::<T>();
1748 if index * type_size + type_size > self.slice.len() {
1749 return None;
1750 }
1751 T::from_le_bytes(&self.slice[index * type_size..][..type_size])
1752 }
1753
1754 fn len(&self) -> usize {
1755 self.slice.len() / std::mem::size_of::<T>()
1756 }
1757}
1758
1759pub struct AuxArrayIter<'a, T> {
1763 index: usize,
1764 array: &'a AuxArray<'a, T>,
1765}
1766
1767impl<T> Iterator for AuxArrayIter<'_, T>
1768where
1769 T: AuxArrayElement,
1770{
1771 type Item = T;
1772 fn next(&mut self) -> Option<Self::Item> {
1773 let value = self.array.get(self.index);
1774 self.index += 1;
1775 value
1776 }
1777
1778 fn size_hint(&self) -> (usize, Option<usize>) {
1779 let array_length = self.array.len() - self.index;
1780 (array_length, Some(array_length))
1781 }
1782}
1783
1784pub struct AuxIter<'a> {
1795 aux: &'a [u8],
1796}
1797
1798impl<'a> Iterator for AuxIter<'a> {
1799 type Item = Result<(&'a [u8], Aux<'a>)>;
1800
1801 fn next(&mut self) -> Option<Self::Item> {
1802 if self.aux.is_empty() {
1804 return None;
1805 }
1806 if (1..=3).contains(&self.aux.len()) {
1808 self.aux = &[];
1810 return Some(Err(Error::BamAuxParsingError));
1811 }
1812 let tag = &self.aux[..2];
1813 Some(unsafe {
1814 let data_ptr = self.aux[2..].as_ptr();
1815 Record::read_aux_field(data_ptr)
1816 .map(|(aux, offset)| {
1817 self.aux = &self.aux[offset..];
1818 (tag, aux)
1819 })
1820 .inspect_err(|_e| {
1821 self.aux = &[];
1823 })
1824 })
1825 }
1826}
1827
1828static DECODE_BASE: &[u8] = b"=ACMGRSVTWYHKDBN";
1829static ENCODE_BASE: [u8; 256] = [
1830 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
1831 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
1832 1, 2, 4, 8, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 1, 14, 2, 13, 15, 15, 4, 11, 15,
1833 15, 12, 15, 3, 15, 15, 15, 15, 5, 6, 8, 15, 7, 9, 15, 10, 15, 15, 15, 15, 15, 15, 15, 1, 14, 2,
1834 13, 15, 15, 4, 11, 15, 15, 12, 15, 3, 15, 15, 15, 15, 5, 6, 8, 15, 7, 9, 15, 10, 15, 15, 15,
1835 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
1836 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
1837 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
1838 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
1839 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
1840 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
1841];
1842
1843#[inline]
1844fn encoded_base(encoded_seq: &[u8], i: usize) -> u8 {
1845 (encoded_seq[i / 2] >> ((!i & 1) << 2)) & 0b1111
1846}
1847
1848#[inline]
1849unsafe fn encoded_base_unchecked(encoded_seq: &[u8], i: usize) -> u8 {
1850 (encoded_seq.get_unchecked(i / 2) >> ((!i & 1) << 2)) & 0b1111
1851}
1852
1853#[inline]
1854fn decode_base_unchecked(base: u8) -> &'static u8 {
1855 unsafe { DECODE_BASE.get_unchecked(base as usize) }
1856}
1857
1858#[derive(Debug, Copy, Clone)]
1860pub struct Seq<'a> {
1861 pub encoded: &'a [u8],
1862 len: usize,
1863}
1864
1865impl Seq<'_> {
1866 #[inline]
1868 pub fn encoded_base(&self, i: usize) -> u8 {
1869 encoded_base(self.encoded, i)
1870 }
1871
1872 #[inline]
1878 pub unsafe fn encoded_base_unchecked(&self, i: usize) -> u8 {
1879 encoded_base_unchecked(self.encoded, i)
1880 }
1881
1882 #[inline]
1890 pub unsafe fn decoded_base_unchecked(&self, i: usize) -> u8 {
1891 *decode_base_unchecked(self.encoded_base_unchecked(i))
1892 }
1893
1894 pub fn as_bytes(&self) -> Vec<u8> {
1896 (0..self.len()).map(|i| self[i]).collect()
1897 }
1898 pub fn rangeextract<'a, T>(&'a self, range: T) -> std::io::Result<Cow<'a, str>>
1900 where
1901 T: RangeBounds<usize> + SliceIndex<str>,
1902 <T as SliceIndex<str>>::Output: std::string::ToString,
1903 {
1904 if self.is_empty() {
1905 return Err(std::io::Error::from(std::io::ErrorKind::InvalidInput));
1906 }
1907 let seq = match String::from_utf8(self.as_bytes().to_vec()) {
1908 Ok(d) if d.is_ascii() => d,
1909 _ => return Err(std::io::Error::from(std::io::ErrorKind::InvalidData)),
1910 };
1911 let substring = match seq.get(range) {
1912 Some(d) => d,
1913 None => return Err(std::io::Error::from(std::io::ErrorKind::InvalidInput)),
1914 };
1915
1916 Ok(Cow::Owned(substring.to_string()))
1917 }
1918 pub fn len(&self) -> usize {
1920 self.len
1921 }
1922
1923 pub fn is_empty(&self) -> bool {
1924 self.len() == 0
1925 }
1926}
1927
1928impl ops::Index<usize> for Seq<'_> {
1929 type Output = u8;
1930
1931 fn index(&self, index: usize) -> &u8 {
1933 decode_base_unchecked(self.encoded_base(index))
1934 }
1935}
1936
1937unsafe impl Send for Seq<'_> {}
1938unsafe impl Sync for Seq<'_> {}
1939
1940#[cfg_attr(feature = "serde_feature", derive(Serialize, Deserialize))]
1941#[derive(PartialEq, PartialOrd, Eq, Debug, Clone, Copy, Hash)]
1942pub enum Cigar {
1944 Match(u32), Ins(u32), Del(u32), RefSkip(u32), SoftClip(u32), HardClip(u32), Pad(u32), Equal(u32), Diff(u32), }
1954
1955impl Cigar {
1956 fn encode(self) -> u32 {
1957 match self {
1958 Cigar::Match(len) => len << 4, Cigar::Ins(len) => (len << 4) | 1,
1960 Cigar::Del(len) => (len << 4) | 2,
1961 Cigar::RefSkip(len) => (len << 4) | 3,
1962 Cigar::SoftClip(len) => (len << 4) | 4,
1963 Cigar::HardClip(len) => (len << 4) | 5,
1964 Cigar::Pad(len) => (len << 4) | 6,
1965 Cigar::Equal(len) => (len << 4) | 7,
1966 Cigar::Diff(len) => (len << 4) | 8,
1967 }
1968 }
1969
1970 pub fn len(self) -> u32 {
1972 match self {
1973 Cigar::Match(len) => len,
1974 Cigar::Ins(len) => len,
1975 Cigar::Del(len) => len,
1976 Cigar::RefSkip(len) => len,
1977 Cigar::SoftClip(len) => len,
1978 Cigar::HardClip(len) => len,
1979 Cigar::Pad(len) => len,
1980 Cigar::Equal(len) => len,
1981 Cigar::Diff(len) => len,
1982 }
1983 }
1984
1985 pub fn is_empty(self) -> bool {
1986 self.len() == 0
1987 }
1988
1989 pub fn char(self) -> char {
1991 match self {
1992 Cigar::Match(_) => 'M',
1993 Cigar::Ins(_) => 'I',
1994 Cigar::Del(_) => 'D',
1995 Cigar::RefSkip(_) => 'N',
1996 Cigar::SoftClip(_) => 'S',
1997 Cigar::HardClip(_) => 'H',
1998 Cigar::Pad(_) => 'P',
1999 Cigar::Equal(_) => '=',
2000 Cigar::Diff(_) => 'X',
2001 }
2002 }
2003}
2004
2005impl fmt::Display for Cigar {
2006 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
2007 fmt.write_fmt(format_args!("{}{}", self.len(), self.char()))
2008 }
2009}
2010
2011unsafe impl Send for Cigar {}
2012unsafe impl Sync for Cigar {}
2013
2014custom_derive! {
2015 #[cfg_attr(feature = "serde_feature", derive(Serialize, Deserialize))]
2034 #[derive(NewtypeDeref,
2035 NewtypeDerefMut,
2036 NewtypeIndex(usize),
2037 NewtypeIndexMut(usize),
2038 NewtypeFrom,
2039 PartialEq,
2040 PartialOrd,
2041 Eq,
2042 NewtypeDebug,
2043 Clone,
2044 Hash
2045 )]
2046 pub struct CigarString(pub Vec<Cigar>);
2047}
2048
2049impl CigarString {
2050 pub fn into_view(self, pos: i64) -> CigarStringView {
2052 CigarStringView::new(self, pos)
2053 }
2054 pub fn containsequal(&self) -> bool {
2056 self.iter()
2057 .any(|f| matches!(f, &Cigar::Equal(_)) || matches!(f, &Cigar::Diff(_)))
2058 || self.iter().all(|f| !matches!(f, &Cigar::Match(_)))
2059 }
2060 pub fn from_alignment(alignment: &Alignment, hard_clip: bool) -> Self {
2065 match alignment.mode {
2066 AlignmentMode::Global => {
2067 panic!(" Bam cigar fn not supported for Global Alignment mode")
2068 }
2069 AlignmentMode::Local => panic!(" Bam cigar fn not supported for Local Alignment mode"),
2070 _ => {}
2071 }
2072
2073 let mut cigar = Vec::new();
2074 if alignment.operations.is_empty() {
2075 return CigarString(cigar);
2076 }
2077
2078 let add_op = |op: AlignmentOperation, length: u32, cigar: &mut Vec<Cigar>| match op {
2079 AlignmentOperation::Del => cigar.push(Cigar::Del(length)),
2080 AlignmentOperation::Ins => cigar.push(Cigar::Ins(length)),
2081 AlignmentOperation::Subst => cigar.push(Cigar::Diff(length)),
2082 AlignmentOperation::Match => cigar.push(Cigar::Equal(length)),
2083 _ => {}
2084 };
2085
2086 if alignment.xstart > 0 {
2087 cigar.push(if hard_clip {
2088 Cigar::HardClip(alignment.xstart as u32)
2089 } else {
2090 Cigar::SoftClip(alignment.xstart as u32)
2091 });
2092 }
2093
2094 let mut last = alignment.operations[0];
2095 let mut k = 1u32;
2096 for &op in alignment.operations[1..].iter() {
2097 if op == last {
2098 k += 1;
2099 } else {
2100 add_op(last, k, &mut cigar);
2101 k = 1;
2102 }
2103 last = op;
2104 }
2105 add_op(last, k, &mut cigar);
2106 if alignment.xlen > alignment.xend {
2107 cigar.push(if hard_clip {
2108 Cigar::HardClip((alignment.xlen - alignment.xend) as u32)
2109 } else {
2110 Cigar::SoftClip((alignment.xlen - alignment.xend) as u32)
2111 });
2112 }
2113
2114 CigarString(cigar)
2115 }
2116}
2117
2118impl TryFrom<&[u8]> for CigarString {
2119 type Error = Error;
2120
2121 fn try_from(bytes: &[u8]) -> Result<Self> {
2142 let mut inner = Vec::new();
2143 let mut i = 0;
2144 let text_len = bytes.len();
2145 while i < text_len {
2146 let mut j = i;
2147 while j < text_len && bytes[j].is_ascii_digit() {
2148 j += 1;
2149 }
2150 if i == j {
2152 return Err(Error::BamParseCigar {
2153 msg: "Expected length before cigar operation [0-9]+[MIDNSHP=X]".to_owned(),
2154 });
2155 }
2156 let s = str::from_utf8(&bytes[i..j]).map_err(|_| Error::BamParseCigar {
2158 msg: format!("Invalid utf-8 bytes '{:?}'.", &bytes[i..j]),
2159 })?;
2160 let n = s.parse().map_err(|_| Error::BamParseCigar {
2161 msg: format!("Unable to parse &str '{:?}' to u32.", s),
2162 })?;
2163 let op = &bytes[j];
2165 inner.push(match op {
2166 b'M' => Cigar::Match(n),
2167 b'I' => Cigar::Ins(n),
2168 b'D' => Cigar::Del(n),
2169 b'N' => Cigar::RefSkip(n),
2170 b'H' => {
2171 if i == 0 || j + 1 == text_len {
2172 Cigar::HardClip(n)
2173 } else {
2174 return Err(Error::BamParseCigar {
2175 msg: "Hard clipping ('H') is only valid at the start or end of a cigar."
2176 .to_owned(),
2177 });
2178 }
2179 }
2180 b'S' => {
2181 if i == 0
2182 || j + 1 == text_len
2183 || bytes[i-1] == b'H'
2184 || bytes[j+1..].iter().all(|c| c.is_ascii_digit() || *c == b'H') {
2185 Cigar::SoftClip(n)
2186 } else {
2187 return Err(Error::BamParseCigar {
2188 msg: "Soft clips ('S') can only have hard clips ('H') between them and the end of the CIGAR string."
2189 .to_owned(),
2190 });
2191 }
2192 },
2193 b'P' => Cigar::Pad(n),
2194 b'=' => Cigar::Equal(n),
2195 b'X' => Cigar::Diff(n),
2196 op => {
2197 return Err(Error::BamParseCigar {
2198 msg: format!("Expected cigar operation [MIDNSHP=X] but got [{}]", op),
2199 })
2200 }
2201 });
2202 i = j + 1;
2203 }
2204 Ok(CigarString(inner))
2205 }
2206}
2207
2208impl TryFrom<&str> for CigarString {
2209 type Error = Error;
2210
2211 fn try_from(text: &str) -> Result<Self> {
2232 let bytes = text.as_bytes();
2233 if text.chars().count() != bytes.len() {
2234 return Err(Error::BamParseCigar {
2235 msg: "CIGAR string contained non-ASCII characters, which are not valid. Valid are [0-9MIDNSHP=X].".to_owned(),
2236 });
2237 }
2238 CigarString::try_from(bytes)
2239 }
2240}
2241
2242impl<'a> CigarString {
2243 pub fn iter(&'a self) -> ::std::slice::Iter<'a, Cigar> {
2244 self.into_iter()
2245 }
2246}
2247
2248impl<'a> IntoIterator for &'a CigarString {
2249 type Item = &'a Cigar;
2250 type IntoIter = ::std::slice::Iter<'a, Cigar>;
2251
2252 fn into_iter(self) -> Self::IntoIter {
2253 self.0.iter()
2254 }
2255}
2256
2257impl fmt::Display for CigarString {
2258 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
2259 for op in self {
2260 fmt.write_fmt(format_args!("{}{}", op.len(), op.char()))?;
2261 }
2262 Ok(())
2263 }
2264}
2265
2266fn calc_softclips<'a>(mut cigar: impl DoubleEndedIterator<Item = &'a Cigar>) -> i64 {
2268 match (cigar.next(), cigar.next()) {
2269 (Some(Cigar::HardClip(_)), Some(Cigar::SoftClip(s))) | (Some(Cigar::SoftClip(s)), _) => {
2270 *s as i64
2271 }
2272 _ => 0,
2273 }
2274}
2275
2276#[derive(Eq, PartialEq, Clone, Debug)]
2277pub struct CigarStringView {
2278 inner: CigarString,
2279 pos: i64,
2280}
2281
2282impl CigarStringView {
2283 pub fn new(c: CigarString, pos: i64) -> CigarStringView {
2285 CigarStringView { inner: c, pos }
2286 }
2287 pub fn containsequal(&self) -> bool {
2289 self.inner.containsequal()
2290 }
2291 pub fn end_pos(&self) -> i64 {
2293 let mut pos = self.pos;
2294 for c in self {
2295 match c {
2296 Cigar::Match(l)
2297 | Cigar::RefSkip(l)
2298 | Cigar::Del(l)
2299 | Cigar::Equal(l)
2300 | Cigar::Diff(l) => pos += *l as i64,
2301 Cigar::Ins(_) | Cigar::SoftClip(_) | Cigar::HardClip(_) | Cigar::Pad(_) => (),
2303 }
2304 }
2305 pos
2306 }
2307
2308 pub fn pos(&self) -> i64 {
2310 self.pos
2311 }
2312
2313 pub fn leading_softclips(&self) -> i64 {
2315 calc_softclips(self.iter())
2316 }
2317
2318 pub fn trailing_softclips(&self) -> i64 {
2320 calc_softclips(self.iter().rev())
2321 }
2322
2323 pub fn leading_hardclips(&self) -> i64 {
2325 self.first().map_or(0, |cigar| {
2326 if let Cigar::HardClip(s) = cigar {
2327 *s as i64
2328 } else {
2329 0
2330 }
2331 })
2332 }
2333
2334 pub fn trailing_hardclips(&self) -> i64 {
2336 self.last().map_or(0, |cigar| {
2337 if let Cigar::HardClip(s) = cigar {
2338 *s as i64
2339 } else {
2340 0
2341 }
2342 })
2343 }
2344
2345 pub fn read_pos(
2355 &self,
2356 ref_pos: u32,
2357 include_softclips: bool,
2358 include_dels: bool,
2359 ) -> Result<Option<u32>> {
2360 let mut rpos = self.pos as u32; let mut qpos = 0u32; let mut j = 0; for (i, c) in self.iter().enumerate() {
2367 match c {
2368 Cigar::Match(_) |
2369 Cigar::Diff(_) |
2370 Cigar::Equal(_) |
2371 Cigar::Ins(_) => {
2374 j = i;
2375 break;
2376 },
2377 Cigar::SoftClip(l) => {
2378 j = i;
2379 if include_softclips {
2380 rpos = rpos.saturating_sub(*l);
2384 }
2385 break;
2386 },
2387 Cigar::Del(l) => {
2388 rpos += l;
2396 },
2397 Cigar::RefSkip(_) => {
2398 return Err(Error::BamUnexpectedCigarOperation {
2399 msg: "'reference skip' (N) found before any operation describing read sequence".to_owned()
2400 });
2401 },
2402 Cigar::HardClip(_) if i > 0 && i < self.len()-1 => {
2403 return Err(Error::BamUnexpectedCigarOperation{
2404 msg: "'hard clip' (H) found in between operations, contradicting SAMv1 spec that hard clips can only be at the ends of reads".to_owned()
2405 });
2406 },
2407 Cigar::Pad(_) | Cigar::HardClip(_) if i == self.len()-1 => return Ok(None),
2409 Cigar::Pad(_) | Cigar::HardClip(_) => ()
2411 }
2412 }
2413
2414 let contains_ref_pos = |cigar_op_start: u32, cigar_op_length: u32| {
2415 cigar_op_start <= ref_pos && cigar_op_start + cigar_op_length > ref_pos
2416 };
2417
2418 while rpos <= ref_pos && j < self.len() {
2419 match self[j] {
2420 Cigar::Match(l) | Cigar::Diff(l) | Cigar::Equal(l) if contains_ref_pos(rpos, l) => {
2422 qpos += ref_pos - rpos;
2425 return Ok(Some(qpos));
2426 }
2427 Cigar::SoftClip(l) if include_softclips && contains_ref_pos(rpos, l) => {
2428 qpos += ref_pos - rpos;
2429 return Ok(Some(qpos));
2430 }
2431 Cigar::Del(l) if include_dels && contains_ref_pos(rpos, l) => {
2432 return Ok(Some(qpos));
2434 }
2435 Cigar::Match(l) | Cigar::Diff(l) | Cigar::Equal(l) => {
2437 rpos += l;
2438 qpos += l;
2439 j += 1;
2440 }
2441 Cigar::SoftClip(l) => {
2442 qpos += l;
2443 j += 1;
2444 if include_softclips {
2445 rpos += l;
2446 }
2447 }
2448 Cigar::Ins(l) => {
2449 qpos += l;
2450 j += 1;
2451 }
2452 Cigar::RefSkip(l) | Cigar::Del(l) => {
2453 rpos += l;
2454 j += 1;
2455 }
2456 Cigar::Pad(_) => {
2457 j += 1;
2458 }
2459 Cigar::HardClip(_) if j < self.len() - 1 => {
2460 return Err(Error::BamUnexpectedCigarOperation{
2461 msg: "'hard clip' (H) found in between operations, contradicting SAMv1 spec that hard clips can only be at the ends of reads".to_owned()
2462 });
2463 }
2464 Cigar::HardClip(_) => return Ok(None),
2465 }
2466 }
2467
2468 Ok(None)
2469 }
2470
2471 pub fn take(self) -> CigarString {
2473 self.inner
2474 }
2475}
2476
2477impl ops::Deref for CigarStringView {
2478 type Target = CigarString;
2479
2480 fn deref(&self) -> &CigarString {
2481 &self.inner
2482 }
2483}
2484
2485impl ops::Index<usize> for CigarStringView {
2486 type Output = Cigar;
2487
2488 fn index(&self, index: usize) -> &Cigar {
2489 self.inner.index(index)
2490 }
2491}
2492
2493impl ops::IndexMut<usize> for CigarStringView {
2494 fn index_mut(&mut self, index: usize) -> &mut Cigar {
2495 self.inner.index_mut(index)
2496 }
2497}
2498
2499impl<'a> CigarStringView {
2500 pub fn iter(&'a self) -> ::std::slice::Iter<'a, Cigar> {
2501 self.inner.into_iter()
2502 }
2503}
2504
2505impl<'a> IntoIterator for &'a CigarStringView {
2506 type Item = &'a Cigar;
2507 type IntoIter = ::std::slice::Iter<'a, Cigar>;
2508
2509 fn into_iter(self) -> Self::IntoIter {
2510 self.inner.into_iter()
2511 }
2512}
2513
2514impl fmt::Display for CigarStringView {
2515 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
2516 self.inner.fmt(fmt)
2517 }
2518}
2519#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2521pub enum CsSame {
2522 Full(String),
2524 Small(usize),
2526}
2527impl CsSame {
2528 pub fn getsize(&self) -> usize {
2530 match self {
2531 CsSame::Full(a) => a.len(),
2532 CsSame::Small(b) => *b,
2533 }
2534 }
2535 pub fn islongform(&self) -> bool {
2537 match self {
2538 CsSame::Full(_) => true,
2539 CsSame::Small(_) => false,
2540 }
2541 }
2542}
2543#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2545pub enum CsValue {
2546 Same(CsSame),
2548 Substitution((char, char)),
2550 Insertion(String),
2552 Deletion(String),
2554 Intron((String, usize, String)),
2556}
2557impl std::fmt::Display for CsValue {
2558 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2559 match self {
2560 CsValue::Same(a) => write!(f, ":{}", a.getsize()),
2561 CsValue::Substitution((a, b)) => write!(f, "*{}{}", a, b),
2562 CsValue::Insertion(a) => write!(f, "+{}", a),
2563 CsValue::Deletion(a) => write!(f, "-{}", a),
2564 CsValue::Intron((a, b, c)) => write!(f, "~{}{}{}", a, b, c),
2565 }
2566 }
2567}
2568impl FromStr for CsValue {
2569 type Err = crate::bam::Error;
2570 fn from_str(s: &str) -> Result<Self, Self::Err> {
2571 let s = if s.trim().len() <= 1 || s.chars().any(|p| !p.is_ascii()) {
2572 return Err(crate::errors::Error::BamParseCS {
2573 rec: "Bad CS parsing".to_string(),
2574 });
2575 } else {
2576 s.trim()
2577 };
2578 match s.split_at_checked(1) {
2579 Some((a, b)) if let Ok(d) = char::from_str(a) => CsValue::new(d, b),
2580 _ => {
2581 return Err(crate::errors::Error::BamParseCS {
2582 rec: format!("Bad CS parsing for {s}"),
2583 });
2584 }
2585 }
2586 }
2587}
2588impl CsValue {
2589 pub fn issame(&self) -> bool {
2590 matches!(self, CsValue::Same(_))
2591 }
2592 pub fn isubstitution(&self) -> bool {
2593 matches!(self, CsValue::Substitution(_))
2594 }
2595 pub fn getseq(&self) -> Option<String> {
2598 match self {
2599 CsValue::Same(CsSame::Full(a)) => Some(a.to_string()),
2600 CsValue::Same(CsSame::Small(_)) => None,
2601 CsValue::Substitution((a, b)) => Some(format!("{}{}", a, b)),
2602 CsValue::Insertion(a) => Some(a.to_string()),
2603 CsValue::Deletion(a) => Some(a.to_string()),
2604 CsValue::Intron(_) => None,
2605 }
2606 }
2607 pub fn isinsertion(&self) -> bool {
2608 matches!(self, CsValue::Insertion(_))
2609 }
2610 pub fn isdeletion(&self) -> bool {
2611 matches!(self, CsValue::Deletion(_))
2612 }
2613 pub fn getlength(&self) -> usize {
2614 match self {
2615 CsValue::Intron(d) => d.1 + 4,
2616 CsValue::Same(a) => a.getsize(),
2617 CsValue::Deletion(d) | CsValue::Insertion(d) => d.len(),
2618 CsValue::Substitution(_) => 1,
2619 }
2620 }
2621 pub fn isintron(&self) -> bool {
2622 matches!(self, CsValue::Intron(_))
2623 }
2624}
2625impl CsValue {
2626 pub fn new<T>(value: char, valtocheck: T) -> Result<CsValue, crate::bam::Error>
2628 where
2629 T: AsRef<str>,
2630 {
2631 let valtocheck = valtocheck.as_ref().to_ascii_lowercase().trim().to_string();
2632 match value {
2633 '=' | ':' => {
2634 match valtocheck.parse::<usize>() {
2635 Ok(d) => Ok(CsValue::Same(CsSame::Small(d))),
2636 Err(_) => {
2637 Ok(CsValue::Same(CsSame::Full(String::from(valtocheck))))
2639 }
2640 }
2641 }
2642 id @ ('-' | '+' | '*') => {
2643 let seq = String::from(valtocheck);
2644 let state = match id {
2645 '-' => CsValue::Deletion(seq),
2646 '+' => CsValue::Insertion(seq),
2647 '*' => {
2648 match seq
2649 .split_at_checked(1)
2650 .map(|(a, b)| (char::from_str(a), char::from_str(b)))
2651 {
2652 Some((Ok(a), Ok(b))) if a != b => CsValue::Substitution((a, b)),
2653 _ => {
2654 return Err(crate::errors::Error::BamParseCS {
2655 rec: "CS tag invalid".to_string(),
2656 });
2657 }
2658 }
2659 }
2660 _ => todo!(),
2661 };
2662 Ok(state)
2663 }
2664 '~' => {
2665 if valtocheck.len() <= 5 || valtocheck.chars().any(|p| !p.is_ascii()) {
2666 return Err(crate::errors::Error::BamParseCS {
2667 rec: "CS tag invalid".to_string(),
2668 });
2669 }
2670 let seq = valtocheck.chars().take(2).collect::<String>();
2671 let seq2 = valtocheck
2672 .chars()
2673 .skip(valtocheck.len() - 2)
2674 .collect::<String>();
2675 let full = valtocheck
2676 .chars()
2677 .skip(2)
2678 .take(valtocheck.len().saturating_sub(4))
2679 .collect::<String>();
2680 let ilength = match full.parse::<usize>() {
2681 Ok(d) => d,
2682 Err(_) => {
2683 return Err(crate::errors::Error::BamParseCS {
2684 rec: "CS tag invalid".to_string(),
2685 });
2686 }
2687 };
2688 Ok(CsValue::Intron((
2689 seq.to_string(),
2690 ilength,
2691 seq2.to_string(),
2692 )))
2693 }
2694 _ => Err(crate::bam::Error::BamAuxParsingError),
2695 }
2696 }
2697}
2698custom_derive! {
2699 #[cfg_attr(feature = "serde_feature", derive(Serialize, Deserialize))]
2718 #[derive(NewtypeDeref,
2719 NewtypeDerefMut,
2720 NewtypeIndex(usize),
2721 NewtypeIndexMut(usize),
2722 NewtypeFrom,
2723 PartialEq,
2724 Eq,
2725 NewtypeDebug,
2726 Clone,
2727 Hash
2728 )]
2729 pub struct CsString(pub Vec<CsValue>);
2730}
2731
2732impl<'a> IntoIterator for &'a CsString {
2756 type Item = &'a CsValue;
2757 type IntoIter = ::std::slice::Iter<'a, CsValue>;
2758 fn into_iter(self) -> Self::IntoIter {
2759 self.0.iter()
2760 }
2761}
2762impl FromStr for CsString {
2763 type Err = crate::bam::Error;
2764
2765 fn from_str(s: &str) -> Result<Self, Self::Err> {
2766 let isnotalphanum = |p: char| !p.is_alphanumeric();
2767 if !s.starts_with(isnotalphanum) {
2768 return Err(crate::errors::Error::BamParseCS {
2769 rec: "CS tag starts with an alphanumeric char".to_string(),
2770 })?;
2771 }
2772 let s_reversed = s.chars().rev().collect::<String>();
2773 let blocks = s_reversed
2774 .split_inclusive(isnotalphanum)
2775 .map(|block| block.chars().rev().collect::<String>())
2777 .rev();
2779 let mut list = Vec::new();
2780 for block in blocks {
2781 let v = CsValue::from_str(&block)
2782 .map_err(|e| crate::errors::Error::BamParseCS { rec: e.to_string() })?;
2783 list.push(v)
2784 }
2785 Ok(CsString(list))
2786 }
2787}
2788impl Display for CsString {
2789 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2790 for elem in self.0.iter() {
2791 write!(f, "{}", elem)?;
2792 }
2793 Ok(())
2794 }
2795}
2796impl CsString {
2797 pub fn islongcs(&self) -> bool {
2798 self.iter().filter(|p| p.issame()).any(|a| match a {
2799 CsValue::Same(CsSame::Full(_)) => true,
2800 CsValue::Same(CsSame::Small(_)) => false,
2801 _ => unreachable!("Filter by issame"),
2802 })
2803 }
2804}
2805pub struct BaseModificationMetadata {
2806 pub strand: i32,
2807 pub implicit: i32,
2808 pub canonical: u8,
2809}
2810
2811pub struct BaseModificationState<'a> {
2814 record: &'a Record,
2815 state: *mut htslib::hts_base_mod_state,
2816 buffer: Vec<htslib::hts_base_mod>,
2817 buffer_pos: i32,
2818}
2819
2820impl BaseModificationState<'_> {
2821 fn new(r: &Record) -> Result<BaseModificationState<'_>> {
2826 let mut bm = unsafe {
2827 BaseModificationState {
2828 record: r,
2829 state: hts_sys::hts_base_mod_state_alloc(),
2830 buffer: Vec::new(),
2831 buffer_pos: -1,
2832 }
2833 };
2834
2835 if bm.state.is_null() {
2836 panic!("Unable to allocate memory for hts_base_mod_state");
2837 }
2838
2839 unsafe {
2841 let ret = hts_sys::bam_parse_basemod(bm.record.inner_ptr(), bm.state);
2842 if ret != 0 {
2843 return Err(Error::BamBaseModificationTagNotFound);
2844 }
2845 }
2846
2847 let types = bm.recorded();
2848 bm.buffer.reserve(types.len());
2849 Ok(bm)
2850 }
2851
2852 pub fn buffer_next_mods(&mut self) -> Result<usize> {
2853 unsafe {
2854 let ret = hts_sys::bam_next_basemod(
2855 self.record.inner_ptr(),
2856 self.state,
2857 self.buffer.as_mut_ptr(),
2858 self.buffer.capacity() as i32,
2859 &mut self.buffer_pos,
2860 );
2861
2862 if ret < 0 {
2863 return Err(Error::BamBaseModificationIterationFailed);
2864 }
2865
2866 if ret as usize > self.buffer.capacity() {
2870 return Err(Error::BamBaseModificationTooManyMods);
2871 }
2872
2873 self.buffer.set_len(ret as usize);
2876
2877 Ok(ret as usize)
2878 }
2879 }
2880
2881 pub fn recorded<'a>(&self) -> &'a [i32] {
2884 unsafe {
2885 let mut n: i32 = 0;
2886 let data_ptr: *const i32 = hts_sys::bam_mods_recorded(self.state, &mut n);
2887
2888 if data_ptr.is_null() {
2890 panic!("Unable to obtain pointer to base modifications");
2891 }
2892 assert!(n >= 0);
2893 slice::from_raw_parts(data_ptr, n as usize)
2894 }
2895 }
2896
2897 pub fn query_type(&self, code: i32) -> Result<BaseModificationMetadata> {
2903 unsafe {
2904 let mut strand: i32 = 0;
2905 let mut implicit: i32 = 0;
2906 let mut canonical: c_char = 0;
2908
2909 let ret = hts_sys::bam_mods_query_type(
2910 self.state,
2911 code,
2912 &mut strand,
2913 &mut implicit,
2914 &mut canonical,
2915 );
2916 if ret == -1 {
2917 Err(Error::BamBaseModificationTypeNotFound)
2918 } else {
2919 Ok(BaseModificationMetadata {
2920 strand,
2921 implicit,
2922 canonical: canonical
2923 .try_into()
2924 .map_err(|_| Error::BamBaseModificationIterationFailed)?,
2925 })
2926 }
2927 }
2928 }
2929}
2930
2931impl Drop for BaseModificationState<'_> {
2932 fn drop<'a>(&mut self) {
2933 unsafe {
2934 hts_sys::hts_base_mod_state_free(self.state);
2935 }
2936 }
2937}
2938
2939pub struct BaseModificationsPositionIter<'a> {
2942 mod_state: BaseModificationState<'a>,
2943}
2944
2945impl BaseModificationsPositionIter<'_> {
2946 fn new(r: &Record) -> Result<BaseModificationsPositionIter<'_>> {
2947 let state = BaseModificationState::new(r)?;
2948 Ok(BaseModificationsPositionIter { mod_state: state })
2949 }
2950
2951 pub fn recorded<'a>(&self) -> &'a [i32] {
2952 self.mod_state.recorded()
2953 }
2954
2955 pub fn query_type(&self, code: i32) -> Result<BaseModificationMetadata> {
2956 self.mod_state.query_type(code)
2957 }
2958}
2959
2960impl Iterator for BaseModificationsPositionIter<'_> {
2961 type Item = Result<(i32, Vec<hts_sys::hts_base_mod>)>;
2962
2963 fn next(&mut self) -> Option<Self::Item> {
2964 let ret = self.mod_state.buffer_next_mods();
2965
2966 match ret {
2971 Ok(num_mods) => {
2972 if num_mods == 0 {
2973 None
2974 } else {
2975 let data = (self.mod_state.buffer_pos, self.mod_state.buffer.clone());
2976 Some(Ok(data))
2977 }
2978 }
2979 Err(e) => Some(Err(e)),
2980 }
2981 }
2982}
2983
2984pub struct BaseModificationsIter<'a> {
2987 mod_state: BaseModificationState<'a>,
2988 buffer_idx: usize,
2989}
2990
2991impl BaseModificationsIter<'_> {
2992 fn new(r: &Record) -> Result<BaseModificationsIter<'_>> {
2993 let state = BaseModificationState::new(r)?;
2994 Ok(BaseModificationsIter {
2995 mod_state: state,
2996 buffer_idx: 0,
2997 })
2998 }
2999
3000 pub fn recorded<'a>(&self) -> &'a [i32] {
3001 self.mod_state.recorded()
3002 }
3003
3004 pub fn query_type(&self, code: i32) -> Result<BaseModificationMetadata> {
3005 self.mod_state.query_type(code)
3006 }
3007}
3008
3009impl Iterator for BaseModificationsIter<'_> {
3010 type Item = Result<(i32, hts_sys::hts_base_mod)>;
3011
3012 fn next(&mut self) -> Option<Self::Item> {
3013 if self.buffer_idx == self.mod_state.buffer.len() {
3014 let ret = self.mod_state.buffer_next_mods();
3017
3018 match ret {
3019 Ok(num_mods) => {
3020 if num_mods == 0 {
3021 return None;
3023 } else {
3024 self.buffer_idx = 0;
3026 }
3027 }
3028 Err(e) => return Some(Err(e)),
3029 }
3030 }
3031
3032 assert!(self.buffer_idx < self.mod_state.buffer.len());
3034 let data = (
3035 self.mod_state.buffer_pos,
3036 self.mod_state.buffer[self.buffer_idx],
3037 );
3038 self.buffer_idx += 1;
3039 Some(Ok(data))
3040 }
3041}
3042
3043#[cfg(test)]
3044mod tests {
3045 use super::*;
3046
3047 #[test]
3048 fn test_cigar_string() {
3049 let cigar = CigarString(vec![Cigar::Match(100), Cigar::SoftClip(10)]);
3050
3051 assert_eq!(cigar[0], Cigar::Match(100));
3052 assert_eq!(format!("{}", cigar), "100M10S");
3053 for op in &cigar {
3054 println!("{}", op);
3055 }
3056 }
3057
3058 #[test]
3059 fn test_cigar_string_view_pos() {
3060 let cigar = CigarString(vec![Cigar::Match(100), Cigar::SoftClip(10)]).into_view(5);
3061 assert_eq!(cigar.pos(), 5);
3062 }
3063
3064 #[test]
3065 fn test_cigar_string_leading_softclips() {
3066 let cigar = CigarString(vec![Cigar::SoftClip(10), Cigar::Match(100)]).into_view(0);
3067 assert_eq!(cigar.leading_softclips(), 10);
3068 let cigar2 = CigarString(vec![
3069 Cigar::HardClip(5),
3070 Cigar::SoftClip(10),
3071 Cigar::Match(100),
3072 ])
3073 .into_view(0);
3074 assert_eq!(cigar2.leading_softclips(), 10);
3075 }
3076
3077 #[test]
3078 fn test_cigar_string_trailing_softclips() {
3079 let cigar = CigarString(vec![Cigar::Match(100), Cigar::SoftClip(10)]).into_view(0);
3080 assert_eq!(cigar.trailing_softclips(), 10);
3081 let cigar2 = CigarString(vec![
3082 Cigar::Match(100),
3083 Cigar::SoftClip(10),
3084 Cigar::HardClip(5),
3085 ])
3086 .into_view(0);
3087 assert_eq!(cigar2.trailing_softclips(), 10);
3088 }
3089
3090 #[test]
3091 fn test_cigar_read_pos() {
3092 let vpos = 5; let c01 = CigarString(vec![Cigar::HardClip(7), Cigar::Match(2)]).into_view(4);
3100 assert_eq!(c01.read_pos(vpos, false, false).unwrap(), Some(1));
3101
3102 let c02 = CigarString(vec![Cigar::SoftClip(2), Cigar::Match(6)]).into_view(2);
3110 assert_eq!(c02.read_pos(vpos, false, false).unwrap(), Some(5));
3111 assert_eq!(c02.read_pos(vpos, true, false).unwrap(), Some(5));
3112
3113 let c03 = CigarString(vec![Cigar::SoftClip(3), Cigar::Match(6)]).into_view(6);
3122 assert_eq!(c03.read_pos(vpos, false, false).unwrap(), None);
3123 assert_eq!(c03.read_pos(vpos, true, false).unwrap(), Some(2));
3124
3125 let c04 = CigarString(vec![Cigar::Ins(3), Cigar::Diff(3)]).into_view(4);
3131 assert_eq!(c04.read_pos(vpos, true, false).unwrap(), Some(4));
3132
3133 let c05 = CigarString(vec![
3139 Cigar::Equal(2),
3140 Cigar::Del(2),
3141 Cigar::Diff(1),
3142 Cigar::Equal(2),
3143 ])
3144 .into_view(0);
3145 assert_eq!(c05.read_pos(vpos, true, false).unwrap(), Some(3));
3146
3147 let c06 = CigarString(vec![Cigar::Equal(2), Cigar::Del(1), Cigar::Diff(2)]).into_view(3);
3153 assert_eq!(c06.read_pos(vpos, false, true).unwrap(), Some(2));
3154 assert_eq!(c06.read_pos(vpos, false, false).unwrap(), None);
3155
3156 let c07 = CigarString(vec![Cigar::Equal(2), Cigar::Del(3), Cigar::Match(2)]).into_view(2);
3162 assert_eq!(c07.read_pos(vpos, false, true).unwrap(), Some(2));
3163 assert_eq!(c07.read_pos(vpos, false, false).unwrap(), None);
3164
3165 let c08 = CigarString(vec![
3171 Cigar::Equal(1),
3172 Cigar::Diff(1),
3173 Cigar::RefSkip(3),
3174 Cigar::Match(2),
3175 ])
3176 .into_view(2);
3177 assert_eq!(c08.read_pos(vpos, false, true).unwrap(), None);
3178 assert_eq!(c08.read_pos(vpos, false, false).unwrap(), None);
3179
3180 let c09 = CigarString(vec![
3186 Cigar::HardClip(3),
3187 Cigar::Equal(2),
3188 Cigar::HardClip(3),
3189 Cigar::Equal(2),
3190 ])
3191 .into_view(2);
3192 assert_eq!(c09.read_pos(vpos, false, true).is_err(), true);
3193
3194 let c10 = CigarString(vec![Cigar::Match(2), Cigar::Del(2), Cigar::Match(2)]).into_view(1);
3200 assert_eq!(c10.read_pos(vpos, false, false).unwrap(), Some(2));
3201
3202 let c11 = CigarString(vec![Cigar::Match(2), Cigar::Ins(3), Cigar::Match(2)]).into_view(3);
3208 assert_eq!(c11.read_pos(vpos, false, false).unwrap(), Some(5));
3209
3210 let c12 = CigarString(vec![Cigar::Match(3), Cigar::Ins(2), Cigar::Equal(1)]).into_view(3);
3216 assert_eq!(c12.read_pos(vpos, false, false).unwrap(), Some(2));
3217
3218 let c13 = CigarString(vec![Cigar::Match(3), Cigar::Del(1), Cigar::Equal(1)]).into_view(3);
3224 assert_eq!(c13.read_pos(vpos, false, false).unwrap(), Some(2));
3225
3226 let vpos2 = 15;
3228 let c14 = CigarString(vec![
3233 Cigar::HardClip(5),
3234 Cigar::SoftClip(3),
3235 Cigar::Equal(1),
3236 Cigar::Pad(2),
3237 Cigar::Match(1),
3238 Cigar::Diff(1),
3239 Cigar::Ins(3),
3240 Cigar::Match(2),
3241 Cigar::Del(1),
3242 Cigar::Ins(2),
3243 Cigar::Equal(2),
3244 Cigar::RefSkip(3),
3245 Cigar::Match(3),
3246 Cigar::Equal(2),
3247 Cigar::SoftClip(5),
3248 Cigar::HardClip(2),
3249 ])
3250 .into_view(0);
3251 assert_eq!(c14.read_pos(vpos2, false, false).unwrap(), Some(19));
3252
3253 let c15 =
3259 CigarString(vec![Cigar::Pad(5), Cigar::HardClip(1), Cigar::Equal(3)]).into_view(3);
3260 assert_eq!(c15.read_pos(vpos, false, false).is_err(), true);
3261
3262 let c16 =
3265 CigarString(vec![Cigar::HardClip(7), Cigar::Pad(5), Cigar::HardClip(2)]).into_view(3);
3266 assert_eq!(c16.read_pos(vpos, false, false).unwrap(), None);
3267 }
3268
3269 #[test]
3270 fn test_clone() {
3271 let mut rec = Record::new();
3272 rec.set_pos(300);
3273 rec.set_qname(b"read1");
3274 let clone = rec.clone();
3275 assert_eq!(rec, clone);
3276 }
3277
3278 #[test]
3279 fn test_flags() {
3280 let mut rec = Record::new();
3281
3282 rec.set_paired();
3283 assert_eq!(rec.is_paired(), true);
3284
3285 rec.set_supplementary();
3286 assert_eq!(rec.is_supplementary(), true);
3287 assert_eq!(rec.is_supplementary(), true);
3288
3289 rec.unset_paired();
3290 assert_eq!(rec.is_paired(), false);
3291 assert_eq!(rec.is_supplementary(), true);
3292
3293 rec.unset_supplementary();
3294 assert_eq!(rec.is_paired(), false);
3295 assert_eq!(rec.is_supplementary(), false);
3296 }
3297
3298 #[test]
3299 fn test_cigar_parse() {
3300 let cigar = "1S20M1D2I3X1=2H";
3301 let parsed = CigarString::try_from(cigar).unwrap();
3302 assert_eq!(parsed.to_string(), cigar);
3303 }
3304}
3305
3306#[cfg(test)]
3307mod alignment_cigar_tests {
3308 use super::*;
3309 use crate::bam::{Read, Reader};
3310 use bio_types::alignment::AlignmentOperation::{Del, Ins, Match, Subst, Xclip, Yclip};
3311 use bio_types::alignment::{Alignment, AlignmentMode};
3312
3313 #[test]
3314 fn test_cigar() {
3315 let alignment = Alignment {
3316 score: 5,
3317 xstart: 3,
3318 ystart: 0,
3319 xend: 9,
3320 yend: 10,
3321 ylen: 10,
3322 xlen: 10,
3323 operations: vec![Match, Match, Match, Subst, Ins, Ins, Del, Del],
3324 mode: AlignmentMode::Semiglobal,
3325 };
3326 assert_eq!(alignment.cigar(false), "3S3=1X2I2D1S");
3327 assert_eq!(
3328 CigarString::from_alignment(&alignment, false).0,
3329 vec![
3330 Cigar::SoftClip(3),
3331 Cigar::Equal(3),
3332 Cigar::Diff(1),
3333 Cigar::Ins(2),
3334 Cigar::Del(2),
3335 Cigar::SoftClip(1),
3336 ]
3337 );
3338
3339 let alignment = Alignment {
3340 score: 5,
3341 xstart: 0,
3342 ystart: 5,
3343 xend: 4,
3344 yend: 10,
3345 ylen: 10,
3346 xlen: 5,
3347 operations: vec![Yclip(5), Match, Subst, Subst, Ins, Del, Del, Xclip(1)],
3348 mode: AlignmentMode::Custom,
3349 };
3350 assert_eq!(alignment.cigar(false), "1=2X1I2D1S");
3351 assert_eq!(alignment.cigar(true), "1=2X1I2D1H");
3352 assert_eq!(
3353 CigarString::from_alignment(&alignment, false).0,
3354 vec![
3355 Cigar::Equal(1),
3356 Cigar::Diff(2),
3357 Cigar::Ins(1),
3358 Cigar::Del(2),
3359 Cigar::SoftClip(1),
3360 ]
3361 );
3362 assert_eq!(
3363 CigarString::from_alignment(&alignment, true).0,
3364 vec![
3365 Cigar::Equal(1),
3366 Cigar::Diff(2),
3367 Cigar::Ins(1),
3368 Cigar::Del(2),
3369 Cigar::HardClip(1),
3370 ]
3371 );
3372
3373 let alignment = Alignment {
3374 score: 5,
3375 xstart: 0,
3376 ystart: 5,
3377 xend: 3,
3378 yend: 8,
3379 ylen: 10,
3380 xlen: 3,
3381 operations: vec![Yclip(5), Subst, Match, Subst, Yclip(2)],
3382 mode: AlignmentMode::Custom,
3383 };
3384 assert_eq!(alignment.cigar(false), "1X1=1X");
3385 assert_eq!(
3386 CigarString::from_alignment(&alignment, false).0,
3387 vec![Cigar::Diff(1), Cigar::Equal(1), Cigar::Diff(1)]
3388 );
3389
3390 let alignment = Alignment {
3391 score: 5,
3392 xstart: 0,
3393 ystart: 5,
3394 xend: 3,
3395 yend: 8,
3396 ylen: 10,
3397 xlen: 3,
3398 operations: vec![Subst, Match, Subst],
3399 mode: AlignmentMode::Semiglobal,
3400 };
3401 assert_eq!(alignment.cigar(false), "1X1=1X");
3402 assert_eq!(
3403 CigarString::from_alignment(&alignment, false).0,
3404 vec![Cigar::Diff(1), Cigar::Equal(1), Cigar::Diff(1)]
3405 );
3406 }
3407
3408 #[test]
3409 fn test_read_orientation_f1r2() {
3410 let mut bam = Reader::from_path("test/test_paired.sam").unwrap();
3411
3412 for res in bam.records() {
3413 let record = res.unwrap();
3414 assert_eq!(
3415 record.read_pair_orientation(),
3416 SequenceReadPairOrientation::F1R2
3417 );
3418 }
3419 }
3420
3421 #[test]
3422 fn test_read_orientation_f2r1() {
3423 let mut bam = Reader::from_path("test/test_nonstandard_orientation.sam").unwrap();
3424
3425 for res in bam.records() {
3426 let record = res.unwrap();
3427 assert_eq!(
3428 record.read_pair_orientation(),
3429 SequenceReadPairOrientation::F2R1
3430 );
3431 }
3432 }
3433
3434 #[test]
3435 fn test_read_orientation_supplementary() {
3436 let mut bam = Reader::from_path("test/test_orientation_supplementary.sam").unwrap();
3437
3438 for res in bam.records() {
3439 let record = res.unwrap();
3440 assert_eq!(
3441 record.read_pair_orientation(),
3442 SequenceReadPairOrientation::F2R1
3443 );
3444 }
3445 }
3446
3447 #[test]
3448 pub fn test_cigar_parsing_non_ascii_error() {
3449 let cigar_str = "43ጷ";
3450 let expected_error = Err(Error::BamParseCigar {
3451 msg: "CIGAR string contained non-ASCII characters, which are not valid. Valid are [0-9MIDNSHP=X].".to_owned(),
3452 });
3453
3454 let result = CigarString::try_from(cigar_str);
3455 assert_eq!(expected_error, result);
3456 }
3457
3458 #[test]
3459 pub fn test_cigar_parsing() {
3460 let cigar_strs = [
3462 "1H10M4D100I300N1102=10P25X11S", "100M", "", "1H1=1H", "1S1=1S", "11H11S11=11S11H", "10H",
3469 "10S",
3470 ];
3471 let cigars = [
3473 CigarString(vec![
3474 Cigar::HardClip(1),
3475 Cigar::Match(10),
3476 Cigar::Del(4),
3477 Cigar::Ins(100),
3478 Cigar::RefSkip(300),
3479 Cigar::Equal(1102),
3480 Cigar::Pad(10),
3481 Cigar::Diff(25),
3482 Cigar::SoftClip(11),
3483 ]),
3484 CigarString(vec![Cigar::Match(100)]),
3485 CigarString(vec![]),
3486 CigarString(vec![
3487 Cigar::HardClip(1),
3488 Cigar::Equal(1),
3489 Cigar::HardClip(1),
3490 ]),
3491 CigarString(vec![
3492 Cigar::SoftClip(1),
3493 Cigar::Equal(1),
3494 Cigar::SoftClip(1),
3495 ]),
3496 CigarString(vec![
3497 Cigar::HardClip(11),
3498 Cigar::SoftClip(11),
3499 Cigar::Equal(11),
3500 Cigar::SoftClip(11),
3501 Cigar::HardClip(11),
3502 ]),
3503 CigarString(vec![Cigar::HardClip(10)]),
3504 CigarString(vec![Cigar::SoftClip(10)]),
3505 ];
3506 for (&cigar_str, truth) in cigar_strs.iter().zip(cigars.iter()) {
3508 let cigar_parse = CigarString::try_from(cigar_str)
3509 .unwrap_or_else(|_| panic!("Unable to parse cigar: {}", cigar_str));
3510 assert_eq!(&cigar_parse, truth);
3511 }
3512 }
3513}
3514
3515#[cfg(test)]
3516mod basemod_tests {
3517 use crate::bam::{Read, Reader};
3518
3519 #[test]
3520 pub fn test_count_recorded() {
3521 let mut bam = Reader::from_path("test/base_mods/MM-double.sam").unwrap();
3522
3523 for r in bam.records() {
3524 let record = r.unwrap();
3525 if let Ok(mods) = record.basemods_iter() {
3526 let n = mods.recorded().len();
3527 assert_eq!(n, 3);
3528 };
3529 }
3530 }
3531
3532 #[test]
3533 pub fn test_query_type() {
3534 let mut bam = Reader::from_path("test/base_mods/MM-orient.sam").unwrap();
3535
3536 let mut n_fwd = 0;
3537 let mut n_rev = 0;
3538
3539 for r in bam.records() {
3540 let record = r.unwrap();
3541 if let Ok(mods) = record.basemods_iter() {
3542 for mod_code in mods.recorded() {
3543 if let Ok(mod_metadata) = mods.query_type(*mod_code) {
3544 if mod_metadata.strand == 0 {
3545 n_fwd += 1;
3546 }
3547 if mod_metadata.strand == 1 {
3548 n_rev += 1;
3549 }
3550 }
3551 }
3552 };
3553 }
3554 assert_eq!(n_fwd, 2);
3555 assert_eq!(n_rev, 2);
3556 }
3557
3558 #[test]
3559 pub fn test_mod_iter() {
3560 let mut bam = Reader::from_path("test/base_mods/MM-double.sam").unwrap();
3561 let expected_positions = [1, 7, 12, 13, 13, 22, 30, 31];
3562 let mut i = 0;
3563
3564 for r in bam.records() {
3565 let record = r.unwrap();
3566 for res in record.basemods_iter().unwrap().flatten() {
3567 let (position, _m) = res;
3568 assert_eq!(position, expected_positions[i]);
3569 i += 1;
3570 }
3571 }
3572 }
3573
3574 #[test]
3575 pub fn test_position_iter() {
3576 let mut bam = Reader::from_path("test/base_mods/MM-double.sam").unwrap();
3577 let expected_positions = [1, 7, 12, 13, 22, 30, 31];
3578 let expected_counts = [1, 1, 1, 2, 1, 1, 1];
3579 let mut i = 0;
3580
3581 for r in bam.records() {
3582 let record = r.unwrap();
3583 for res in record.basemods_position_iter().unwrap().flatten() {
3584 let (position, elements) = res;
3585 assert_eq!(position, expected_positions[i]);
3586 assert_eq!(elements.len(), expected_counts[i]);
3587 i += 1;
3588 }
3589 }
3590 }
3591}