use parlov_core::SignalKind;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) enum SignalFamily {
Range,
CacheValidator,
Auth,
Precondition,
Negotiation,
ErrorBody,
Redirect,
General,
}
const FAMILY_CAP: u8 = 75;
pub(crate) struct SignalContribution {
pub family: SignalFamily,
pub confidence: f32,
pub impact: u8,
pub description: String,
}
pub(crate) struct FamilyAdjustedScores {
pub confidence_total: f32,
pub impact_total: u8,
pub family_count: usize,
}
pub(crate) fn apply_family_adjustment(
contributions: &[SignalContribution],
) -> FamilyAdjustedScores {
let mut family_counts = std::collections::HashMap::new();
let mut confidence_total: f32 = 0.0;
let mut impact_total: u16 = 0;
for contrib in contributions {
let count = family_counts
.entry(contrib.family)
.or_insert(0u8);
let capped = family_adjusted_confidence(contrib.confidence, *count);
confidence_total += capped;
impact_total += u16::from(contrib.impact);
*count += 1;
}
FamilyAdjustedScores {
confidence_total,
impact_total: impact_total.min(255) as u8,
family_count: family_counts.len(),
}
}
fn family_adjusted_confidence(raw: f32, position: u8) -> f32 {
let cap = f32::from(FAMILY_CAP);
let clamped = raw.min(cap);
match position {
0 => clamped,
1 => clamped * 0.5,
_ => 0.0,
}
}
pub(crate) fn header_family(name: &str) -> SignalFamily {
match name {
"content-range" | "accept-ranges" => SignalFamily::Range,
"etag" | "last-modified" => SignalFamily::CacheValidator,
"www-authenticate" => SignalFamily::Auth,
"location" => SignalFamily::Redirect,
_ => SignalFamily::General,
}
}
pub(crate) fn status_code_family(baseline_status: u16) -> SignalFamily {
match baseline_status {
206 | 416 => SignalFamily::Range,
304 => SignalFamily::CacheValidator,
401 | 403 => SignalFamily::Auth,
412 => SignalFamily::Precondition,
406 => SignalFamily::Negotiation,
300 | 301 | 302 | 303 | 307 | 308 => SignalFamily::Redirect,
_ => SignalFamily::General,
}
}
pub(crate) fn corroboration_bonus(family_count: usize) -> u8 {
match family_count {
0 | 1 => 0,
2 => 3,
3 => 6,
_ => 8,
}
}
pub(crate) fn signal_kind_family(_kind: SignalKind) -> SignalFamily {
SignalFamily::General
}
#[cfg(test)]
#[path = "families_tests.rs"]
mod tests;