• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rust-bio / rust-htslib / 14738329579

29 Apr 2025 06:12PM UTC coverage: 83.282% (-0.3%) from 83.605%
14738329579

Pull #473

github

web-flow
Merge 61ffae407 into 8741513e4
Pull Request #473: feat: Adding `from_hashmap` for bam Header

0 of 9 new or added lines in 1 file covered. (0.0%)

3 existing lines in 2 files now uncovered.

2720 of 3266 relevant lines covered (83.28%)

17801.95 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.64
/src/bam/pileup.rs
1
// Copyright 2014 Johannes Köster.
2
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
3
// This file may not be copied, modified, or distributed
4
// except according to those terms.
5

6
use std::fmt;
7
use std::iter;
8
use std::slice;
9

10
use crate::htslib;
11

12
use crate::bam;
13
use crate::bam::record;
14
use crate::errors::{Error, Result};
15

16
/// Iterator over alignments of a pileup.
17
pub type Alignments<'a> = iter::Map<
18
    slice::Iter<'a, htslib::bam_pileup1_t>,
19
    fn(&'a htslib::bam_pileup1_t) -> Alignment<'a>,
20
>;
21

22
/// A pileup over one genomic position.
23
#[derive(Debug)]
24
pub struct Pileup {
25
    inner: *const htslib::bam_pileup1_t,
26
    depth: u32,
27
    tid: u32,
28
    pos: u32,
29
}
30

31
impl Pileup {
32
    pub fn tid(&self) -> u32 {
100,128✔
33
        self.tid
100,128✔
34
    }
35

36
    pub fn pos(&self) -> u32 {
200,228✔
37
        self.pos
200,228✔
38
    }
39

40
    pub fn depth(&self) -> u32 {
100,128✔
41
        self.depth
100,128✔
42
    }
43

44
    pub fn alignments(&self) -> Alignments<'_> {
100,128✔
45
        self.inner().iter().map(Alignment::new)
100,128✔
46
    }
47

48
    fn inner(&self) -> &[htslib::bam_pileup1_t] {
100,128✔
49
        unsafe {
50
            slice::from_raw_parts(
51
                self.inner as *mut htslib::bam_pileup1_t,
100,128✔
52
                self.depth as usize,
100,128✔
53
            )
54
        }
55
    }
56
}
57

58
/// An aligned read in a pileup.
59
pub struct Alignment<'a> {
60
    inner: &'a htslib::bam_pileup1_t,
61
}
62

63
impl fmt::Debug for Alignment<'_> {
64
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
×
65
        write!(f, "Alignment")
×
66
    }
67
}
68

69
impl<'a> Alignment<'a> {
70
    pub fn new(inner: &'a htslib::bam_pileup1_t) -> Self {
100,763✔
71
        Alignment { inner }
72
    }
73

74
    /// Position within the read. None if either `is_del` or `is_refskip`.
75
    pub fn qpos(&self) -> Option<usize> {
758✔
76
        if self.is_del() || self.is_refskip() {
1,514✔
77
            // there is no alignment position in such a case
78
            None
×
79
        } else {
80
            Some(self.inner.qpos as usize)
758✔
81
        }
82
    }
83

84
    /// Insertion, deletion (with length) if indel starts at next base or None otherwise.
85
    pub fn indel(&self) -> Indel {
100,763✔
86
        match self.inner.indel {
100,755✔
87
            len if len < 0 => Indel::Del(-len as u32),
100,777✔
88
            len if len > 0 => Indel::Ins(len as u32),
2✔
89
            _ => Indel::None,
100,757✔
90
        }
91
    }
92

93
    /// Whether there is a deletion in the alignment at this position.
94
    pub fn is_del(&self) -> bool {
101,363✔
95
        self.inner.is_del() != 0
101,363✔
96
    }
97

98
    /// Whether the alignment starts at this position.
99
    pub fn is_head(&self) -> bool {
×
100
        self.inner.is_head() != 0
×
101
    }
102

103
    /// Whether the alignment ends at this position.
104
    pub fn is_tail(&self) -> bool {
×
105
        self.inner.is_tail() != 0
×
106
    }
107

108
    /// Whether this position is marked as refskip in the CIGAR string.
109
    pub fn is_refskip(&self) -> bool {
1,358✔
110
        self.inner.is_refskip() != 0
1,358✔
111
    }
112

113
    /// The corresponding record.
114
    pub fn record(&self) -> record::Record {
914✔
115
        record::Record::from_inner(self.inner.b)
914✔
116
    }
117
}
118

