@@ -6,6 +6,7 @@ use std::io::Write;
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
use path_absolutize::Absolutize;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use crate::path::get_relative_path;
|
use crate::path::get_relative_path;
|
||||||
@@ -41,24 +42,28 @@ impl Group {
|
|||||||
create_dir(group_dir).context("group dir does not exist, creating")?;
|
create_dir(group_dir).context("group dir does not exist, creating")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut symlink_dirs = Vec::new();
|
||||||
|
|
||||||
for entry in WalkDir::new(group_dir).follow_links(true).min_depth(1) {
|
for entry in WalkDir::new(group_dir).follow_links(true).min_depth(1) {
|
||||||
let file = entry?;
|
let file = entry?;
|
||||||
let path = file.path();
|
let path = file.path().absolutize_from(group_dir)?.to_path_buf();
|
||||||
|
|
||||||
if path.is_dir() {
|
if path.is_dir() {
|
||||||
|
if warn_not_symlinks && path.is_symlink() {
|
||||||
|
symlink_dirs.push(path.to_owned());
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if warn_not_symlinks && !path.is_symlink() {
|
if warn_not_symlinks && !path.is_symlink() && !is_child_of_any_dir(&path, &symlink_dirs)
|
||||||
// TODO is there an efficient way to make sure *any* of the elements in the path
|
{
|
||||||
// is a symlink?
|
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"WARNING: group file {} is not a symlink",
|
"WARNING: group file {} is not a symlink",
|
||||||
path.to_string_lossy()
|
path.to_string_lossy()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let group = Self::try_from(path, group_dir)
|
let group = Self::try_from(path.as_path(), group_dir)
|
||||||
.with_context(|| format!("reading group file {path:?}"))?;
|
.with_context(|| format!("reading group file {path:?}"))?;
|
||||||
|
|
||||||
result.insert(group);
|
result.insert(group);
|
||||||
@@ -68,6 +73,23 @@ impl Group {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if `path` is a child of any of the [`PathBuf`] in `dirs`. All paths should be
|
||||||
|
/// absolute.
|
||||||
|
fn is_child_of_any_dir(path: &Path, dirs: &[PathBuf]) -> bool {
|
||||||
|
dirs.iter()
|
||||||
|
// pair `path` with every item from `symlink_dirs`
|
||||||
|
.zip([path].iter().cycle())
|
||||||
|
// for every pair, test if all path elements of the dir are present in the file path
|
||||||
|
.map(|(dir, file)| {
|
||||||
|
dir.iter()
|
||||||
|
.zip(file.iter())
|
||||||
|
.map(|(dir_elem, file_elem)| dir_elem == file_elem)
|
||||||
|
.all(|path_element_equal| path_element_equal)
|
||||||
|
})
|
||||||
|
// it suffices if that holds for any of the generated pairs
|
||||||
|
.any(|is_child| is_child)
|
||||||
|
}
|
||||||
|
|
||||||
impl PartialOrd for Group {
|
impl PartialOrd for Group {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
match self.name.partial_cmp(&other.name) {
|
match self.name.partial_cmp(&other.name) {
|
||||||
@@ -290,4 +312,19 @@ mod tests {
|
|||||||
let result = super::extract_group_name(&path, &group_path);
|
let result = super::extract_group_name(&path, &group_path);
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn is_child_of_any_symlink_dir() {
|
||||||
|
let path = PathBuf::from("/a/b/c/d/e");
|
||||||
|
let dir = PathBuf::from("/z");
|
||||||
|
let mut symlink_dirs = vec![dir];
|
||||||
|
|
||||||
|
let result = super::is_child_of_any_dir(&path, &symlink_dirs);
|
||||||
|
assert!(!result);
|
||||||
|
|
||||||
|
symlink_dirs.push(PathBuf::from("/a/b/c"));
|
||||||
|
|
||||||
|
let result = super::is_child_of_any_dir(&path, &symlink_dirs);
|
||||||
|
assert!(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user