nomad-cli 0.1.1

Nomad CLI - extract structured web content
Documentation
use serde::Deserialize;

#[derive(Deserialize)]
pub struct Envelope {
    pub success: bool,
    pub data: serde_json::Value,
    pub error: Option<String>,
    pub meta: Option<Meta>,
}

#[derive(Deserialize)]
pub struct Meta {
    pub duration_ms: Option<f64>,
    pub daemon_rss_kb: Option<u64>,
    pub daemon_uptime_secs: Option<u64>,
    pub daemon_connections: Option<u32>,
}

pub fn daemon_alive(port: u16) -> bool {
    let url = format!("http://127.0.0.1:{}/api/health", port);
    reqwest::blocking::get(&url).is_ok()
}

pub fn health(port: u16, json_output: bool) -> Result<(), Box<dyn std::error::Error>> {
    let url = format!("http://127.0.0.1:{}/api/health", port);
    match reqwest::blocking::get(&url) {
        Ok(resp) => {
            if let Ok(e) = resp.json::<Envelope>() {
                if e.success {
                    let d = &e.data;
                    let pid = d.get("pid").and_then(|v| v.as_u64()).unwrap_or(0);
                    let rss = d.get("rss_kb").and_then(|v| v.as_u64()).unwrap_or(0);
                    let uptime = d.get("uptime_secs").and_then(|v| v.as_u64()).unwrap_or(0);
                    let conns = d.get("connections").and_then(|v| v.as_u64()).unwrap_or(0);
                    let ver = d.get("version").and_then(|v| v.as_str()).unwrap_or("?");
                    if json_output {
                        println!("{}", serde_json::to_string_pretty(&e.data)?);
                    } else {
                        let rss_mb = rss as f64 / 1024.0;
                        let (uptime_str, _) = format_duration(uptime);
                        println!(" ● nomad-daemon: RUNNING");
                        println!("   PID:    {pid}");
                        println!("   Port:   {port}");
                        println!("   RSS:    {rss_mb:.1} MB");
                        println!("   Uptime: {uptime_str}");
                        println!("   Conns:  {conns}");
                        println!("   Ver:    {ver}");
                    }
                } else {
                    let err = e.error.unwrap_or_else(|| "unknown".into());
                    if json_output {
                        println!(r#"{{"running":false,"error":"{err}"}}"#);
                    } else {
                        println!(" ● nomad-daemon: ERROR  {err}");
                    }
                }
            }
        }
        Err(_) => {
            if json_output {
                println!(r#"{{"running":false}}"#);
            } else {
                println!(" ● nomad-daemon: STOPPED. Start with: nom daemon start");
            }
        }
    }
    Ok(())
}

fn format_duration(secs: u64) -> (String, u64) {
    let h = secs / 3600;
    let m = (secs % 3600) / 60;
    let s = secs % 60;
    if h > 0 {
        (format!("{h}h {m}m"), secs)
    } else if m > 0 {
        (format!("{m}m {s}s"), secs)
    } else {
        (format!("{s}s"), secs)
    }
}