freeswitch_log_parser/
stamp.rs1pub fn log_rotation_stamp(filename: &str) -> Option<&str> {
10 const STAMP_LEN: usize = 19;
11 let rest = filename.strip_prefix("freeswitch.log.")?;
12 let candidate = rest.get(..STAMP_LEN)?;
13 candidate
14 .bytes()
15 .enumerate()
16 .all(|(i, b)| match i {
17 4 | 7 | 10 | 13 | 16 => b == b'-',
18 _ => b.is_ascii_digit(),
19 })
20 .then_some(candidate)
21}
22
23pub fn normalize_entry_timestamp(ts: &str) -> String {
29 const TS_LEN: usize = 19;
30 if let (Some(date), Some(time)) = (ts.get(..10), ts.get(11..TS_LEN)) {
33 return format!("{date}-{}", time.replace(':', "-"));
34 }
35 let mut s = ts.replace(['T', ':', ' '], "-");
36 while s.ends_with('-') {
37 s.pop();
38 }
39 s
40}
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45
46 #[test]
47 fn stamp_from_rotated_name() {
48 assert_eq!(
49 log_rotation_stamp("freeswitch.log.2026-03-08-16-52-07.1.xz"),
50 Some("2026-03-08-16-52-07"),
51 );
52 assert_eq!(
53 log_rotation_stamp("freeswitch.log.2026-03-08-16-52-07"),
54 Some("2026-03-08-16-52-07"),
55 );
56 }
57
58 #[test]
59 fn no_stamp_on_active_log_or_foreign_names() {
60 assert_eq!(log_rotation_stamp("freeswitch.log"), None);
61 assert_eq!(log_rotation_stamp("freeswitch.log.1.xz"), None);
62 assert_eq!(log_rotation_stamp("freeswitch.log.not-a-date-here!!"), None);
63 assert_eq!(log_rotation_stamp("other.log.2026-03-08-16-52-07"), None);
64 }
65
66 #[test]
67 fn stamp_rejects_multibyte_boundary() {
68 assert_eq!(
70 log_rotation_stamp("freeswitch.log.2026-03-08-16-52-é"),
71 None
72 );
73 }
74
75 #[test]
76 fn entry_timestamp_normalized() {
77 assert_eq!(
78 normalize_entry_timestamp("2026-03-08 16:52:07.123456"),
79 "2026-03-08-16-52-07",
80 );
81 assert_eq!(
82 normalize_entry_timestamp("2026-03-08 16:52:07"),
83 "2026-03-08-16-52-07",
84 );
85 }
86
87 #[test]
88 fn short_entry_timestamp_still_normalizes() {
89 assert_eq!(normalize_entry_timestamp("2026-03-08"), "2026-03-08");
90 assert_eq!(normalize_entry_timestamp("2026-03-08 "), "2026-03-08");
91 assert_eq!(normalize_entry_timestamp(""), "");
92 }
93
94 #[test]
95 fn multibyte_entry_timestamp_does_not_panic() {
96 assert_eq!(
98 normalize_entry_timestamp("2026-03-08é16:52:07"),
99 "2026-03-08é16-52-07"
100 );
101 assert_eq!(
102 normalize_entry_timestamp("2026-03-0é 16:52:07"),
103 "2026-03-0é-16-52-07"
104 );
105 assert_eq!(normalize_entry_timestamp("ééééééééé"), "ééééééééé");
106 }
107
108 #[test]
109 fn normalized_forms_compare_lexicographically() {
110 let entry = normalize_entry_timestamp("2026-03-08 16:52:07.123456");
111 let earlier = log_rotation_stamp("freeswitch.log.2026-03-08-00-00-00.1.xz").unwrap();
112 let later = log_rotation_stamp("freeswitch.log.2026-03-09-00-00-00.1.xz").unwrap();
113 assert!(entry.as_str() > earlier);
114 assert!(entry.as_str() < later);
115 }
116}