use serde::Serialize;
use kindling_types::Timestamp;
pub fn format_json<T: Serialize>(value: &T, pretty: bool) -> serde_json::Result<String> {
if pretty {
serde_json::to_string_pretty(value)
} else {
serde_json::to_string(value)
}
}
pub fn format_timestamp(ts: Timestamp) -> String {
let iso = iso8601_utc(ts);
let trimmed = iso.split('.').next().unwrap_or(&iso);
trimmed.replacen('T', " ", 1)
}
pub fn iso8601_utc(ts: Timestamp) -> String {
let total_ms = ts;
let ms = total_ms.rem_euclid(1000);
let total_secs = total_ms.div_euclid(1000);
let secs_of_day = total_secs.rem_euclid(86_400);
let days = total_secs.div_euclid(86_400);
let (year, month, day) = civil_from_days(days);
let hour = secs_of_day / 3600;
let minute = (secs_of_day % 3600) / 60;
let second = secs_of_day % 60;
format!("{year:04}-{month:02}-{day:02}T{hour:02}:{minute:02}:{second:02}.{ms:03}Z")
}
fn civil_from_days(z: i64) -> (i64, u32, u32) {
let z = z + 719_468;
let era = if z >= 0 { z } else { z - 146_096 } / 146_097;
let doe = z - era * 146_097; let yoe = (doe - doe / 1460 + doe / 36_524 - doe / 146_096) / 365; let y = yoe + era * 400;
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100); let mp = (5 * doy + 2) / 153; let d = (doy - (153 * mp + 2) / 5 + 1) as u32; let m = if mp < 10 { mp + 3 } else { mp - 9 } as u32; let year = if m <= 2 { y + 1 } else { y };
(year, m, d)
}
pub fn truncate(text: &str, max_length: usize) -> String {
let char_count = text.chars().count();
if char_count <= max_length {
return text.to_string();
}
let keep = max_length.saturating_sub(3);
let prefix: String = text.chars().take(keep).collect();
format!("{prefix}...")
}
pub fn print_error(message: &str, as_json: bool) {
if as_json {
let value = serde_json::json!({ "error": message });
eprintln!("{}", serde_json::to_string(&value).unwrap_or_default());
} else {
eprintln!("Error: {message}");
}
}