use crate::error_handling::{ErrorConfig, ErrorMode, ErrorOverride, ErrorType, ResolvedAction};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
pub enum ShuffleDirection {
#[default]
ThreePrime,
FivePrime,
}
impl std::fmt::Display for ShuffleDirection {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ShuffleDirection::ThreePrime => write!(f, "3prime"),
ShuffleDirection::FivePrime => write!(f, "5prime"),
}
}
}
impl std::str::FromStr for ShuffleDirection {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"3prime" | "3'" | "three_prime" => Ok(ShuffleDirection::ThreePrime),
"5prime" | "5'" | "five_prime" => Ok(ShuffleDirection::FivePrime),
_ => Err(format!("Invalid shuffle direction: {}", s)),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NormalizeConfig {
pub shuffle_direction: ShuffleDirection,
pub cross_boundaries: bool,
#[serde(skip)]
pub error_config: ErrorConfig,
pub window_size: u64,
pub prevent_overlap: bool,
}
impl Default for NormalizeConfig {
fn default() -> Self {
Self {
shuffle_direction: ShuffleDirection::ThreePrime,
cross_boundaries: false,
error_config: ErrorConfig::lenient(),
window_size: 100,
prevent_overlap: true,
}
}
}
impl PartialEq for NormalizeConfig {
fn eq(&self, other: &Self) -> bool {
self.shuffle_direction == other.shuffle_direction
&& self.cross_boundaries == other.cross_boundaries
&& self.window_size == other.window_size
&& self.prevent_overlap == other.prevent_overlap
}
}
impl Eq for NormalizeConfig {}
impl NormalizeConfig {
pub fn new() -> Self {
Self::default()
}
pub fn strict() -> Self {
Self {
error_config: ErrorConfig::strict(),
..Default::default()
}
}
pub fn lenient() -> Self {
Self {
error_config: ErrorConfig::lenient(),
..Default::default()
}
}
pub fn silent() -> Self {
Self {
error_config: ErrorConfig::silent(),
..Default::default()
}
}
pub fn with_direction(mut self, direction: ShuffleDirection) -> Self {
self.shuffle_direction = direction;
self
}
pub fn allow_crossing_boundaries(mut self) -> Self {
self.cross_boundaries = true;
self
}
pub fn with_error_mode(mut self, mode: ErrorMode) -> Self {
self.error_config = ErrorConfig::new(mode);
self
}
pub fn with_error_override(mut self, error_type: ErrorType, action: ErrorOverride) -> Self {
self.error_config = self.error_config.with_override(error_type, action);
self
}
#[deprecated(
since = "0.2.0",
note = "Use with_error_mode(ErrorMode::Silent) instead"
)]
pub fn skip_validation(mut self) -> Self {
self.error_config = self
.error_config
.with_override(ErrorType::RefSeqMismatch, ErrorOverride::SilentCorrect);
self
}
pub fn with_overlap_prevention(mut self, prevent: bool) -> Self {
self.prevent_overlap = prevent;
self
}
pub fn ref_mismatch_action(&self) -> ResolvedAction {
self.error_config.action_for(ErrorType::RefSeqMismatch)
}
pub fn should_reject_ref_mismatch(&self) -> bool {
self.ref_mismatch_action().should_reject()
}
pub fn should_warn_ref_mismatch(&self) -> bool {
self.ref_mismatch_action().should_warn()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = NormalizeConfig::default();
assert_eq!(config.shuffle_direction, ShuffleDirection::ThreePrime);
assert!(!config.cross_boundaries);
assert!(!config.should_reject_ref_mismatch());
assert!(config.should_warn_ref_mismatch());
}
#[test]
fn test_strict_config() {
let config = NormalizeConfig::strict();
assert!(config.should_reject_ref_mismatch());
assert!(!config.should_warn_ref_mismatch());
}
#[test]
fn test_lenient_config() {
let config = NormalizeConfig::lenient();
assert!(!config.should_reject_ref_mismatch());
assert!(config.should_warn_ref_mismatch());
}
#[test]
fn test_silent_config() {
let config = NormalizeConfig::silent();
assert!(!config.should_reject_ref_mismatch());
assert!(!config.should_warn_ref_mismatch());
}
#[test]
fn test_error_override() {
let config = NormalizeConfig::lenient()
.with_error_override(ErrorType::RefSeqMismatch, ErrorOverride::Reject);
assert!(config.should_reject_ref_mismatch());
}
#[test]
fn test_direction_parsing() {
assert_eq!(
"3prime".parse::<ShuffleDirection>().unwrap(),
ShuffleDirection::ThreePrime
);
assert_eq!(
"5prime".parse::<ShuffleDirection>().unwrap(),
ShuffleDirection::FivePrime
);
}
#[test]
#[allow(deprecated)]
fn test_skip_validation_deprecated() {
let config = NormalizeConfig::default().skip_validation();
assert!(!config.should_reject_ref_mismatch());
assert!(!config.should_warn_ref_mismatch());
}
}