use std::{
fs::read_dir,
io::{stderr, Write},
};
use super::{Matcher, MatcherIO, WalkEntry};
pub struct EmptyMatcher;
impl EmptyMatcher {
pub fn new() -> Self {
Self
}
}
impl Matcher for EmptyMatcher {
fn matches(&self, file_info: &WalkEntry, _: &mut MatcherIO) -> bool {
if file_info.file_type().is_file() {
match file_info.metadata() {
Ok(meta) => meta.len() == 0,
Err(err) => {
writeln!(
&mut stderr(),
"Error getting size for {}: {}",
file_info.path().display(),
err
)
.unwrap();
false
}
}
} else if file_info.file_type().is_dir() {
match read_dir(file_info.path()) {
Ok(mut it) => it.next().is_none(),
Err(err) => {
writeln!(
&mut stderr(),
"Error getting contents of {}: {}",
file_info.path().display(),
err
)
.unwrap();
false
}
}
} else {
false
}
}
}
#[cfg(test)]
mod tests {
use tempfile::Builder;
use super::*;
use crate::find::matchers::tests::get_dir_entry_for;
use crate::find::tests::FakeDependencies;
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
#[test]
fn empty_files() {
let empty_file_info = get_dir_entry_for("test_data/simple", "abbbc");
let nonempty_file_info = get_dir_entry_for("test_data/size", "512bytes");
let matcher = EmptyMatcher::new();
let deps = FakeDependencies::new();
assert!(matcher.matches(&empty_file_info, &mut deps.new_matcher_io()));
assert!(!matcher.matches(&nonempty_file_info, &mut deps.new_matcher_io()));
}
#[test]
fn empty_directories() {
let temp_dir = Builder::new()
.prefix("empty_directories")
.tempdir()
.unwrap();
let temp_dir_path = temp_dir.path().to_string_lossy();
let subdir_name = "subdir";
std::fs::create_dir(temp_dir.path().join(subdir_name)).unwrap();
let matcher = EmptyMatcher::new();
let deps = FakeDependencies::new();
let file_info = get_dir_entry_for(&temp_dir_path, subdir_name);
assert!(matcher.matches(&file_info, &mut deps.new_matcher_io()));
std::fs::File::create(temp_dir.path().join(subdir_name).join("a")).unwrap();
let file_info = get_dir_entry_for(&temp_dir_path, subdir_name);
assert!(!matcher.matches(&file_info, &mut deps.new_matcher_io()));
}
#[test]
fn empty_file_vs_empty_directory() {
let empty_file_info = get_dir_entry_for("test_data/simple", "abbbc");
let temp_dir = Builder::new()
.prefix("empty_file_vs_empty_directory")
.tempdir()
.unwrap();
let temp_dir_path = temp_dir.path().to_string_lossy();
let subdir_name = "empty_subdir";
std::fs::create_dir(temp_dir.path().join(subdir_name)).unwrap();
let matcher = EmptyMatcher::new();
let deps = FakeDependencies::new();
assert!(
matcher.matches(&empty_file_info, &mut deps.new_matcher_io()),
"empty file should match"
);
let empty_dir_info = get_dir_entry_for(&temp_dir_path, subdir_name);
assert!(
matcher.matches(&empty_dir_info, &mut deps.new_matcher_io()),
"empty directory should match"
);
}
#[test]
#[cfg(unix)]
fn non_readable_directory() {
let temp_dir = Builder::new()
.prefix("non_readable_directory")
.tempdir()
.unwrap();
let temp_dir_path = temp_dir.path().to_string_lossy();
let subdir_name = "unreadable";
std::fs::create_dir(temp_dir.path().join(subdir_name)).unwrap();
let mut perms = std::fs::metadata(temp_dir.path().join(subdir_name))
.unwrap()
.permissions();
perms.set_mode(0o000);
std::fs::set_permissions(temp_dir.path().join(subdir_name), perms).unwrap();
if std::fs::read_dir(temp_dir.path().join(subdir_name)).is_ok() {
let mut perms = std::fs::metadata(temp_dir.path().join(subdir_name))
.unwrap()
.permissions();
perms.set_mode(0o755);
std::fs::set_permissions(temp_dir.path().join(subdir_name), perms).unwrap();
return;
}
let matcher = EmptyMatcher::new();
let deps = FakeDependencies::new();
let file_info = get_dir_entry_for(&temp_dir_path, subdir_name);
assert!(!matcher.matches(&file_info, &mut deps.new_matcher_io()));
let mut perms = std::fs::metadata(temp_dir.path().join(subdir_name))
.unwrap()
.permissions();
perms.set_mode(0o755);
std::fs::set_permissions(temp_dir.path().join(subdir_name), perms).unwrap();
}
}