use std::time::Duration as StdDuration;
use time::format_description::well_known::Rfc3339;
use time::OffsetDateTime;
pub(crate) fn format_timestamp_rfc3339(value: OffsetDateTime) -> String {
value.format(&Rfc3339).unwrap_or_else(|_| value.to_string())
}
pub(crate) fn format_unix_ms_rfc3339(ms: i64) -> String {
let seconds = ms.div_euclid(1000);
let value = OffsetDateTime::from_unix_timestamp(seconds).unwrap_or(OffsetDateTime::UNIX_EPOCH);
format_timestamp_rfc3339(value)
}
pub(crate) fn format_duration_coarse(value: StdDuration) -> String {
if value.as_secs() == 0 {
return format!("{}ms", value.as_millis());
}
let seconds = value.as_secs();
if seconds < 60 {
return format!("{seconds}s");
}
if seconds < 60 * 60 {
return format!("{}m", seconds / 60);
}
if seconds < 60 * 60 * 24 {
return format!("{}h", seconds / (60 * 60));
}
if seconds < 60 * 60 * 24 * 7 {
return format!("{}d", seconds / (60 * 60 * 24));
}
if seconds.is_multiple_of(60 * 60 * 24 * 7) {
return format!("{}w", seconds / (60 * 60 * 24 * 7));
}
format!("{}d", seconds / (60 * 60 * 24))
}
pub(crate) fn format_duration_ms(duration_ms: u64) -> String {
if duration_ms >= 60_000 {
format!("{:.1}m", duration_ms as f64 / 60_000.0)
} else if duration_ms >= 1_000 {
format!("{:.1}s", duration_ms as f64 / 1_000.0)
} else {
format!("{duration_ms}ms")
}
}
pub(crate) fn escape_md(value: &str) -> String {
value.replace('|', "\\|")
}
pub(crate) fn escape_html(value: &str) -> String {
let mut out = String::with_capacity(value.len());
for ch in value.chars() {
match ch {
'&' => out.push_str("&"),
'<' => out.push_str("<"),
'>' => out.push_str(">"),
'"' => out.push_str("""),
'\'' => out.push_str("'"),
_ => out.push(ch),
}
}
out
}
pub(crate) fn escape_toml_basic_string(value: &str) -> String {
let mut out = String::with_capacity(value.len());
for ch in value.chars() {
match ch {
'"' => out.push_str("\\\""),
'\\' => out.push_str("\\\\"),
'\n' => out.push_str("\\n"),
'\t' => out.push_str("\\t"),
'\r' => out.push_str("\\r"),
c if (c as u32) < 0x20 || c == '\u{7f}' => {
out.push_str(&format!("\\u{:04X}", c as u32));
}
_ => out.push(ch),
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn timestamp_uses_rfc3339() {
let value = OffsetDateTime::UNIX_EPOCH;
assert_eq!(format_timestamp_rfc3339(value), "1970-01-01T00:00:00Z");
}
#[test]
fn escape_html_handles_all_five_specials() {
assert_eq!(
escape_html(r#"<a href="x">&'b'</a>"#),
"<a href="x">&'b'</a>"
);
}
#[test]
fn escape_toml_basic_string_escapes_control_characters() {
assert_eq!(
escape_toml_basic_string("a\"b\\c\nd\te\rf"),
"a\\\"b\\\\c\\nd\\te\\rf"
);
assert_eq!(escape_toml_basic_string("bell\u{7}"), "bell\\u0007");
}
#[test]
fn unix_ms_rounds_to_seconds() {
assert_eq!(format_unix_ms_rfc3339(0), "1970-01-01T00:00:00Z");
assert_eq!(format_unix_ms_rfc3339(1500), "1970-01-01T00:00:01Z");
assert_eq!(format_unix_ms_rfc3339(-1), "1969-12-31T23:59:59Z");
}
#[test]
fn coarse_duration_picks_a_unit() {
assert_eq!(format_duration_coarse(StdDuration::from_millis(0)), "0ms");
assert_eq!(format_duration_coarse(StdDuration::from_secs(5)), "5s");
assert_eq!(format_duration_coarse(StdDuration::from_mins(2)), "2m");
assert_eq!(format_duration_coarse(StdDuration::from_hours(2)), "2h");
assert_eq!(format_duration_coarse(StdDuration::from_hours(72)), "3d");
assert_eq!(format_duration_coarse(StdDuration::from_hours(336)), "2w");
}
#[test]
fn ms_duration_uses_one_decimal_above_a_second() {
assert_eq!(format_duration_ms(500), "500ms");
assert_eq!(format_duration_ms(1_500), "1.5s");
assert_eq!(format_duration_ms(90_000), "1.5m");
}
#[test]
fn escape_md_escapes_only_pipes() {
assert_eq!(escape_md("a|b|c"), "a\\|b\\|c");
assert_eq!(escape_md("no pipes here"), "no pipes here");
assert_eq!(escape_md(""), "");
}
}