Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

find: Implement -fstype #408

Merged
merged 8 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ regex = "1.10"
once_cell = "1.19"
onig = { version = "6.4", default-features = false }
uucore = { version = "0.0.27", features = ["entries", "fs", "fsext", "mode"] }
nix = { version = "0.29", features = ["user"] }
nix = { version = "0.29", features = ["fs", "user"] }

[dev-dependencies]
assert_cmd = "2"
Expand Down
93 changes: 93 additions & 0 deletions src/find/matchers/fs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// This file is part of the uutils findutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

use super::Matcher;

pub struct FileSystemMatcher {
hanbings marked this conversation as resolved.
Show resolved Hide resolved
fs_text: String,
}

impl FileSystemMatcher {
pub fn new(fs_text: String) -> Self {
Self { fs_text }
}
}

impl Matcher for FileSystemMatcher {
fn matches(&self, file_info: &walkdir::DirEntry, _: &mut super::MatcherIO) -> bool {
#[cfg(not(unix))]
{
false
}
#[cfg(unix)]
{
use std::os::unix::fs::MetadataExt;
let dev_id = file_info
.metadata()
.expect("Could not get metadata")
hanbings marked this conversation as resolved.
Show resolved Hide resolved
.dev()
.to_string();
let fs_list =
uucore::fsext::read_fs_list().expect("Could not find the filesystem info");

fs_list
.into_iter()
.find(|fs| fs.dev_id == dev_id)
.map_or_else(String::new, |fs| fs.fs_type)
.contains(&self.fs_text)
}
}
}

#[cfg(test)]
mod tests {
#[test]
#[cfg(unix)]
fn test_fs_matcher() {
use crate::find::{
matchers::{tests::get_dir_entry_for, Matcher},
tests::FakeDependencies,
};
use std::{fs::File, os::unix::fs::MetadataExt};
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 dev_id = file_info
.metadata()
.expect("Could not get metadata")
.dev()
.to_string();
let fs_list = uucore::fsext::read_fs_list().expect("Could not find the filesystem info");
let target_fs_type = fs_list
.into_iter()
.find(|fs| fs.dev_id == dev_id)
.map_or_else(String::new, |fs| fs.fs_type);

// should match fs type
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
);

// should not match 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
);
}
}
9 changes: 9 additions & 0 deletions src/find/matchers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod access;
mod delete;
mod empty;
pub mod exec;
mod fs;
mod glob;
mod group;
mod lname;
Expand All @@ -29,6 +30,7 @@ mod user;

use ::regex::Regex;
use chrono::{DateTime, Datelike, NaiveDateTime, Utc};
use fs::FileSystemMatcher;
use std::path::Path;
use std::time::SystemTime;
use std::{error::Error, str::FromStr};
Expand Down Expand Up @@ -419,6 +421,13 @@ fn build_matcher_tree(
i += 1;
Some(TypeMatcher::new(args[i])?.into_box())
}
"-fstype" => {
if i >= args.len() - 1 {
return Err(From::from(format!("missing argument to {}", args[i])));
}
i += 1;
Some(FileSystemMatcher::new(args[i].to_string()).into_box())
}
"-delete" => {
// -delete implicitly requires -depth
config.depth_first = true;
Expand Down
33 changes: 33 additions & 0 deletions src/find/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1148,4 +1148,37 @@ mod tests {
assert_eq!(rc, 0);
assert_eq!(deps.get_output_as_string(), "");
}

#[test]
#[cfg(unix)]
fn test_fs_matcher() {
use crate::find::tests::FakeDependencies;
use std::{os::unix::fs::MetadataExt, path::Path};

let path = Path::new("./test_data/simple/subdir");
let dev_id = path
.metadata()
.expect("Could not get metadata")
.dev()
.to_string();
let fs_list = uucore::fsext::read_fs_list().expect("Could not find the filesystem info");
let target_fs_type = fs_list
.into_iter()
.find(|fs| fs.dev_id == dev_id)
.map_or_else(String::new, |fs| fs.fs_type);

// should match fs type
let deps = FakeDependencies::new();
let rc = find_main(
&[
"find",
"./test_data/simple/subdir",
"-fstype",
&target_fs_type,
],
&deps,
);

assert_eq!(rc, 0);
}
}
58 changes: 58 additions & 0 deletions tests/find_cmd_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -743,3 +743,61 @@ fn find_age_range() {
}
}
}

#[test]
#[cfg(unix)]
#[serial(working_dir)]
fn find_fs() {
use std::{os::unix::fs::MetadataExt, path::Path};

let path = Path::new("./test_data/simple/subdir");
let dev_id = path
hanbings marked this conversation as resolved.
Show resolved Hide resolved
.metadata()
.expect("Could not get metadata")
.dev()
.to_string();
let fs_list = uucore::fsext::read_fs_list().expect("Could not find the filesystem info");
let target_fs_type = fs_list
hanbings marked this conversation as resolved.
Show resolved Hide resolved
.into_iter()
.find(|fs| fs.dev_id == dev_id)
.map_or_else(String::new, |fs| fs.fs_type);

// match fs type
Command::cargo_bin("find")
.expect("found binary")
.args(["./test_data/simple/subdir", "-fstype", &target_fs_type])
.assert()
.success()
.stdout(predicate::str::contains("./test_data/simple/subdir"))
.stderr(predicate::str::is_empty());

// not match fs type
Command::cargo_bin("find")
.expect("found binary")
.args([
"./test_data/simple/subdir",
"-fstype",
format!("{} foo", target_fs_type).as_str(),
])
.assert()
.success()
.stdout(predicate::str::is_empty())
.stderr(predicate::str::is_empty());

// not contain fstype text.
Command::cargo_bin("find")
.expect("found binary")
.args(["./test_data/simple/subdir", "-fstype"])
.assert()
.failure()
.stdout(predicate::str::is_empty());

// void fstype
Command::cargo_bin("find")
.expect("found binary")
.args(["./test_data/simple/subdir", "-fstype", " "])
.assert()
.success()
.stdout(predicate::str::is_empty())
.stderr(predicate::str::is_empty());
}
Loading