#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct CountFilter {
pub min_count: Option<u64>,
pub max_count: Option<u64>,
}
impl CountFilter {
pub fn new(min_count: Option<u64>, max_count: Option<u64>) -> Self {
Self {
min_count,
max_count,
}
}
pub fn passes(&self, count: u64) -> bool {
if let Some(min) = self.min_count {
if count < min {
return false;
}
}
if let Some(max) = self.max_count {
if count > max {
return false;
}
}
true
}
}
#[derive(Debug, Clone)]
pub struct CountFilterConfig {
pub filter: CountFilter,
pub enabled: bool,
pub validation_state: ValidationState,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ValidationState {
Valid,
Invalid(Vec<String>),
}
impl CountFilterConfig {
pub fn new(min_count: Option<u64>, max_count: Option<u64>) -> Self {
let filter = CountFilter::new(min_count, max_count);
let enabled = min_count.is_some() || max_count.is_some();
let validation_state = Self::validate_parameters(min_count, max_count);
Self {
filter,
enabled,
validation_state,
}
}
fn validate_parameters(min_count: Option<u64>, max_count: Option<u64>) -> ValidationState {
let mut errors = Vec::new();
if let (Some(min), Some(max)) = (min_count, max_count) {
if min > max {
errors.push("Minimum count cannot exceed maximum count".to_string());
}
}
if errors.is_empty() {
ValidationState::Valid
} else {
ValidationState::Invalid(errors)
}
}
pub fn is_valid(&self) -> bool {
matches!(self.validation_state, ValidationState::Valid)
}
pub fn get_errors(&self) -> Vec<String> {
match &self.validation_state {
ValidationState::Valid => Vec::new(),
ValidationState::Invalid(errors) => errors.clone(),
}
}
}
#[derive(Debug, Clone)]
pub struct FilteringResult {
pub total_before: u64,
pub unique_before: u64,
pub kept_after: u64,
pub filtered_out: u64,
pub filter: CountFilter,
}
impl FilteringResult {
pub fn new(
total_before: u64,
unique_before: u64,
kept_after: u64,
filter: CountFilter,
) -> Self {
let filtered_out = unique_before.saturating_sub(kept_after);
Self {
total_before,
unique_before,
kept_after,
filtered_out,
filter,
}
}
pub fn has_filtering(&self) -> bool {
self.filter.min_count.is_some() || self.filter.max_count.is_some()
}
pub fn kept_percentage(&self) -> f64 {
if self.unique_before == 0 {
100.0
} else {
(self.kept_after as f64 / self.unique_before as f64) * 100.0
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_count_filter_default() {
let filter = CountFilter::default();
assert!(filter.passes(0));
assert!(filter.passes(1));
assert!(filter.passes(1000));
}
#[test]
fn test_count_filter_min_only() {
let filter = CountFilter::new(Some(5), None);
assert!(!filter.passes(4));
assert!(filter.passes(5));
assert!(filter.passes(100));
}
#[test]
fn test_count_filter_max_only() {
let filter = CountFilter::new(None, Some(10));
assert!(filter.passes(0));
assert!(filter.passes(10));
assert!(!filter.passes(11));
}
#[test]
fn test_count_filter_range() {
let filter = CountFilter::new(Some(5), Some(10));
assert!(!filter.passes(4));
assert!(filter.passes(5));
assert!(filter.passes(7));
assert!(filter.passes(10));
assert!(!filter.passes(11));
}
#[test]
fn test_filter_config_valid() {
let config = CountFilterConfig::new(Some(5), Some(10));
assert!(config.is_valid());
assert!(config.enabled);
assert_eq!(config.get_errors().len(), 0);
}
#[test]
fn test_filter_config_invalid_range() {
let config = CountFilterConfig::new(Some(10), Some(5));
assert!(!config.is_valid());
assert_eq!(config.get_errors().len(), 1);
assert!(config.get_errors()[0].contains("Minimum count cannot exceed maximum"));
}
#[test]
fn test_filter_config_no_filtering() {
let config = CountFilterConfig::new(None, None);
assert!(config.is_valid());
assert!(!config.enabled);
}
#[test]
fn test_filtering_result() {
let filter = CountFilter::new(Some(5), None);
let result = FilteringResult::new(1000, 100, 80, filter);
assert_eq!(result.total_before, 1000);
assert_eq!(result.unique_before, 100);
assert_eq!(result.kept_after, 80);
assert_eq!(result.filtered_out, 20);
assert!(result.has_filtering());
assert_eq!(result.kept_percentage(), 80.0);
}
}