use std::collections::HashSet;
use std::path::Path;
use std::sync::Mutex;
#[derive(Debug, Default)]
pub struct OperationContext {
warned_files: Mutex<HashSet<String>>,
}
impl OperationContext {
#[must_use]
pub fn new() -> Self {
Self::default()
}
fn normalize_key(path: &Path) -> String {
if let Some(filename) = path.file_name().and_then(|n| n.to_str()) {
filename.to_string()
} else {
path.to_string_lossy().to_string()
}
}
pub fn should_warn_file(&self, path: &Path) -> bool {
let normalized_key = Self::normalize_key(path);
let mut warned = self.warned_files.lock().unwrap();
warned.insert(normalized_key)
}
#[cfg(test)]
pub fn has_warned(&self, path: &Path) -> bool {
let normalized_key = Self::normalize_key(path);
self.warned_files.lock().unwrap().contains(&normalized_key)
}
#[must_use]
pub fn warning_count(&self) -> usize {
self.warned_files.lock().unwrap().len()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
#[test]
fn test_new_context_is_empty() {
let ctx = OperationContext::new();
assert_eq!(ctx.warning_count(), 0);
}
#[test]
fn test_should_warn_first_time() {
let ctx = OperationContext::new();
let path = PathBuf::from("test.md");
assert!(ctx.should_warn_file(&path));
assert!(!ctx.should_warn_file(&path));
}
#[test]
fn test_same_filename_different_paths() {
let ctx = OperationContext::new();
let path1 = PathBuf::from("/foo/bar/test.md");
let path2 = PathBuf::from("/baz/qux/test.md");
assert!(ctx.should_warn_file(&path1));
assert!(!ctx.should_warn_file(&path2));
}
#[test]
fn test_different_filenames() {
let ctx = OperationContext::new();
let path1 = PathBuf::from("file1.md");
let path2 = PathBuf::from("file2.md");
assert!(ctx.should_warn_file(&path1));
assert!(ctx.should_warn_file(&path2));
assert_eq!(ctx.warning_count(), 2);
}
#[test]
fn test_has_warned() {
let ctx = OperationContext::new();
let path = PathBuf::from("test.md");
assert!(!ctx.has_warned(&path));
ctx.should_warn_file(&path);
assert!(ctx.has_warned(&path));
}
#[test]
fn test_warning_count() {
let ctx = OperationContext::new();
assert_eq!(ctx.warning_count(), 0);
ctx.should_warn_file(&PathBuf::from("file1.md"));
assert_eq!(ctx.warning_count(), 1);
ctx.should_warn_file(&PathBuf::from("file2.md"));
assert_eq!(ctx.warning_count(), 2);
ctx.should_warn_file(&PathBuf::from("file1.md"));
assert_eq!(ctx.warning_count(), 2);
}
#[test]
fn test_multiple_contexts_are_isolated() {
let ctx1 = OperationContext::new();
let ctx2 = OperationContext::new();
let path = PathBuf::from("test.md");
assert!(ctx1.should_warn_file(&path));
assert!(ctx2.should_warn_file(&path));
assert!(!ctx1.should_warn_file(&path));
assert!(!ctx2.should_warn_file(&path));
}
#[test]
fn test_path_without_filename() {
let ctx = OperationContext::new();
let path = PathBuf::from("/");
assert!(ctx.should_warn_file(&path));
assert!(!ctx.should_warn_file(&path));
}
}