use super::AGENT_MIN_FUZZY_SCORE;
#[derive(Debug, Clone, Copy, PartialEq)]
#[non_exhaustive]
pub struct FuzzySpanPolicy {
pub max_ratio: f64,
pub abs_extra_chars: usize,
pub near_floor_score_lo: f64,
pub near_floor_score_hi: f64,
pub near_floor_ratio: f64,
}
impl Default for FuzzySpanPolicy {
fn default() -> Self {
Self {
max_ratio: 4.0,
abs_extra_chars: 40,
near_floor_score_lo: AGENT_MIN_FUZZY_SCORE,
near_floor_score_hi: 0.95,
near_floor_ratio: 2.0,
}
}
}
#[must_use]
pub fn fuzzy_span_suspicious(
old: &str,
matched_text: Option<&str>,
match_score: Option<f64>,
) -> bool {
fuzzy_span_suspicious_with_policy(old, matched_text, match_score, &FuzzySpanPolicy::default())
}
#[must_use]
pub fn fuzzy_span_suspicious_with_policy(
old: &str,
matched_text: Option<&str>,
match_score: Option<f64>,
policy: &FuzzySpanPolicy,
) -> bool {
let Some(matched) = matched_text.filter(|s| !s.is_empty()) else {
return false;
};
let old_n = old.chars().count();
let matched_n = matched.chars().count();
if old_n == 0 {
return matched_n > 0;
}
let ratio_cap = (old_n as f64 * policy.max_ratio).ceil() as usize;
let abs_cap = old_n.saturating_add(policy.abs_extra_chars);
let max_allowed = ratio_cap.max(abs_cap);
if matched_n > max_allowed {
return true;
}
if let Some(score) = match_score
&& score >= policy.near_floor_score_lo
&& score < policy.near_floor_score_hi
{
let ratio = matched_n as f64 / old_n as f64;
if ratio > policy.near_floor_ratio {
return true;
}
}
false
}