#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ImportanceLevel {
Critical,
Important,
Normal,
Low,
Noise,
}
impl ImportanceLevel {
pub fn as_str(&self) -> &'static str {
match self {
Self::Critical => "critical",
Self::Important => "important",
Self::Normal => "normal",
Self::Low => "low",
Self::Noise => "noise",
}
}
pub fn from_score(score: f32) -> Self {
if score >= 0.8 {
Self::Critical
} else if score >= 0.5 {
Self::Important
} else if score >= 0.2 {
Self::Normal
} else if score >= 0.0 {
Self::Low
} else {
Self::Noise
}
}
}
#[derive(Debug, Clone)]
pub struct ImportanceSignals {
pub is_mutual_contact: bool,
pub is_direct_recipient: bool,
pub is_reply_to_my_email: bool,
pub has_action_items: bool,
pub is_vip_sender: bool,
pub is_bulk_sender: bool,
pub is_mailing_list: bool,
pub is_automated: bool,
pub has_tracking_pixel: bool,
pub is_template_heavy: bool,
pub text_to_html_ratio: f32,
pub link_count: usize,
pub contact_importance_bias: f32,
}
impl Default for ImportanceSignals {
fn default() -> Self {
Self {
is_mutual_contact: false,
is_direct_recipient: false,
is_reply_to_my_email: false,
has_action_items: false,
is_vip_sender: false,
is_bulk_sender: false,
is_mailing_list: false,
is_automated: false,
has_tracking_pixel: false,
is_template_heavy: false,
text_to_html_ratio: 1.0,
link_count: 0,
contact_importance_bias: 0.0,
}
}
}
pub fn calculate_importance(signals: &ImportanceSignals) -> (ImportanceLevel, f32) {
let mut score: f32 = 0.3;
if signals.is_mutual_contact {
score += 0.3;
}
if signals.is_direct_recipient {
score += 0.1;
}
if signals.is_reply_to_my_email {
score += 0.3;
}
if signals.has_action_items {
score += 0.2;
}
if signals.is_vip_sender {
score += 0.4;
}
if signals.is_bulk_sender {
score -= 0.3;
}
if signals.is_mailing_list {
score -= 0.2;
}
if signals.is_automated {
score -= 0.3;
}
if signals.has_tracking_pixel {
score -= 0.1;
}
if signals.is_template_heavy {
score -= 0.2;
}
if signals.link_count > 20 {
score -= 0.1;
}
score += signals.contact_importance_bias;
score = score.clamp(-0.5, 1.0);
let level = ImportanceLevel::from_score(score);
(level, score)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn baseline_score_is_normal() {
let signals = ImportanceSignals::default();
let (level, score) = calculate_importance(&signals);
assert_eq!(level, ImportanceLevel::Normal);
assert!((score - 0.3).abs() < 0.001);
}
#[test]
fn mutual_contact_direct_is_important() {
let signals = ImportanceSignals {
is_mutual_contact: true,
is_direct_recipient: true,
..Default::default()
};
let (level, _) = calculate_importance(&signals);
assert!(level == ImportanceLevel::Important || level == ImportanceLevel::Critical);
}
#[test]
fn vip_reply_is_critical() {
let signals = ImportanceSignals {
is_vip_sender: true,
is_reply_to_my_email: true,
is_mutual_contact: true,
..Default::default()
};
let (level, score) = calculate_importance(&signals);
assert_eq!(level, ImportanceLevel::Critical);
assert!(score >= 0.8);
}
#[test]
fn bulk_automated_is_low_or_noise() {
let signals = ImportanceSignals {
is_bulk_sender: true,
is_automated: true,
is_mailing_list: true,
has_tracking_pixel: true,
..Default::default()
};
let (level, _) = calculate_importance(&signals);
assert!(level == ImportanceLevel::Low || level == ImportanceLevel::Noise);
}
#[test]
fn template_heavy_marketing_is_low() {
let signals = ImportanceSignals {
is_bulk_sender: true,
has_tracking_pixel: true,
is_template_heavy: true,
link_count: 30,
..Default::default()
};
let (level, _) = calculate_importance(&signals);
assert!(level == ImportanceLevel::Low || level == ImportanceLevel::Noise);
}
#[test]
fn user_bias_positive_boosts() {
let signals = ImportanceSignals {
contact_importance_bias: 0.5,
..Default::default()
};
let (level, _) = calculate_importance(&signals);
assert_eq!(level, ImportanceLevel::Critical);
}
#[test]
fn user_bias_negative_demotes() {
let signals = ImportanceSignals {
is_mutual_contact: true,
contact_importance_bias: -0.5,
..Default::default()
};
let (level, _) = calculate_importance(&signals);
assert!(level == ImportanceLevel::Normal || level == ImportanceLevel::Low);
}
#[test]
fn score_clamped_to_range() {
let signals = ImportanceSignals {
is_vip_sender: true,
is_mutual_contact: true,
is_reply_to_my_email: true,
is_direct_recipient: true,
has_action_items: true,
contact_importance_bias: 1.0,
..Default::default()
};
let (_, score) = calculate_importance(&signals);
assert!(score <= 1.0);
let signals2 = ImportanceSignals {
is_bulk_sender: true,
is_automated: true,
is_mailing_list: true,
has_tracking_pixel: true,
is_template_heavy: true,
contact_importance_bias: -1.0,
..Default::default()
};
let (_, score2) = calculate_importance(&signals2);
assert!(score2 >= -0.5);
}
#[test]
fn importance_level_as_str() {
assert_eq!(ImportanceLevel::Critical.as_str(), "critical");
assert_eq!(ImportanceLevel::Important.as_str(), "important");
assert_eq!(ImportanceLevel::Normal.as_str(), "normal");
assert_eq!(ImportanceLevel::Low.as_str(), "low");
assert_eq!(ImportanceLevel::Noise.as_str(), "noise");
}
#[test]
fn importance_level_from_score_boundaries() {
assert_eq!(ImportanceLevel::from_score(1.0), ImportanceLevel::Critical);
assert_eq!(ImportanceLevel::from_score(0.8), ImportanceLevel::Critical);
assert_eq!(
ImportanceLevel::from_score(0.79),
ImportanceLevel::Important
);
assert_eq!(ImportanceLevel::from_score(0.5), ImportanceLevel::Important);
assert_eq!(ImportanceLevel::from_score(0.49), ImportanceLevel::Normal);
assert_eq!(ImportanceLevel::from_score(0.2), ImportanceLevel::Normal);
assert_eq!(ImportanceLevel::from_score(0.19), ImportanceLevel::Low);
assert_eq!(ImportanceLevel::from_score(0.0), ImportanceLevel::Low);
assert_eq!(ImportanceLevel::from_score(-0.01), ImportanceLevel::Noise);
}
#[test]
fn default_signals_have_text_to_html_ratio_one() {
let d = ImportanceSignals::default();
assert!((d.text_to_html_ratio - 1.0).abs() < f32::EPSILON);
assert_eq!(d.link_count, 0);
assert!((d.contact_importance_bias).abs() < f32::EPSILON);
}
#[test]
fn link_count_threshold_exactly_20_does_not_penalize() {
let s = ImportanceSignals {
link_count: 20,
..Default::default()
};
let (_, score) = calculate_importance(&s);
assert!((score - 0.3).abs() < 0.001);
}
#[test]
fn link_count_above_threshold_penalizes() {
let s = ImportanceSignals {
link_count: 21,
..Default::default()
};
let (_, score) = calculate_importance(&s);
assert!((score - 0.2).abs() < 0.001);
}
#[test]
fn from_score_negative_clamp_lower_bound() {
assert_eq!(ImportanceLevel::from_score(-0.5), ImportanceLevel::Noise);
assert_eq!(ImportanceLevel::from_score(-100.0), ImportanceLevel::Noise);
assert_eq!(
ImportanceLevel::from_score(f32::NEG_INFINITY),
ImportanceLevel::Noise
);
}
#[test]
fn from_score_above_one_still_critical() {
assert_eq!(ImportanceLevel::from_score(2.0), ImportanceLevel::Critical);
assert_eq!(
ImportanceLevel::from_score(f32::INFINITY),
ImportanceLevel::Critical
);
}
#[test]
fn from_score_nan_behavior_is_noise() {
assert_eq!(
ImportanceLevel::from_score(f32::NAN),
ImportanceLevel::Noise
);
}
#[test]
fn signals_with_all_positives_capped_at_one() {
let s = ImportanceSignals {
is_mutual_contact: true,
is_direct_recipient: true,
is_reply_to_my_email: true,
has_action_items: true,
is_vip_sender: true,
contact_importance_bias: 10.0, ..Default::default()
};
let (level, score) = calculate_importance(&s);
assert!((score - 1.0).abs() < f32::EPSILON);
assert_eq!(level, ImportanceLevel::Critical);
}
#[test]
fn signals_with_all_negatives_capped_at_minus_half() {
let s = ImportanceSignals {
is_bulk_sender: true,
is_mailing_list: true,
is_automated: true,
has_tracking_pixel: true,
is_template_heavy: true,
link_count: 100,
contact_importance_bias: -10.0,
..Default::default()
};
let (level, score) = calculate_importance(&s);
assert!((score - (-0.5)).abs() < f32::EPSILON);
assert_eq!(level, ImportanceLevel::Noise);
}
#[test]
fn direct_recipient_alone_does_not_promote_to_important() {
let s = ImportanceSignals {
is_direct_recipient: true,
..Default::default()
};
let (level, score) = calculate_importance(&s);
assert!((score - 0.4).abs() < 0.001);
assert_eq!(level, ImportanceLevel::Normal);
}
#[test]
fn signals_cloning_preserves_all_fields() {
let s = ImportanceSignals {
is_mutual_contact: true,
is_direct_recipient: true,
is_vip_sender: true,
text_to_html_ratio: 0.5,
link_count: 7,
contact_importance_bias: 0.25,
..Default::default()
};
let c = s.clone();
assert_eq!(c.is_mutual_contact, s.is_mutual_contact);
assert_eq!(c.is_direct_recipient, s.is_direct_recipient);
assert_eq!(c.is_vip_sender, s.is_vip_sender);
assert!((c.text_to_html_ratio - s.text_to_html_ratio).abs() < f32::EPSILON);
assert_eq!(c.link_count, s.link_count);
assert!((c.contact_importance_bias - s.contact_importance_bias).abs() < f32::EPSILON);
}
#[test]
fn importance_level_copy_trait() {
let l = ImportanceLevel::Critical;
let copy = l;
assert_eq!(l, copy);
assert_eq!(copy.as_str(), "critical");
}
#[test]
fn calculate_importance_returns_consistent_level_and_score() {
let cases = [
ImportanceSignals::default(),
ImportanceSignals {
is_vip_sender: true,
..Default::default()
},
ImportanceSignals {
is_bulk_sender: true,
contact_importance_bias: -0.1,
..Default::default()
},
];
for s in cases {
let (level, score) = calculate_importance(&s);
assert_eq!(
level,
ImportanceLevel::from_score(score),
"level and score must agree (score={score})",
);
}
}
}