use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct VizCommand {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tab: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub workspace: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub palette: Option<String>,
}
pub fn cmd_path() -> String {
std::env::var("NORNIR_VIZ_CMD").unwrap_or_else(|_| "/tmp/nornir_viz_cmd.json".to_string())
}
pub fn take_pending() -> Option<Result<VizCommand, String>> {
let path = cmd_path();
let raw = std::fs::read_to_string(&path).ok()?;
let _ = std::fs::remove_file(&path);
if raw.trim().is_empty() {
return None;
}
Some(serde_json::from_str::<VizCommand>(&raw).map_err(|e| format!("bad viz command: {e}")))
}
pub fn write_command(cmd: &VizCommand) -> std::io::Result<()> {
let path = cmd_path();
let s = serde_json::to_string_pretty(cmd).unwrap_or_else(|_| "{}".to_string());
std::fs::write(path, s)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn roundtrip_write_take_consumes_once() {
let tmp = format!("/tmp/nornir_viz_cmd_test_{}.json", std::process::id());
std::env::set_var("NORNIR_VIZ_CMD", &tmp);
let _ = std::fs::remove_file(&tmp);
assert!(take_pending().is_none());
write_command(&VizCommand {
tab: Some("Test".into()),
workspace: Some("korp".into()),
palette: Some("cyberpunk-neon".into()),
})
.unwrap();
let got = take_pending().expect("a command is pending").expect("parses");
assert_eq!(got.tab.as_deref(), Some("Test"));
assert_eq!(got.workspace.as_deref(), Some("korp"));
assert_eq!(got.palette.as_deref(), Some("cyberpunk-neon"));
assert!(take_pending().is_none());
std::fs::write(&tmp, "{not json").unwrap();
let err = take_pending().expect("present").expect_err("malformed");
assert!(err.contains("bad viz command"), "got: {err}");
assert!(take_pending().is_none());
let _ = std::fs::remove_file(&tmp);
}
}