use rust_htslib::bam::record::Aux;
#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
#[expect(
clippy::exhaustive_structs,
reason = "vendored type constructed directly in user code and doctests"
)]
pub struct FiberAnnotation {
pub start: i64,
pub end: i64,
pub length: i64,
pub qual: u8,
pub reference_start: Option<i64>,
pub reference_end: Option<i64>,
pub reference_length: Option<i64>,
pub extra_columns: Option<Vec<String>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
#[expect(
clippy::exhaustive_structs,
reason = "vendored type constructed directly in user code and doctests"
)]
pub struct FiberAnnotations {
pub annotations: Vec<FiberAnnotation>,
pub seq_len: i64,
pub reverse: bool,
}
pub type Ranges = FiberAnnotations;
impl FiberAnnotations {
#[must_use]
pub fn from_annotations(
mut annotations: Vec<FiberAnnotation>,
seq_len: i64,
reverse: bool,
) -> Self {
annotations.sort_by_key(|a| a.start);
Self {
annotations,
seq_len,
reverse,
}
}
#[must_use]
pub fn starts(&self) -> Vec<i64> {
self.annotations.iter().map(|a| a.start).collect()
}
#[must_use]
pub fn ends(&self) -> Vec<i64> {
self.annotations.iter().map(|a| a.end).collect()
}
#[must_use]
pub fn lengths(&self) -> Vec<i64> {
self.annotations.iter().map(|a| a.length).collect()
}
#[must_use]
pub fn qual(&self) -> Vec<u8> {
self.annotations.iter().map(|a| a.qual).collect()
}
#[must_use]
pub fn reference_starts(&self) -> Vec<Option<i64>> {
self.annotations.iter().map(|a| a.reference_start).collect()
}
#[must_use]
pub fn reference_ends(&self) -> Vec<Option<i64>> {
self.annotations.iter().map(|a| a.reference_end).collect()
}
#[must_use]
pub fn reference_lengths(&self) -> Vec<Option<i64>> {
self.annotations
.iter()
.map(|a| a.reference_length)
.collect()
}
}
#[derive(Eq, PartialEq, Debug, PartialOrd, Ord, Clone)]
#[expect(
clippy::exhaustive_structs,
reason = "vendored type constructed directly in user code and doctests"
)]
pub struct BaseMod {
pub modified_base: u8,
pub strand: char,
pub modification_type: char,
pub ranges: Ranges,
pub record_is_reverse: bool,
}
#[derive(Eq, PartialEq, Debug, Clone)]
#[expect(
clippy::exhaustive_structs,
reason = "vendored type constructed directly in user code and doctests"
)]
pub struct BaseMods {
pub base_mods: Vec<BaseMod>,
}
#[must_use]
pub fn convert_seq_uppercase(mut seq: Vec<u8>) -> Vec<u8> {
for base in &mut seq {
match *base {
b'a' => *base = b'A',
b'c' => *base = b'C',
b'g' => *base = b'G',
b't' => *base = b'T',
b'n' => *base = b'N',
_ => {}
}
}
seq
}
#[must_use]
pub fn get_u8_tag(record: &rust_htslib::bam::Record, tag: &[u8; 2]) -> Vec<u8> {
if let Ok(Aux::ArrayU8(array)) = record.aux(tag) {
array.iter().collect()
} else {
vec![]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn convert_seq_uppercase_mixed() {
let input = vec![
b'A', b'C', b'G', b'T', b'N', b'a', b'c', b'g', b't', b'n', b'=',
];
let expected = vec![
b'A', b'C', b'G', b'T', b'N', b'A', b'C', b'G', b'T', b'N', b'=',
];
assert_eq!(convert_seq_uppercase(input), expected);
}
#[test]
fn convert_seq_uppercase_already_upper() {
let input = vec![b'A', b'C', b'G', b'T'];
assert_eq!(convert_seq_uppercase(input.clone()), input);
}
#[test]
fn convert_seq_uppercase_empty() {
let input: Vec<u8> = vec![];
assert_eq!(convert_seq_uppercase(input), Vec::<u8>::new());
}
#[test]
fn get_u8_tag_missing() {
let record = rust_htslib::bam::Record::new();
assert_eq!(get_u8_tag(&record, b"ML"), Vec::<u8>::new());
}
#[test]
fn fiber_annotations_accessors() {
let annotations = FiberAnnotations {
annotations: vec![
FiberAnnotation {
start: 5,
end: 10,
length: 5,
qual: 100,
reference_start: Some(50),
reference_end: Some(55),
reference_length: Some(5),
extra_columns: None,
},
FiberAnnotation {
start: 20,
end: 25,
length: 5,
qual: 150,
reference_start: None,
reference_end: None,
reference_length: None,
extra_columns: None,
},
],
seq_len: 50,
reverse: false,
};
assert_eq!(annotations.starts(), vec![5, 20]);
assert_eq!(annotations.ends(), vec![10, 25]);
assert_eq!(annotations.lengths(), vec![5, 5]);
assert_eq!(annotations.qual(), vec![100, 150]);
assert_eq!(annotations.reference_starts(), vec![Some(50), None]);
assert_eq!(annotations.reference_ends(), vec![Some(55), None]);
assert_eq!(annotations.reference_lengths(), vec![Some(5), None]);
}
#[test]
fn fiber_annotations_empty() {
let annotations = FiberAnnotations {
annotations: vec![],
seq_len: 0,
reverse: false,
};
assert!(annotations.starts().is_empty());
assert!(annotations.ends().is_empty());
assert!(annotations.lengths().is_empty());
assert!(annotations.qual().is_empty());
assert!(annotations.reference_starts().is_empty());
assert!(annotations.reference_ends().is_empty());
assert!(annotations.reference_lengths().is_empty());
}
}