fix(core): tighten word boundary to reject digits and underscores in level detection (closes #28)

This commit is contained in:
dailz
2026-06-11 14:28:13 +08:00
parent 5cb56dafd8
commit 19a3b877f9

View File

@@ -100,13 +100,21 @@ fn detect_level_from_text(line: &str) -> Option<LogLevel> {
)
}
// ─── is_ident_char ─────────────────────────────────────────────────────────
/// Whether a byte looks like an ASCII identifier continuation character
/// (letter / digit / underscore). Log-level keywords must NOT be adjacent to
/// such characters to count as a valid word boundary.
fn is_ident_char(b: u8) -> bool {
b.is_ascii_alphanumeric() || b == b'_'
}
// ─── is_word_boundary ───────────────────────────────────────────────────────
/// Check that the match at `start..start+len` is surrounded by non-alphabetic
/// Check that the match at `start..start+len` is surrounded by non-identifier
/// characters (or the string edge).
fn is_word_boundary(text: &str, start: usize, len: usize) -> bool {
let before_ok = start == 0 || !text.as_bytes()[start - 1].is_ascii_alphabetic();
let before_ok = start == 0 || !is_ident_char(text.as_bytes()[start - 1]);
let after_idx = start + len;
let after_ok = after_idx >= text.len() || !text.as_bytes()[after_idx].is_ascii_alphabetic();
let after_ok = after_idx >= text.len() || !is_ident_char(text.as_bytes()[after_idx]);
before_ok && after_ok
}
@@ -209,4 +217,36 @@ mod tests {
let line = format!("{prefix} ERROR something");
assert_eq!(detect_level(&line), Some(LogLevel::Error));
}
#[test]
fn test_boundary_rejects_trailing_digits() {
assert_eq!(detect_level("ERROR123"), None);
assert_eq!(detect_level("WARN2: bad"), None);
assert_eq!(detect_level("ERR2"), None);
}
#[test]
fn test_boundary_rejects_underscore() {
assert_eq!(detect_level("INFO_foo"), None);
assert_eq!(detect_level("DBG_value=5"), None);
}
#[test]
fn test_boundary_rejects_leading_digits_and_underscore() {
assert_eq!(detect_level("123ERROR: fail"), None);
assert_eq!(detect_level("foo_ERROR: fail"), None);
assert_eq!(detect_level("1WRN"), None);
}
#[test]
fn test_boundary_accepts_valid_suffixes() {
assert_eq!(detect_level("ERROR: fail"), Some(LogLevel::Error));
assert_eq!(detect_level("[ERROR] fail"), Some(LogLevel::Error));
assert_eq!(detect_level("ERROR fail"), Some(LogLevel::Error));
}
#[test]
fn test_boundary_camel_case_regression() {
assert_eq!(detect_level("errorLevel"), None);
}
}