Files
empty-dir/src/walker.rs

132 lines
3.3 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use std::{
ffi::OsStr,
fs::{self, DirEntry, ReadDir},
io,
path::Path,
};
use crate::{logger::log_error, mac_meta};
pub struct Walker;
impl Walker {
pub fn new() -> Self {
Self
}
pub fn prune(&self, path: &Path) -> io::Result<bool> {
let entries = match self.read_entries(path)? {
EntryStream::Entries(entries) => entries,
EntryStream::Skip => return Ok(false),
};
let mut is_empty = true;
for entry_result in entries {
if !self.process_entry(path, entry_result) {
is_empty = false;
}
}
Ok(is_empty)
}
fn read_entries(&self, path: &Path) -> io::Result<EntryStream> {
match fs::read_dir(path) {
Ok(entries) => Ok(EntryStream::Entries(entries)),
Err(err) => {
if err.kind() == io::ErrorKind::PermissionDenied {
eprintln!("无法读取 `{}`{}", path.display(), err);
Ok(EntryStream::Skip)
} else {
Err(err)
}
}
}
}
fn process_entry(&self, parent: &Path, entry_result: io::Result<DirEntry>) -> bool {
let entry = match entry_result {
Ok(entry) => entry,
Err(err) => {
log_error(parent, &err);
return false;
}
};
let child_path = entry.path();
let file_type = match entry.file_type() {
Ok(file_type) => file_type,
Err(err) => {
log_error(&child_path, &err);
return false;
}
};
if file_type.is_dir() {
return self.handle_directory(&child_path);
}
if file_type.is_file() || file_type.is_symlink() {
let name = entry.file_name();
return self.handle_metadata_file(&name, &child_path);
}
false
}
fn handle_directory(&self, child_path: &Path) -> bool {
match self.prune(child_path) {
Ok(true) => match fs::remove_dir(child_path) {
Ok(_) => {
println!("删除空目录 `{}`", child_path.display());
true
}
Err(err) => {
log_error(child_path, &err);
false
}
},
Ok(false) => false,
Err(err) => {
log_error(child_path, &err);
false
}
}
}
fn handle_metadata_file(&self, name: &OsStr, path: &Path) -> bool {
if !should_remove_file(name, path) {
return false;
}
println!("删除元数据文件 `{}`", path.display());
match fs::remove_file(path) {
Ok(_) => true,
Err(err) => {
log_error(path, &err);
false
}
}
}
}
enum EntryStream {
Entries(ReadDir),
Skip,
}
fn should_remove_file(name: &OsStr, path: &Path) -> bool {
if name == OsStr::new(".DS_Store") {
return true;
}
if let Some(name_str) = name.to_str() {
if name_str.starts_with("._") {
return mac_meta::is_mac_meta_file(path);
}
}
false
}