119
#[derive(PartialEq, Eq, Debug, Copy, Clone, Hash)]
120
pub enum Indel {
121
    Ins(u32),
122
    Del(u32),
123
    None,
124
}
125

126
/// Iterator over pileups.
127
#[derive(Debug)]
128
pub struct Pileups<'a, R: bam::Read> {
129
    #[allow(dead_code)]
130
    reader: &'a mut R,
131
    itr: htslib::bam_plp_t,
132
}
133

134
impl<'a, R: bam::Read> Pileups<'a, R> {
135
    pub fn new(reader: &'a mut R, itr: htslib::bam_plp_t) -> Self {
9✔
136
        Pileups { reader, itr }
137
    }
138

139
    /// Warning: because htslib internally uses signed integer for depth this method
140
    /// will panic if `depth` exceeds `i32::MAX`.
141
    pub fn set_max_depth(&mut self, depth: u32) {
4✔
142
        if depth > i32::MAX as u32 {
4✔
143
            panic!(
2✔
144
                "Maximum value for pileup depth is {} but {} was provided",
UNCOV
145
                i32::MAX,
×
UNCOV
146
                depth
×
147
            )
148
        }
149
        let intdepth = depth as i32;
3✔
150
        unsafe {
151
            htslib::bam_plp_set_maxcnt(self.itr, intdepth);
3✔
152
        }
153
    }
154
}
155

156
impl<R: bam::Read> Iterator for Pileups<'_, R> {
157
    type Item = Result<Pileup>;
158

159
    #[allow(clippy::match_bool)]
160
    fn next(&mut self) -> Option<Result<Pileup>> {
300,332✔
161
        let (mut tid, mut pos, mut depth) = (0i32, 0i32, 0i32);
300,332✔
162
        let inner = unsafe { htslib::bam_plp_auto(self.itr, &mut tid, &mut pos, &mut depth) };
300,332✔
163

164
        match inner.is_null() {
300,332✔
165
            true if depth == -1 => Some(Err(Error::BamPileup)),
5✔
166
            true => None,
5✔
167
            false => Some(Ok(Pileup {
300,329✔
168
                inner,
300,326✔
169
                depth: depth as u32,
300,329✔
170
                tid: tid as u32,
300,329✔
171
                pos: pos as u32,
300,329✔
172
            })),
173
        }
174
    }
175
}
176

177
impl<R: bam::Read> Drop for Pileups<'_, R> {
178
    fn drop(&mut self) {
9✔
179
        unsafe {
180
            htslib::bam_plp_reset(self.itr);
9✔
181
            htslib::bam_plp_destroy(self.itr);
9✔
182
        }
183
    }
184
}
185

186
#[cfg(test)]
187
mod tests {
188

189
    use crate::bam;
190
    use crate::bam::Read;
191

192
    #[test]
193
    fn test_max_pileup() {
194
        let mut bam = bam::Reader::from_path("test/test.bam").unwrap();
195
        let mut p = bam.pileup();
196
        p.set_max_depth(0u32);
197
        p.set_max_depth(800u32);
198
    }
199

200
    #[test]
201
    #[should_panic]
202
    fn test_max_pileup_to_high() {
203
        let mut bam = bam::Reader::from_path("test/test.bam").unwrap();
204
        let mut p = bam.pileup();
205
        p.set_max_depth((i32::MAX as u32) + 1);
206
    }
207
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc