use super::HolderInfo;
const CMDLINE_MAX_CHARS: usize = 100;
pub(crate) const NO_HOLDERS_RECORDED: &str = "<none recorded>";
pub(super) fn holder_info_for_pid(pid: u32) -> HolderInfo {
let raw = match std::fs::read(format!("/proc/{pid}/cmdline")) {
Ok(bytes) => bytes,
Err(_) => {
return HolderInfo {
pid,
cmdline: "<cmdline unavailable>".to_string(),
};
}
};
let text: String = String::from_utf8_lossy(&raw)
.chars()
.map(|c| if c == '\0' { ' ' } else { c })
.collect::<String>()
.trim_end()
.to_string();
let truncated = if text.chars().count() > CMDLINE_MAX_CHARS {
let head: String = text.chars().take(CMDLINE_MAX_CHARS).collect();
format!("{head}…")
} else if text.is_empty() {
"<cmdline unavailable>".to_string()
} else {
text
};
HolderInfo {
pid,
cmdline: truncated,
}
}
pub fn format_holder_list(holders: &[HolderInfo]) -> String {
if holders.is_empty() {
NO_HOLDERS_RECORDED.to_string()
} else {
holders
.iter()
.map(|h| format!(" pid={} cmd={}", h.pid, h.cmdline))
.collect::<Vec<_>>()
.join("\n")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn format_holder_list_empty_yields_sentinel() {
assert_eq!(format_holder_list(&[]), NO_HOLDERS_RECORDED);
}
#[test]
fn format_holder_list_single_holder() {
let holders = [HolderInfo {
pid: 12345,
cmdline: "cargo build".to_string(),
}];
assert_eq!(format_holder_list(&holders), " pid=12345 cmd=cargo build");
}
#[test]
fn format_holder_list_multiple_newline_separated() {
let holders = [
HolderInfo {
pid: 1,
cmdline: "a".to_string(),
},
HolderInfo {
pid: 2,
cmdline: "b".to_string(),
},
];
let out = format_holder_list(&holders);
assert!(out.contains("\n"), "must contain newline: {out}");
assert!(!out.contains(", "), "must NOT contain comma-space: {out}");
assert_eq!(out, " pid=1 cmd=a\n pid=2 cmd=b");
}
#[test]
fn holder_info_json_keys_are_snake_case() {
let holder = HolderInfo {
pid: 123,
cmdline: "bash".to_string(),
};
let val = serde_json::to_value(&holder).expect("serialize");
assert_eq!(val["pid"], serde_json::json!(123));
assert_eq!(val["cmdline"], serde_json::json!("bash"));
assert!(
val.get("cmdLine").is_none(),
"camelCase cmdLine must not appear: {val}",
);
}
}