robotrt-cli 0.1.0-beta.1

RobotRT modular robotics runtime and middleware components.
pub(super) use std::fs;
pub(super) use std::io;
pub(super) use std::path::PathBuf;
pub(super) use std::process::Command;
pub(super) use std::process::Stdio;
pub(super) use std::time::Duration;

pub(super) use introspection_core::{
    RuntimeHealthView, RuntimeLoadReport, RuntimeMetricView, write_runtime_load_report,
};
pub(super) use serde_json::Value;

pub(super) struct CliOutput {
    pub status: std::process::ExitStatus,
    pub stdout: String,
    pub stderr: String,
}

pub(super) fn run_cli(args: &[&str]) -> CliOutput {
    let output = Command::new(env!("CARGO_BIN_EXE_robotrt-cli"))
        .args(args)
        .output()
        .expect("run robotrt-cli command");

    CliOutput {
        status: output.status,
        stdout: String::from_utf8_lossy(&output.stdout).to_string(),
        stderr: String::from_utf8_lossy(&output.stderr).to_string(),
    }
}

pub(super) fn temp_path(prefix: &str, ext: &str) -> PathBuf {
    let mut path = std::env::temp_dir();
    let nanos = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .expect("unix epoch")
        .as_nanos();
    path.push(format!("robotrt-cli-{prefix}-{nanos}.{ext}"));
    path
}

pub(super) fn temp_dir(prefix: &str) -> io::Result<PathBuf> {
    let mut path = std::env::temp_dir();
    let nanos = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .expect("unix epoch")
        .as_nanos();
    path.push(format!("robotrt-cli-{prefix}-{nanos}"));
    fs::create_dir_all(&path)?;
    Ok(path)
}

pub(super) fn allocate_udp_endpoint() -> String {
    let socket = std::net::UdpSocket::bind("127.0.0.1:0").expect("bind loopback udp port");
    let addr = socket.local_addr().expect("read local addr");
    format!("127.0.0.1:{}", addr.port())
}

pub(super) fn assert_exact_object_keys(value: &Value, expected_keys: &[&str]) {
    let obj = value.as_object().expect("json object expected");
    assert_eq!(
        obj.len(),
        expected_keys.len(),
        "unexpected key count: {obj:?}"
    );
    for key in expected_keys {
        assert!(obj.contains_key(*key), "missing expected key: {key}");
    }
}