use super::{Matcher, MatcherIO, WalkEntry};
#[cfg(unix)]
use uucore::error::UResult;
#[cfg(unix)]
pub struct Cache {
dev_id: String,
fs_type: String,
}
#[cfg(unix)]
use std::{
cell::RefCell,
io::{stderr, Write},
path::Path,
};
#[cfg(unix)]
pub fn get_file_system_type(path: &Path, cache: &RefCell<Option<Cache>>) -> UResult<String> {
use std::os::unix::fs::MetadataExt;
let metadata = match path.symlink_metadata() {
Ok(metadata) => metadata,
Err(err) => Err(err)?,
};
let dev_id = metadata.dev().to_string();
if let Some(cache) = cache.borrow().as_ref() {
if cache.dev_id == dev_id {
return Ok(cache.fs_type.clone());
}
}
let fs_list = uucore::fsext::read_fs_list()?;
let result = fs_list
.into_iter()
.find(|fs| fs.dev_id == dev_id)
.map_or_else(String::new, |fs| fs.fs_type);
cache.replace(Some(Cache {
dev_id,
fs_type: result.clone(),
}));
Ok(result)
}
pub struct FileSystemMatcher {
#[cfg(unix)]
fs_text: String,
#[cfg(unix)]
cache: RefCell<Option<Cache>>,
}
impl FileSystemMatcher {
#[cfg(unix)]
pub fn new(fs_text: String) -> Self {
Self {
fs_text,
cache: RefCell::new(None),
}
}
#[cfg(not(unix))]
pub fn new(_fs_text: String) -> Self {
Self {}
}
}
impl Matcher for FileSystemMatcher {
#[cfg(unix)]
fn matches(&self, file_info: &WalkEntry, _: &mut MatcherIO) -> bool {
match get_file_system_type(file_info.path(), &self.cache) {
Ok(result) => result == self.fs_text,
Err(_) => {
writeln!(
&mut stderr(),
"Error getting filesystem type for {}",
file_info.path().to_string_lossy()
)
.unwrap();
false
}
}
}
#[cfg(not(unix))]
fn matches(&self, _file_info: &WalkEntry, _: &mut MatcherIO) -> bool {
false
}
}
#[cfg(test)]
mod tests {
#[test]
#[cfg(unix)]
fn test_fs_matcher() {
use crate::find::{
matchers::{
fs::{get_file_system_type, Cache},
tests::get_dir_entry_for,
Matcher,
},
tests::FakeDependencies,
};
use std::cell::RefCell;
use std::fs::File;
use tempfile::Builder;
let deps = FakeDependencies::new();
let mut matcher_io = deps.new_matcher_io();
let temp_dir = Builder::new().prefix("fs_matcher").tempdir().unwrap();
let foo_path = temp_dir.path().join("foo");
let _ = File::create(foo_path).expect("create temp file");
let file_info = get_dir_entry_for(&temp_dir.path().to_string_lossy(), "foo");
let empty_cache = RefCell::new(None);
let target_fs_type = get_file_system_type(file_info.path(), &empty_cache).unwrap();
let unmatched_cache = RefCell::new(Some(Cache {
dev_id: "foo".to_string(),
fs_type: "bar".to_string(),
}));
let target_fs_type_unmatched_cache =
get_file_system_type(file_info.path(), &unmatched_cache).unwrap();
assert_eq!(
target_fs_type, target_fs_type_unmatched_cache,
"get_file_system_type should return correct result with unmatched cache"
);
assert_eq!(
unmatched_cache.borrow().as_ref().unwrap().fs_type,
target_fs_type,
"get_file_system_type should set the cache to the last query result"
);
let matcher = super::FileSystemMatcher::new(target_fs_type.clone());
assert!(
matcher.matches(&file_info, &mut matcher_io),
"{} should match {}",
file_info.path().to_string_lossy(),
target_fs_type
);
let matcher = super::FileSystemMatcher::new(target_fs_type.clone() + "foo");
assert!(
!matcher.matches(&file_info, &mut matcher_io),
"{} should not match {}",
file_info.path().to_string_lossy(),
target_fs_type
);
}
}