naenae 0.1.0

Monitor external command output and send Discord webhook notifications.
use std::sync::OnceLock;
use std::time::Duration;

use regex::Regex;

pub fn render_command(command: &str, args: &[String]) -> String {
    if args.is_empty() {
        return command.to_string();
    }
    format!("{command} {}", args.join(" "))
}

pub fn truncate(value: &str, max_chars: usize) -> String {
    if value.chars().count() <= max_chars {
        return value.to_string();
    }
    value.chars().take(max_chars).collect::<String>() + "..."
}

pub fn format_duration(duration: Duration) -> String {
    let secs = duration.as_secs();
    let hours = secs / 3600;
    let minutes = (secs % 3600) / 60;
    let seconds = secs % 60;

    if hours > 0 {
        return format!("{hours}h {minutes}m {seconds}s");
    }
    if minutes > 0 {
        return format!("{minutes}m {seconds}s");
    }
    format!("{seconds}s")
}

pub fn strip_ansi(value: &str) -> String {
    static ANSI_RE: OnceLock<Regex> = OnceLock::new();
    ANSI_RE
        .get_or_init(|| Regex::new(r"\x1b\[[0-9;?]*[ -/]*[@-~]").expect("valid ANSI regex"))
        .replace_all(value, "")
        .into_owned()
}

pub fn discord_message(title: &str, job_name: &str, lines: &[String]) -> String {
    let mut rendered = format!("Nae^2 says | **{}** | job: `{}`", title, job_name);
    if let Some((first, rest)) = lines.split_first() {
        rendered.push('\n');
        rendered.push_str(first);
        for line in rest {
            rendered.push('\n');
            rendered.push_str(line);
        }
    }
    rendered
}

pub fn inline_fields(fields: &[(&str, String)]) -> String {
    fields
        .iter()
        .map(|(label, value)| format!("{label}: `{value}`"))
        .collect::<Vec<_>>()
        .join(" | ")
}