use std::path::Path;
#[cfg(unix)]
use crate::Result;
#[cfg(not(feature="tokio"))]
use std::fs;
#[cfg(feature="tokio")]
use {
core::future::Future,
tokio::fs,
};
#[derive(Debug, Clone)]
pub enum Filter<'a> {
AllPaths,
All(&'a [Filter<'a>]),
Any(&'a [Filter<'a>]),
IgnoredDirNames {
names: &'a [&'a str],
case: Case,
},
IgnoredFileExts {
exts: &'a [&'a str],
case: Case,
files_without_extension: bool,
},
IgnoredFileNames {
names: &'a [&'a str],
case: Case,
},
NonSymlinkFiles,
#[cfg(unix)]
#[doc(cfg(unix))]
SameDevice {
device_id: u64,
},
SomeDirNames {
names: &'a [&'a str],
case: Case,
},
SomeFileExts {
exts: &'a [&'a str],
case: Case,
files_without_extension: bool,
},
SomeFileNames {
names: &'a [&'a str],
case: Case,
},
SymlinkFiles,
UserDefined {
#[cfg(not(feature="tokio"))]
#[doc(cfg(not(feature="tokio")))]
accept: fn(&Path) -> bool,
#[cfg(feature="tokio")]
#[doc(cfg(feature="tokio"))]
accept: fn(&Path) -> Box<dyn Future<Output=bool> + Unpin>,
},
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum Case {
Sensitive,
Insensitive,
}
#[cfg(unix)]
macro_rules! make_same_device { ($path: ident) => {{
use std::os::unix::fs::MetadataExt;
Ok(Self::SameDevice {
device_id: async_call!(fs::metadata($path.as_ref())).map(|m| m.dev())?,
})
}}}
macro_rules! accept { ($self: ident, $path: ident) => {{
match $self {
Self::AllPaths => true,
Self::All(filters) => accept_all!(filters, $path),
Self::Any(filters) => accept_any!(filters, $path),
Self::IgnoredDirNames { names, case } => ignored_dir_names!(names, case, $path),
Self::IgnoredFileExts { exts, case, files_without_extension: fwe } => ignored_file_exts!(exts, case, fwe, $path),
Self::IgnoredFileNames { names, case } => ignored_file_names!(names, case, $path),
Self::NonSymlinkFiles => non_symlink_files!($path),
#[cfg(unix)]
Self::SameDevice { device_id } => same_device!(device_id, $path),
Self::SomeDirNames { names, case } => some_dir_names!(names, case, $path),
Self::SomeFileExts { exts, case, files_without_extension } => some_file_exts!(exts, case, files_without_extension, $path),
Self::SomeFileNames { names, case } => some_file_names!(names, case, $path),
Self::SymlinkFiles => symlink_files!($path),
Self::UserDefined { accept } => {
let f = accept($path);
#[cfg(feature="tokio")]
let f = Box::pin(f);
async_call!(f)
},
}
}}}
macro_rules! accept_all { ($filters: ident, $path: ident) => {{
if $filters.is_empty() {
false
} else {
for f in *$filters {
let f = f.accept($path);
#[cfg(feature="tokio")]
let f = Box::pin(f);
if async_call!(f) == false {
return false
}
}
true
}
}}}
macro_rules! accept_any { ($filters: ident, $path: ident) => {{
for f in *$filters {
let f = f.accept($path);
#[cfg(feature="tokio")]
let f = Box::pin(f);
if async_call!(f) {
return true
}
}
false
}}}
macro_rules! ignored_dir_names { ($dir_names: ident, $case: ident, $path: ident) => {{
if $dir_names.is_empty() {
return true;
}
if $path.is_dir() {
if let Some(Some(name)) = $path.file_name().map(|n| n.to_str()) {
return match $case {
Case::Sensitive => $dir_names.contains(&name) == false,
Case::Insensitive => {
let name = name.to_lowercase();
$dir_names.iter().all(|n| n.to_lowercase() != name)
},
};
}
}
true
}}}
macro_rules! ignored_file_exts { ($file_exts: ident, $case: ident, $files_without_extension: ident, $path: ident) => {{
if $path.is_file() {
return match $path.extension().map(|e| e.to_str()) {
Some(Some(ext)) => $file_exts.is_empty() || match $case {
Case::Sensitive => $file_exts.contains(&ext) == false,
Case::Insensitive => {
let ext = ext.to_lowercase();
$file_exts.iter().all(|e| e.to_lowercase() != ext)
},
},
Some(None) => true,
None => $files_without_extension == &false,
};
}
true
}}}
macro_rules! ignored_file_names { ($file_names: ident, $case: ident, $path: ident) => {{
if $file_names.is_empty() {
return true;
}
if $path.is_file() {
if let Some(Some(name)) = $path.file_name().map(|n| n.to_str()) {
return match $case {
Case::Sensitive => $file_names.contains(&name) == false,
Case::Insensitive => {
let name = name.to_lowercase();
$file_names.iter().all(|n| n.to_lowercase() != name)
},
};
}
}
true
}}}
macro_rules! non_symlink_files { ($path: ident) => {{
if $path.is_file() {
if let Ok(true) = async_call!(fs::symlink_metadata($path)).map(|m| m.file_type().is_symlink()) {
return false;
}
}
true
}}}
#[cfg(unix)]
macro_rules! same_device { ($device_id: ident, $path: ident) => {{
use std::os::unix::fs::MetadataExt;
match async_call!(fs::metadata($path)).map(|m| m.dev()) {
Ok(id) => $device_id == &id,
Err(_) => false,
}
}}}
macro_rules! some_dir_names { ($names: ident, $case: ident, $path: ident) => {{
if $path.is_dir() {
if $names.is_empty() {
return false;
}
return match $path.file_name().map(|n| n.to_str()) {
Some(Some(name)) => match $case {
Case::Sensitive => $names.contains(&name),
Case::Insensitive => {
let name = name.to_lowercase();
$names.iter().any(|n| n.to_lowercase() == name)
},
},
_ => false,
};
}
true
}}}
macro_rules! some_file_exts { ($file_exts: ident, $case: ident, $files_without_extension: ident, $path: ident) => {{
if $path.is_file() {
return match $path.extension().map(|e| e.to_str()) {
Some(Some(ext)) => $file_exts.is_empty() == false && match $case {
Case::Sensitive => $file_exts.contains(&ext),
Case::Insensitive => {
let ext = ext.to_lowercase();
$file_exts.iter().any(|e| e.to_lowercase() == ext)
},
},
Some(None) => false,
None => *$files_without_extension,
};
}
true
}}}
macro_rules! some_file_names { ($names: ident, $case: ident, $path: ident) => {{
if $path.is_file() {
if $names.is_empty() {
return false;
}
return match $path.file_name().map(|n| n.to_str()) {
Some(Some(name)) => match $case {
Case::Sensitive => $names.contains(&name),
Case::Insensitive => {
let name = name.to_lowercase();
$names.iter().any(|n| n.to_lowercase() == name)
},
},
_ => false,
};
}
true
}}}
macro_rules! symlink_files { ($path: ident) => {{
if $path.is_file() {
return match async_call!(fs::symlink_metadata($path)).map(|m| m.file_type().is_symlink()) {
Ok(true) => true,
_ => false,
};
}
true
}}}
impl Filter<'_> {
#[cfg(all(not(feature="tokio"), unix))]
#[doc(cfg(all(not(feature="tokio"), unix)))]
pub fn make_same_device<P>(path: P) -> Result<Self> where P: AsRef<Path> {
make_same_device!(path)
}
#[cfg(all(feature="tokio", unix))]
#[doc(cfg(all(feature="tokio", unix)))]
pub async fn make_same_device<P>(path: P) -> Result<Self> where P: AsRef<Path> {
make_same_device!(path)
}
#[cfg(not(feature="tokio"))]
#[doc(cfg(not(feature="tokio")))]
pub (crate) fn accept(&self, path: &Path) -> bool {
accept!(self, path)
}
#[cfg(feature="tokio")]
#[doc(cfg(feature="tokio"))]
pub (crate) async fn accept(&self, path: &Path) -> bool {
accept!(self, path)
}
}