use super::TrimResult;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct GlobalTrimConfig {
pub trim_front1: usize,
pub trim_tail1: usize,
pub trim_front2: usize,
pub trim_tail2: usize,
}
impl GlobalTrimConfig {
pub fn new() -> Self {
Self::default()
}
pub fn with_trim_front1(mut self, bases: usize) -> Self {
self.trim_front1 = bases;
self
}
pub fn with_trim_tail1(mut self, bases: usize) -> Self {
self.trim_tail1 = bases;
self
}
pub fn with_trim_front2(mut self, bases: usize) -> Self {
self.trim_front2 = bases;
self
}
pub fn with_trim_tail2(mut self, bases: usize) -> Self {
self.trim_tail2 = bases;
self
}
pub fn for_read1(&self) -> (usize, usize) {
(self.trim_front1, self.trim_tail1)
}
pub fn for_read2(&self) -> (usize, usize) {
(self.trim_front2, self.trim_tail2)
}
pub fn is_enabled(&self) -> bool {
self.trim_front1 > 0
|| self.trim_tail1 > 0
|| self.trim_front2 > 0
|| self.trim_tail2 > 0
}
}
pub fn trim_global(seq: &[u8], config: &GlobalTrimConfig, is_read2: bool) -> TrimResult {
let len = seq.len();
if !config.is_enabled() {
return TrimResult::full(len);
}
let (trim_front, trim_tail) = if is_read2 {
config.for_read2()
} else {
config.for_read1()
};
if len == 0 {
return TrimResult::empty();
}
let start = trim_front.min(len);
let end = len.saturating_sub(trim_tail);
if start >= end {
return TrimResult::empty();
}
TrimResult::new(start, end)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_global_trim_config_default() {
let config = GlobalTrimConfig::default();
assert_eq!(config.trim_front1, 0);
assert_eq!(config.trim_tail1, 0);
assert_eq!(config.trim_front2, 0);
assert_eq!(config.trim_tail2, 0);
assert!(!config.is_enabled());
}
#[test]
fn test_global_trim_config_builder() {
let config = GlobalTrimConfig::new()
.with_trim_front1(5)
.with_trim_tail1(3)
.with_trim_front2(7)
.with_trim_tail2(4);
assert_eq!(config.trim_front1, 5);
assert_eq!(config.trim_tail1, 3);
assert_eq!(config.trim_front2, 7);
assert_eq!(config.trim_tail2, 4);
assert!(config.is_enabled());
}
#[test]
fn test_global_trim_config_for_read1() {
let config = GlobalTrimConfig::new()
.with_trim_front1(5)
.with_trim_tail1(3);
let (front, tail) = config.for_read1();
assert_eq!(front, 5);
assert_eq!(tail, 3);
}
#[test]
fn test_global_trim_config_for_read2() {
let config = GlobalTrimConfig::new()
.with_trim_front2(7)
.with_trim_tail2(4);
let (front, tail) = config.for_read2();
assert_eq!(front, 7);
assert_eq!(tail, 4);
}
#[test]
fn test_trim_global_no_trimming() {
let seq = b"ACGTACGTACGTACGT";
let config = GlobalTrimConfig::new();
let result = trim_global(seq, &config, false);
assert_eq!(result.start, 0);
assert_eq!(result.end, 16);
assert_eq!(result.len(), 16);
}
#[test]
fn test_trim_global_front_only() {
let seq = b"ACGTACGTACGTACGT";
let config = GlobalTrimConfig::new().with_trim_front1(5);
let result = trim_global(seq, &config, false);
assert_eq!(result.start, 5);
assert_eq!(result.end, 16);
assert_eq!(result.len(), 11);
assert_eq!(result.apply(seq), b"CGTACGTACGT");
}
#[test]
fn test_trim_global_tail_only() {
let seq = b"ACGTACGTACGTACGT";
let config = GlobalTrimConfig::new().with_trim_tail1(4);
let result = trim_global(seq, &config, false);
assert_eq!(result.start, 0);
assert_eq!(result.end, 12);
assert_eq!(result.len(), 12);
assert_eq!(result.apply(seq), b"ACGTACGTACGT");
}
#[test]
fn test_trim_global_both_ends() {
let seq = b"ACGTACGTACGTACGT";
let config = GlobalTrimConfig::new()
.with_trim_front1(3)
.with_trim_tail1(2);
let result = trim_global(seq, &config, false);
assert_eq!(result.start, 3);
assert_eq!(result.end, 14);
assert_eq!(result.len(), 11);
assert_eq!(result.apply(seq), b"TACGTACGTAC");
}
#[test]
fn test_trim_global_read1_vs_read2() {
let seq = b"ACGTACGTACGTACGT";
let config = GlobalTrimConfig::new()
.with_trim_front1(2)
.with_trim_tail1(1)
.with_trim_front2(4)
.with_trim_tail2(3);
let result_r1 = trim_global(seq, &config, false);
assert_eq!(result_r1.start, 2);
assert_eq!(result_r1.end, 15);
assert_eq!(result_r1.len(), 13);
let result_r2 = trim_global(seq, &config, true);
assert_eq!(result_r2.start, 4);
assert_eq!(result_r2.end, 13);
assert_eq!(result_r2.len(), 9);
}
#[test]
fn test_trim_global_empty_sequence() {
let seq = b"";
let config = GlobalTrimConfig::new()
.with_trim_front1(5)
.with_trim_tail1(3);
let result = trim_global(seq, &config, false);
assert!(result.is_empty());
}
#[test]
fn test_trim_global_trim_more_than_length() {
let seq = b"ACGTACGT"; let config = GlobalTrimConfig::new()
.with_trim_front1(5)
.with_trim_tail1(5);
let result = trim_global(seq, &config, false);
assert!(result.is_empty());
}
#[test]
fn test_trim_global_trim_exact_length() {
let seq = b"ACGTACGT"; let config = GlobalTrimConfig::new()
.with_trim_front1(4)
.with_trim_tail1(4);
let result = trim_global(seq, &config, false);
assert!(result.is_empty());
}
#[test]
fn test_trim_global_front_exceeds_length() {
let seq = b"ACGT";
let config = GlobalTrimConfig::new().with_trim_front1(10);
let result = trim_global(seq, &config, false);
assert!(result.is_empty());
}
#[test]
fn test_trim_global_tail_exceeds_length() {
let seq = b"ACGT";
let config = GlobalTrimConfig::new().with_trim_tail1(10);
let result = trim_global(seq, &config, false);
assert!(result.is_empty());
}
#[test]
fn test_trim_global_one_base_remaining() {
let seq = b"ACGTACGT";
let config = GlobalTrimConfig::new()
.with_trim_front1(3)
.with_trim_tail1(4);
let result = trim_global(seq, &config, false);
assert_eq!(result.len(), 1);
assert_eq!(result.apply(seq), b"T");
}
#[test]
fn test_is_enabled() {
let config1 = GlobalTrimConfig::new();
assert!(!config1.is_enabled());
let config2 = GlobalTrimConfig::new().with_trim_front1(1);
assert!(config2.is_enabled());
let config3 = GlobalTrimConfig::new().with_trim_tail2(1);
assert!(config3.is_enabled());
}
}