semora-cli 0.7.0

Semora CLI: publish Markdown and HTML, and hear back from the page
Documentation
//! Printing helpers: short text by default, JSON with `--json`.

use serde::Serialize;
use time::OffsetDateTime;

pub fn json<T: Serialize>(v: &T) {
    println!("{}", serde_json::to_string_pretty(v).expect("serializable"));
}

pub fn warn(msg: impl AsRef<str>) {
    eprintln!("warning: {}", msg.as_ref());
}

/// `2026-09-25 14:03` in UTC.
pub fn ts(t: OffsetDateTime) -> String {
    let fmt = time::macros::format_description!("[year]-[month]-[day] [hour]:[minute]");
    t.format(&fmt).unwrap_or_default()
}

pub fn size(bytes: u64) -> String {
    match bytes {
        b if b >= 1024 * 1024 => format!("{:.1} MB", b as f64 / (1024.0 * 1024.0)),
        b if b >= 1024 => format!("{:.1} KB", b as f64 / 1024.0),
        b => format!("{b} B"),
    }
}

/// A person's name for printing on one line: control characters removed, square brackets
/// turned into round ones (so a name can't end an event header like `[comment #12 from Ada]`
/// early), cut at 80 characters.
pub fn clean_name(name: &str) -> String {
    name.chars()
        .filter(|c| !c.is_control())
        .map(|c| match c {
            '[' => '(',
            ']' => ')',
            c => c,
        })
        .take(80)
        .collect::<String>()
        .trim()
        .to_string()
}