use std::sync::LazyLock;
use regex::Regex;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Classification {
pub bug_fix: bool,
pub security_fix: bool,
pub revert: bool,
}
static BUG_FIX: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(
r"(?i)\b(?:fix(?:es|ed|ing)?|bug(?:fix)?(?:es|s)?|defect|hotfix|regression|fault|crash)\b",
)
.expect("BUG_FIX pattern is valid")
});
static SECURITY_FIX: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(
r"(?i)\b(?:security|vulnerabilit(?:y|ies)|vuln|exploit|sanitiz(?:e|ation)|insecure|xss|csrf|rce|disclosure|malicious|hijack|spoof|(?:sql|command|code|html|ldap|os|xml)\s+injection|(?:buffer|heap)\s+overflow)\b|CVE-\d{4}-\d+|CWE-\d+",
)
.expect("SECURITY_FIX pattern is valid")
});
static REVERT_SUBJECT: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"(?i)^revert\b").expect("REVERT_SUBJECT pattern is valid"));
static ROLLBACK: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"(?i)^rollback\b").expect("ROLLBACK pattern is valid"));
#[must_use]
pub fn classify(message: &[u8]) -> Classification {
let text = String::from_utf8_lossy(message);
let subject = text.lines().next().unwrap_or("");
Classification {
bug_fix: BUG_FIX.is_match(&text),
security_fix: SECURITY_FIX.is_match(&text),
revert: REVERT_SUBJECT.is_match(subject) || ROLLBACK.is_match(subject),
}
}
#[cfg(test)]
#[path = "classify_tests.rs"]
mod tests;