pileuphi_lib 0.9.3

High-throughput, extensible SAM/BAM pileup generation library
use crate::alignment::PileupAlignmentRef;
use crate::errors::{Error, ErrorKind};
use rust_htslib::bam::Record;
use std::collections::HashMap;
use std::hash::{DefaultHasher, Hash, Hasher};

extern "C" {
    fn tweak_overlap_quality(a: *mut rust_htslib::htslib::bam1_t, b: *mut rust_htslib::htslib::bam1_t) -> i32;
}

pub fn tweak_overlap_qual(a: &mut Record, b: &mut Record) -> Result<(), Error> {
    unsafe {
        let ret = tweak_overlap_quality(a.inner_mut() as *mut _, b.inner_mut() as *mut _);
        if ret < 0 {
            let qname = std::str::from_utf8(a.qname())?;
            return Err(Error::from(ErrorKind::MateOverlapFailed(qname.to_string())));
        }
    }
    Ok(())
}

pub type OverlapMap = HashMap<u64, usize>;

pub trait MapOverlaps {
    fn lookup(&self, rec: &PileupAlignmentRef) -> Option<usize>;
    fn store(&mut self, val: usize, rec: &PileupAlignmentRef);
    fn nullify(&mut self, rec1: &PileupAlignmentRef, rec2: &PileupAlignmentRef);
    fn delete_hash(&mut self, r: u64);
    fn delete_read(&mut self, r: &Record);
}

pub fn hash_qname(r: &Record) -> u64 {
    let mut hasher = DefaultHasher::new();
    r.qname().hash(&mut hasher);
    hasher.finish()
}

impl MapOverlaps for OverlapMap {
    fn lookup(&self, rec: &PileupAlignmentRef) -> Option<usize> {
        unsafe {
            let _r = rec.get();
            let len = _r.cstate.read_len_from_cigar;
            let r = &mut _r.rec;

            if r.is_mate_unmapped() || !r.is_proper_pair() {
                return None;
            }

            if (r.mtid() >= 0 && (r.mtid() != r.tid()))
                || r.insert_size().abs() >= 2 * (r.seq_len() as i64) && r.mpos() >= r.pos() + len
            {
                return None;
            }

            let h = hash_qname(r);

            self.get(&h).copied()
        }
    }

    fn store(&mut self, val: usize, rec: &PileupAlignmentRef) {
        unsafe {
            let h = hash_qname(&rec.get().rec);
            self.insert(h, val);
        }
    }

    fn nullify(&mut self, cur_rec: &PileupAlignmentRef, prev_rec: &PileupAlignmentRef) {
        unsafe {
            let h = hash_qname(&cur_rec.get().rec);
            tweak_overlap_qual(&mut prev_rec.get().rec, &mut cur_rec.get().rec).unwrap();
            self.delete_hash(h)
        }
    }

    fn delete_hash(&mut self, r: u64) {
        self.remove(&r);
    }

    fn delete_read(&mut self, r: &Record) {
        let mut h = DefaultHasher::new();
        r.qname().hash(&mut h);
        self.remove(&h.finish());
    }
}