pub fn log_rotation_stamp(filename: &str) -> Option<&str> {
const STAMP_LEN: usize = 19;
let rest = filename.strip_prefix("freeswitch.log.")?;
let candidate = rest.get(..STAMP_LEN)?;
candidate
.bytes()
.enumerate()
.all(|(i, b)| match i {
4 | 7 | 10 | 13 | 16 => b == b'-',
_ => b.is_ascii_digit(),
})
.then_some(candidate)
}
pub fn normalize_entry_timestamp(ts: &str) -> String {
const TS_LEN: usize = 19;
if let (Some(date), Some(time)) = (ts.get(..10), ts.get(11..TS_LEN)) {
return format!("{date}-{}", time.replace(':', "-"));
}
let mut s = ts.replace(['T', ':', ' '], "-");
while s.ends_with('-') {
s.pop();
}
s
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn stamp_from_rotated_name() {
assert_eq!(
log_rotation_stamp("freeswitch.log.2026-03-08-16-52-07.1.xz"),
Some("2026-03-08-16-52-07"),
);
assert_eq!(
log_rotation_stamp("freeswitch.log.2026-03-08-16-52-07"),
Some("2026-03-08-16-52-07"),
);
}
#[test]
fn no_stamp_on_active_log_or_foreign_names() {
assert_eq!(log_rotation_stamp("freeswitch.log"), None);
assert_eq!(log_rotation_stamp("freeswitch.log.1.xz"), None);
assert_eq!(log_rotation_stamp("freeswitch.log.not-a-date-here!!"), None);
assert_eq!(log_rotation_stamp("other.log.2026-03-08-16-52-07"), None);
}
#[test]
fn stamp_rejects_multibyte_boundary() {
assert_eq!(
log_rotation_stamp("freeswitch.log.2026-03-08-16-52-é"),
None
);
}
#[test]
fn entry_timestamp_normalized() {
assert_eq!(
normalize_entry_timestamp("2026-03-08 16:52:07.123456"),
"2026-03-08-16-52-07",
);
assert_eq!(
normalize_entry_timestamp("2026-03-08 16:52:07"),
"2026-03-08-16-52-07",
);
}
#[test]
fn short_entry_timestamp_still_normalizes() {
assert_eq!(normalize_entry_timestamp("2026-03-08"), "2026-03-08");
assert_eq!(normalize_entry_timestamp("2026-03-08 "), "2026-03-08");
assert_eq!(normalize_entry_timestamp(""), "");
}
#[test]
fn multibyte_entry_timestamp_does_not_panic() {
assert_eq!(
normalize_entry_timestamp("2026-03-08é16:52:07"),
"2026-03-08é16-52-07"
);
assert_eq!(
normalize_entry_timestamp("2026-03-0é 16:52:07"),
"2026-03-0é-16-52-07"
);
assert_eq!(normalize_entry_timestamp("ééééééééé"), "ééééééééé");
}
#[test]
fn normalized_forms_compare_lexicographically() {
let entry = normalize_entry_timestamp("2026-03-08 16:52:07.123456");
let earlier = log_rotation_stamp("freeswitch.log.2026-03-08-00-00-00.1.xz").unwrap();
let later = log_rotation_stamp("freeswitch.log.2026-03-09-00-00-00.1.xz").unwrap();
assert!(entry.as_str() > earlier);
assert!(entry.as_str() < later);
}
}