use crate::recorder::{SlotRecord, backtrace, collect_sorted};
use slotmap::Key;
use std::fmt::{self, Display, Write};
pub struct SlotDisplayFormatter<K: Key> {
pub label: Option<&'static str>,
pub key: K,
}
impl<K: Key> Display for SlotDisplayFormatter<K> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut debug = f.debug_tuple("slot");
debug.field(&self.key);
if let Some(label) = &self.label {
debug.field(label);
}
debug.finish()
}
}
pub fn new_handles<K: Key>(key: K, record: &SlotRecord, count: usize) -> String {
format!(
"{} with {count}/{} new handles",
SlotDisplayFormatter { key, label: record.label },
record.handles.len()
)
}
pub fn handles_leaked<K: Key>(key: K, record: &SlotRecord) -> String {
let mut out = String::default();
for (handle_id, backtrace) in collect_sorted(&record.handles) {
writeln!(
out,
"{} leaked handle: {handle_id:?}\n{:?}",
SlotDisplayFormatter { label: record.label, key },
backtrace::resolve_and_filter(backtrace.clone()),
)
.unwrap();
}
out
}
#[cfg(test)]
mod tests {
use super::*;
use crate::recorder::{
Backtrace,
tests::{Recorder, make_key, record_create, shutdown},
};
#[test]
fn handles() {
let mut recorder = Recorder::default();
let key = make_key(1);
recorder.record_create_handle(key, Some("foo"), Backtrace::new_unresolved());
record_create(&mut recorder, key);
let message = new_handles(key, &recorder.slots[&key], 1);
assert_eq!(message, r#"slot(DefaultKey(1v1), "foo") with 1/2 new handles"#);
shutdown(&mut recorder);
}
#[test]
fn leaked() {
let mut recorder = Recorder::default();
let key = make_key(1);
let handle_id1 = record_create(&mut recorder, key);
let handle_id2 = record_create(&mut recorder, key);
let message = handles_leaked(key, &recorder.slots[&key]);
assert_eq!(message.matches(" leaked handle: ").count(), 2);
let pos1 = message.find(&format!("{handle_id1:?}")).unwrap();
let pos2 = message.find(&format!("{handle_id2:?}")).unwrap();
assert!(pos1 < pos2);
shutdown(&mut recorder);
}
#[test]
fn debug() {
let fmt = SlotDisplayFormatter { key: make_key(1), label: None };
assert_eq!(format!("{fmt}"), "slot(DefaultKey(1v1))");
}
#[test]
fn debug_with_label() {
let fmt = SlotDisplayFormatter { key: make_key(1), label: Some("foo") };
assert_eq!(format!("{fmt}"), r#"slot(DefaultKey(1v1), "foo")"#);
}
}