rok-cli 0.6.1

Developer CLI for rok-based Axum applications
//! `rok profile` — CPU/memory profiling integration (M7.11).

pub fn run(target: Option<&str>, output: Option<&str>) -> anyhow::Result<()> {
    let out = output.unwrap_or("profile_output");

    println!("rok profile — profiling integration");
    println!("{}", "".repeat(50));

    // Detect available profilers
    let flamegraph = has_tool("flamegraph");
    let perf = has_tool("perf");
    let instruments = has_tool("instruments");
    let heaptrack = has_tool("heaptrack");
    let valgrind = has_tool("valgrind");

    if !flamegraph && !perf && !instruments && !heaptrack && !valgrind {
        println!("  No profiler found.  Install one of:");
        println!("  CPU profiling:");
        println!("    cargo flamegraph  cargo install flamegraph");
        println!("    perf              (Linux, via package manager)");
        println!("    Instruments       (macOS, via Xcode)");
        println!();
        println!("  Memory profiling:");
        println!("    heaptrack         (Linux, via package manager)");
        println!("    valgrind          (Linux/macOS, via package manager)");
        return Ok(());
    }

    println!("  Available profilers:");
    if flamegraph { println!("    ✓ cargo-flamegraph  (CPU)"); }
    if perf       { println!("    ✓ perf              (CPU, Linux)"); }
    if instruments{ println!("    ✓ Instruments       (CPU/memory, macOS)"); }
    if heaptrack  { println!("    ✓ heaptrack         (memory, Linux)"); }
    if valgrind   { println!("    ✓ valgrind/massif   (memory)"); }
    println!();

    if let Some(t) = target {
        if flamegraph {
            println!("  Running: cargo flamegraph --bin {t} -o {out}.svg");
            let status = std::process::Command::new("cargo")
                .args(["flamegraph", "--bin", t, "-o", &format!("{out}.svg")])
                .status()?;
            if status.success() {
                println!("  ✓ flamegraph saved to {out}.svg");
            } else {
                println!("  ✗ flamegraph failed (exit {status})");
            }
        } else {
            println!("  Hint: cargo install flamegraph  then  rok profile --target {t}");
        }
    } else {
        println!("  Usage: rok profile --target <binary-name> [--output <file>]");
        println!("  Example: rok profile --target axum_app");
    }

    Ok(())
}

fn has_tool(name: &str) -> bool {
    let cmd = if name == "flamegraph" {
        std::process::Command::new("cargo").args(["flamegraph", "--help"]).output()
    } else {
        std::process::Command::new(name).arg("--version").output()
    };
    cmd.map(|o| o.status.success() || !o.stderr.is_empty()).unwrap_or(false)
}