use molecular_annotation::{AnnotationInfo, MolecularAnnotations};
use std::cell::OnceCell;
#[inline]
pub fn primary_qual(qualities: &[u8], type_name: &str) -> u8 {
debug_assert!(
qualities.len() <= 1,
"primary_qual: type {:?} carries {} qualities; fibertools expects \u{2264} 1",
type_name,
qualities.len(),
);
qualities.first().copied().unwrap_or(0)
}
#[derive(Debug)]
pub struct AnnotationTypeView<'a> {
annot: &'a MolecularAnnotations,
type_name: &'a str,
ordered: OnceCell<Vec<AnnotationInfo<'a>>>,
}
impl<'a> AnnotationTypeView<'a> {
pub(crate) fn new(annot: &'a MolecularAnnotations, type_name: &'a str) -> Self {
Self {
annot,
type_name,
ordered: OnceCell::new(),
}
}
pub fn len(&self) -> usize {
self.annot
.get_type(self.type_name)
.map(|t| t.annotations.len())
.unwrap_or(0)
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
fn bam_ordered(&self) -> &[AnnotationInfo<'a>] {
self.ordered.get_or_init(|| {
let Some(it) = self.annot.iter_type(self.type_name) else {
return Vec::new();
};
let mut v: Vec<AnnotationInfo<'a>> = it.collect();
if self.annot.is_reverse_aligned() {
v.reverse();
}
v
})
}
pub fn infos(&self) -> &[AnnotationInfo<'a>] {
self.bam_ordered()
}
pub fn count_query_in(&self, start: i64, end: i64) -> usize {
self.bam_ordered()
.iter()
.filter(|a| {
let pos = a.query_start as i64;
pos >= start && pos < end
})
.count()
}
pub fn starts(&self) -> Vec<i64> {
self.bam_ordered()
.iter()
.map(|a| a.query_start as i64)
.collect()
}
pub fn ends(&self) -> Vec<i64> {
self.bam_ordered()
.iter()
.map(|a| a.query_end as i64)
.collect()
}
pub fn option_starts(&self) -> Vec<Option<i64>> {
self.bam_ordered()
.iter()
.map(|a| Some(a.query_start as i64))
.collect()
}
pub fn option_ends(&self) -> Vec<Option<i64>> {
self.bam_ordered()
.iter()
.map(|a| Some(a.query_end as i64))
.collect()
}
pub fn option_lengths(&self) -> Vec<Option<i64>> {
self.bam_ordered()
.iter()
.map(|a| Some((a.query_end - a.query_start) as i64))
.collect()
}
pub fn lengths(&self) -> Vec<i64> {
self.bam_ordered()
.iter()
.map(|a| (a.query_end - a.query_start) as i64)
.collect()
}
pub fn qual(&self) -> Vec<u8> {
self.qual_at(0)
}
pub fn qual_at(&self, idx: usize) -> Vec<u8> {
self.bam_ordered()
.iter()
.map(|a| {
debug_assert!(
idx > 0 || a.qualities.len() <= 1,
"AnnotationTypeView::qual() called on multi-quality type {:?} ({} qualities); use qual_at(idx)",
self.type_name,
a.qualities.len(),
);
a.qualities.get(idx).copied().unwrap_or(0)
})
.collect()
}
pub fn reference_starts(&self) -> Vec<Option<i64>> {
self.bam_ordered()
.iter()
.map(|a| a.ref_start.map(|x| x as i64))
.collect()
}
pub fn reference_ends(&self) -> Vec<Option<i64>> {
self.bam_ordered()
.iter()
.map(|a| a.ref_end.map(|x| x as i64))
.collect()
}
pub fn reference_lengths(&self) -> Vec<Option<i64>> {
self.bam_ordered()
.iter()
.map(|a| match (a.ref_start, a.ref_end) {
(Some(s), Some(e)) => Some((e - s) as i64),
_ => None,
})
.collect()
}
pub fn iter(&self) -> std::vec::IntoIter<AnnotationInfo<'a>> {
self.bam_ordered().to_vec().into_iter()
}
}
impl<'a> IntoIterator for &AnnotationTypeView<'a> {
type Item = AnnotationInfo<'a>;
type IntoIter = std::vec::IntoIter<AnnotationInfo<'a>>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}