use crate::message::FALLBACK_REFRESH_SECS;
pub fn is_wsl() -> bool {
#[cfg(target_os = "linux")]
{
std::fs::read_to_string("/proc/version")
.map(|v| v.to_lowercase().contains("microsoft"))
.unwrap_or(false)
}
#[cfg(not(target_os = "linux"))]
{
false
}
}
#[cfg(target_os = "linux")]
pub fn parse_inotify_value(s: &str) -> Option<usize> {
s.trim().parse().ok()
}
#[cfg(target_os = "linux")]
fn read_inotify_max_watches() -> Option<usize> {
let content = std::fs::read_to_string("/proc/sys/fs/inotify/max_user_watches").ok()?;
parse_inotify_value(&content)
}
#[cfg(target_os = "linux")]
pub fn get_watch_limit() -> Option<usize> {
if is_wsl() {
return None; }
let max = read_inotify_max_watches().unwrap_or(8192);
Some(max / 2)
}
#[cfg(not(target_os = "linux"))]
pub fn get_watch_limit() -> Option<usize> {
None
}
#[derive(Debug, Clone, Default)]
pub struct WatcherMetrics {
pub directory_count: usize,
pub skipped_count: usize,
}
pub fn check_watch_warning(metrics: &WatcherMetrics, limit: Option<usize>) -> Option<String> {
let limit = limit?;
if metrics.skipped_count > 0 || metrics.directory_count > limit {
Some(format!(
"Large repo: refreshing every {}s",
FALLBACK_REFRESH_SECS
))
} else {
None
}
}
#[derive(Debug, Clone)]
pub struct DiffThresholds {
pub warn_line_count: usize,
pub warn_file_count: usize,
}
impl Default for DiffThresholds {
fn default() -> Self {
Self {
warn_line_count: 50_000,
warn_file_count: 500,
}
}
}
impl DiffThresholds {
pub fn check_diff_warning(&self, metrics: &DiffMetrics) -> Option<String> {
if metrics.total_lines > self.warn_line_count {
let k = metrics.total_lines / 1000;
Some(format!("Large diff: {}k lines", k))
} else if metrics.file_count > self.warn_file_count {
Some(format!("Many files: {}", metrics.file_count))
} else {
None
}
}
}
#[derive(Debug, Clone, Default)]
pub struct DiffMetrics {
pub total_lines: usize,
pub file_count: usize,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(target_os = "linux")]
fn test_parse_inotify_value_valid() {
assert_eq!(parse_inotify_value("524288\n"), Some(524288));
assert_eq!(parse_inotify_value("8192"), Some(8192));
assert_eq!(parse_inotify_value(" 1024 "), Some(1024));
}
#[test]
#[cfg(target_os = "linux")]
fn test_parse_inotify_value_invalid() {
assert_eq!(parse_inotify_value("invalid"), None);
assert_eq!(parse_inotify_value(""), None);
assert_eq!(parse_inotify_value("12abc"), None);
}
#[test]
#[cfg(not(target_os = "linux"))]
fn test_get_watch_limit_non_linux_returns_none() {
assert!(get_watch_limit().is_none());
}
#[test]
#[cfg(not(target_os = "linux"))]
fn test_is_wsl_returns_false_on_non_linux() {
assert!(!is_wsl());
}
#[test]
fn test_check_watch_warning_no_limit_returns_none() {
let metrics = WatcherMetrics {
directory_count: 10000,
skipped_count: 5000,
};
assert!(check_watch_warning(&metrics, None).is_none());
}
#[test]
fn test_check_watch_warning_below_limit() {
let metrics = WatcherMetrics {
directory_count: 50,
skipped_count: 0,
};
assert!(check_watch_warning(&metrics, Some(100)).is_none());
}
#[test]
fn test_check_watch_warning_above_limit() {
let metrics = WatcherMetrics {
directory_count: 200,
skipped_count: 0,
};
let warning = check_watch_warning(&metrics, Some(100));
assert!(warning.is_some());
assert!(warning.unwrap().contains("refreshing every"));
}
#[test]
fn test_check_watch_warning_with_skipped() {
let metrics = WatcherMetrics {
directory_count: 50,
skipped_count: 10,
};
let warning = check_watch_warning(&metrics, Some(100));
assert!(warning.is_some());
}
#[test]
fn test_diff_thresholds_default() {
let thresholds = DiffThresholds::default();
assert_eq!(thresholds.warn_line_count, 50_000);
assert_eq!(thresholds.warn_file_count, 500);
}
#[test]
fn test_diff_warning_small_diff() {
let thresholds = DiffThresholds::default();
let metrics = DiffMetrics {
total_lines: 100,
file_count: 5,
};
assert!(thresholds.check_diff_warning(&metrics).is_none());
}
#[test]
fn test_diff_warning_large_line_count() {
let thresholds = DiffThresholds::default();
let metrics = DiffMetrics {
total_lines: 60_000,
file_count: 10,
};
let warning = thresholds.check_diff_warning(&metrics);
assert!(warning.is_some());
let msg = warning.unwrap();
assert!(msg.contains("60k"));
assert!(msg.contains("lines"));
}
#[test]
fn test_diff_warning_many_files() {
let thresholds = DiffThresholds::default();
let metrics = DiffMetrics {
total_lines: 1000,
file_count: 600,
};
let warning = thresholds.check_diff_warning(&metrics);
assert!(warning.is_some());
let msg = warning.unwrap();
assert!(msg.contains("600"));
assert!(msg.contains("files"));
}
#[test]
fn test_diff_warning_line_count_takes_precedence() {
let thresholds = DiffThresholds::default();
let metrics = DiffMetrics {
total_lines: 60_000,
file_count: 600,
};
let warning = thresholds.check_diff_warning(&metrics);
assert!(warning.is_some());
assert!(warning.unwrap().contains("lines"));
}
}