use hashbrown::HashMap;
use noxu_sync::Mutex;
#[derive(Debug)]
pub struct FileProtector {
protected_files: Mutex<HashMap<u32, ProtectionInfo>>,
}
#[derive(Debug, Clone)]
pub struct ProtectionInfo {
pub count: u32,
pub reason: String,
}
impl FileProtector {
pub fn new() -> Self {
Self { protected_files: Mutex::new(HashMap::new()) }
}
pub fn protect_file(&self, file_number: u32, reason: &str) {
let mut protected = self.protected_files.lock();
protected
.entry(file_number)
.and_modify(|info| {
info.count += 1;
if !info.reason.contains(reason) {
info.reason = format!("{}, {}", info.reason, reason);
}
})
.or_insert_with(|| ProtectionInfo {
count: 1,
reason: reason.to_string(),
});
}
pub fn unprotect_file(&self, file_number: u32) -> bool {
let mut protected = self.protected_files.lock();
if let Some(info) = protected.get_mut(&file_number) {
if info.count > 1 {
info.count -= 1;
return false;
} else {
protected.remove(&file_number);
return true;
}
}
true
}
pub fn is_protected(&self, file_number: u32) -> bool {
self.protected_files.lock().contains_key(&file_number)
}
pub fn get_protected_files(&self) -> Vec<(u32, String)> {
let protected = self.protected_files.lock();
protected
.iter()
.map(|(file, info)| (*file, info.reason.clone()))
.collect()
}
pub fn get_protected_size(&self) -> usize {
self.protected_files.lock().len()
}
pub fn get_protection_count(&self, file_number: u32) -> u32 {
self.protected_files
.lock()
.get(&file_number)
.map(|info| info.count)
.unwrap_or(0)
}
pub fn clear(&self) {
self.protected_files.lock().clear();
}
}
impl Default for FileProtector {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_protector() {
let protector = FileProtector::new();
assert_eq!(protector.get_protected_size(), 0);
assert!(!protector.is_protected(1));
}
#[test]
fn test_protect_file() {
let protector = FileProtector::new();
protector.protect_file(1, "Backup");
assert!(protector.is_protected(1));
assert_eq!(protector.get_protected_size(), 1);
assert_eq!(protector.get_protection_count(1), 1);
}
#[test]
fn test_protect_multiple_times() {
let protector = FileProtector::new();
protector.protect_file(1, "Backup");
protector.protect_file(1, "Feeder");
protector.protect_file(1, "DiskOrderedCursor");
assert!(protector.is_protected(1));
assert_eq!(protector.get_protection_count(1), 3);
}
#[test]
fn test_unprotect_file() {
let protector = FileProtector::new();
protector.protect_file(1, "Backup");
assert!(protector.is_protected(1));
let fully_unprotected = protector.unprotect_file(1);
assert!(fully_unprotected);
assert!(!protector.is_protected(1));
assert_eq!(protector.get_protected_size(), 0);
}
#[test]
fn test_unprotect_with_multiple_protections() {
let protector = FileProtector::new();
protector.protect_file(1, "Backup");
protector.protect_file(1, "Feeder");
protector.protect_file(1, "Cursor");
let result = protector.unprotect_file(1);
assert!(!result);
assert!(protector.is_protected(1));
assert_eq!(protector.get_protection_count(1), 2);
let result = protector.unprotect_file(1);
assert!(!result);
assert!(protector.is_protected(1));
assert_eq!(protector.get_protection_count(1), 1);
let result = protector.unprotect_file(1);
assert!(result);
assert!(!protector.is_protected(1));
assert_eq!(protector.get_protection_count(1), 0);
}
#[test]
fn test_unprotect_unprotected_file() {
let protector = FileProtector::new();
let result = protector.unprotect_file(99);
assert!(result);
assert!(!protector.is_protected(99));
}
#[test]
fn test_multiple_files() {
let protector = FileProtector::new();
protector.protect_file(1, "Backup");
protector.protect_file(2, "Feeder");
protector.protect_file(3, "Cursor");
assert_eq!(protector.get_protected_size(), 3);
assert!(protector.is_protected(1));
assert!(protector.is_protected(2));
assert!(protector.is_protected(3));
assert!(!protector.is_protected(4));
}
#[test]
fn test_get_protected_files() {
let protector = FileProtector::new();
protector.protect_file(1, "Backup");
protector.protect_file(2, "Feeder:node1");
protector.protect_file(3, "DiskOrderedCursor");
let protected = protector.get_protected_files();
assert_eq!(protected.len(), 3);
let file_numbers: Vec<u32> =
protected.iter().map(|(f, _)| *f).collect();
assert!(file_numbers.contains(&1));
assert!(file_numbers.contains(&2));
assert!(file_numbers.contains(&3));
}
#[test]
fn test_reason_tracking() {
let protector = FileProtector::new();
protector.protect_file(1, "Backup");
protector.protect_file(1, "Feeder");
let protected = protector.get_protected_files();
assert_eq!(protected.len(), 1);
let (file, reason) = &protected[0];
assert_eq!(*file, 1);
assert!(reason.contains("Backup"));
assert!(reason.contains("Feeder"));
}
#[test]
fn test_clear() {
let protector = FileProtector::new();
protector.protect_file(1, "Backup");
protector.protect_file(2, "Feeder");
protector.protect_file(3, "Cursor");
assert_eq!(protector.get_protected_size(), 3);
protector.clear();
assert_eq!(protector.get_protected_size(), 0);
assert!(!protector.is_protected(1));
assert!(!protector.is_protected(2));
assert!(!protector.is_protected(3));
}
#[test]
fn test_protection_count() {
let protector = FileProtector::new();
assert_eq!(protector.get_protection_count(1), 0);
protector.protect_file(1, "Reason1");
assert_eq!(protector.get_protection_count(1), 1);
protector.protect_file(1, "Reason2");
assert_eq!(protector.get_protection_count(1), 2);
protector.unprotect_file(1);
assert_eq!(protector.get_protection_count(1), 1);
protector.unprotect_file(1);
assert_eq!(protector.get_protection_count(1), 0);
}
#[test]
fn test_default() {
let protector = FileProtector::default();
assert_eq!(protector.get_protected_size(), 0);
}
}