use std::path::Path;
const FD_GROWTH_TOLERANCE: f64 = 0.30;
pub fn count_open_fds() -> Option<u64> {
let fd_dir = if cfg!(target_os = "macos") {
Path::new("/dev/fd")
} else {
Path::new("/proc/self/fd")
};
let entries = std::fs::read_dir(fd_dir).ok()?;
let count = entries.filter_map(|e| e.ok()).count() as u64;
Some(count)
}
#[derive(Debug, Clone)]
pub struct FdWatchResult {
pub current: u64,
pub baseline: u64,
pub growth_detected: bool,
pub message: String,
}
pub fn check_fd_count(baseline: Option<u64>) -> FdWatchResult {
let current = count_open_fds().unwrap_or(0);
let baseline = baseline.unwrap_or(current);
let growth_ratio = if baseline > 0 {
(current as f64 - baseline as f64) / baseline as f64
} else {
if current > 10 { 1.0 } else { 0.0 }
};
let growth_detected = growth_ratio > FD_GROWTH_TOLERANCE;
let message = if growth_detected {
format!(
"FD growth detected: current={} baseline={} ratio={:.2} (>{:.0}%)",
current,
baseline,
growth_ratio,
FD_GROWTH_TOLERANCE * 100.0,
)
} else {
format!("FD count normal: current={current} baseline={baseline} ratio={growth_ratio:.2}",)
};
FdWatchResult {
current,
baseline,
growth_detected,
message,
}
}
#[cfg(test)]
mod tests {
use crate::health::fd_watch::{check_fd_count, count_open_fds};
#[test]
fn count_open_fds_returns_some() {
let count = count_open_fds();
assert!(count.is_some(), "FD counting should work on this platform");
assert!(count.unwrap() >= 3, "at least stdin/out/err");
}
#[test]
fn check_fd_no_baseline_returns_current() {
let result = check_fd_count(None);
assert!(result.current >= 3);
assert_eq!(result.baseline, result.current);
assert!(!result.growth_detected);
}
#[test]
fn check_fd_growth_detected() {
let result = check_fd_count(Some(1));
assert!(
result.growth_detected || result.current <= 1,
"baseline=1 should trigger growth: {:?}",
result.message,
);
}
#[test]
fn check_fd_no_growth() {
let current = count_open_fds().unwrap_or(100);
let result = check_fd_count(Some(current));
assert!(!result.growth_detected, "same baseline should match");
}
}