pub mod merger;
pub use merger::{MergeResult, MergeStats, OverlapInfo, ReadMerger};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MergeConfig {
pub enabled: bool,
pub min_overlap: usize,
pub max_mismatch_ratio: f64,
pub correct_mismatches: bool,
pub quality_diff_threshold: u8,
}
impl Default for MergeConfig {
fn default() -> Self {
Self {
enabled: false,
min_overlap: 30,
max_mismatch_ratio: 0.1,
correct_mismatches: true,
quality_diff_threshold: 3,
}
}
}
impl MergeConfig {
pub fn new() -> Self {
Self::default()
}
pub fn enabled() -> Self {
Self {
enabled: true,
..Self::default()
}
}
pub fn disabled() -> Self {
Self {
enabled: false,
..Self::default()
}
}
pub fn with_enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
pub fn with_min_overlap(mut self, min_overlap: usize) -> Self {
self.min_overlap = min_overlap;
self
}
pub fn with_max_mismatch_ratio(mut self, ratio: f64) -> Self {
self.max_mismatch_ratio = ratio.clamp(0.0, 1.0);
self
}
pub fn with_correct_mismatches(mut self, correct: bool) -> Self {
self.correct_mismatches = correct;
self
}
pub fn with_quality_diff_threshold(mut self, threshold: u8) -> Self {
self.quality_diff_threshold = threshold;
self
}
pub fn validate(&self) -> Result<(), &'static str> {
if self.min_overlap == 0 {
return Err("min_overlap must be greater than 0");
}
if self.max_mismatch_ratio < 0.0 || self.max_mismatch_ratio > 1.0 {
return Err("max_mismatch_ratio must be between 0.0 and 1.0");
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_merge_config_default() {
let config = MergeConfig::default();
assert!(!config.enabled);
assert_eq!(config.min_overlap, 30);
assert!((config.max_mismatch_ratio - 0.1).abs() < f64::EPSILON);
assert!(config.correct_mismatches);
}
#[test]
fn test_merge_config_enabled() {
let config = MergeConfig::enabled();
assert!(config.enabled);
}
#[test]
fn test_merge_config_disabled() {
let config = MergeConfig::disabled();
assert!(!config.enabled);
}
#[test]
fn test_merge_config_builder() {
let config = MergeConfig::new()
.with_enabled(true)
.with_min_overlap(20)
.with_max_mismatch_ratio(0.15)
.with_correct_mismatches(false)
.with_quality_diff_threshold(5);
assert!(config.enabled);
assert_eq!(config.min_overlap, 20);
assert!((config.max_mismatch_ratio - 0.15).abs() < f64::EPSILON);
assert!(!config.correct_mismatches);
assert_eq!(config.quality_diff_threshold, 5);
}
#[test]
fn test_merge_config_clamp_mismatch_ratio() {
let config = MergeConfig::new().with_max_mismatch_ratio(1.5);
assert!((config.max_mismatch_ratio - 1.0).abs() < f64::EPSILON);
let config = MergeConfig::new().with_max_mismatch_ratio(-0.5);
assert!((config.max_mismatch_ratio - 0.0).abs() < f64::EPSILON);
}
#[test]
fn test_merge_config_validate() {
let config = MergeConfig::default();
assert!(config.validate().is_ok());
let bad_config = MergeConfig::new().with_min_overlap(0);
assert!(bad_config.validate().is_err());
}
#[test]
fn test_merge_config_serialize() {
let config = MergeConfig::enabled();
let json = serde_json::to_string(&config).unwrap();
let deserialized: MergeConfig = serde_json::from_str(&json).unwrap();
assert_eq!(config.enabled, deserialized.enabled);
assert_eq!(config.min_overlap, deserialized.min_overlap);
}
}