use std::{
env, fs, io, path,
path::{Path, PathBuf},
};
#[inline(always)]
fn env_var_path(env: &str) -> Option<PathBuf> {
env::var_os(env).map(PathBuf::from)
}
#[cfg(unix)]
pub mod unix;
#[cfg(windows)]
pub mod windows;
pub mod sys {
use std::path::{Path, PathBuf};
#[cfg(unix)]
use super::unix as sys;
#[cfg(windows)]
use super::windows as sys;
pub fn is_protected_path(item: impl AsRef<Path>) -> Vec<PathBuf> {
let item = item.as_ref();
let mut protected_by = Vec::new();
if let Some(path) = sys::protected_paths().iter().find(|p| *p == item) {
protected_by.push(path.to_owned());
}
sys::protected_directories().iter().for_each(|path| {
if item.starts_with(path) {
protected_by.push(path.to_owned());
}
});
protected_by
}
}
pub fn absolutize_to_cwd(path: impl AsRef<Path>) -> io::Result<PathBuf> {
let abs = env::current_dir()?.join(path);
Ok(abs)
}
pub fn latest_existing_ancestor(path: impl AsRef<Path>) -> io::Result<PathBuf> {
let mut latest = Some(path.as_ref());
while let Some(path) = latest {
match path.symlink_metadata() {
Ok(meta) => {
if meta.is_dir() {
return Ok(path.to_path_buf());
}
latest = path.parent()
}
Err(err) if matches!(err.kind(), io::ErrorKind::NotFound) => latest = path.parent(),
Err(err) => return Err(err),
}
}
Err(io::Error::new(
io::ErrorKind::NotFound,
format!(
"No ancestor was found for {path}",
path = path.as_ref().display()
),
))
}
pub fn normalize_path(path: impl AsRef<Path>) -> PathBuf {
let mut normalized = PathBuf::new();
for component in path.as_ref().components() {
match component {
path::Component::CurDir => {}
path::Component::ParentDir => {
if !normalized.pop() {
normalized.push("..");
}
}
_ => normalized.push(component),
}
}
normalized.iter().collect()
}
pub fn follow_symlink(symlink: impl AsRef<Path>) -> io::Result<PathBuf> {
let target = fs::read_link(symlink.as_ref())?;
let real_target = if target.is_absolute() {
target
} else {
symlink
.as_ref()
.parent()
.unwrap_or(Path::new("/"))
.join(target)
};
if let Err(err) = real_target.symlink_metadata() {
if matches!(err.kind(), io::ErrorKind::NotFound) {
return Err(io::Error::new(
io::ErrorKind::NotFound,
format!(
"Target of symlink '{symlink}' does not exist ({target})",
symlink = symlink.as_ref().display(),
target = real_target.display()
),
));
} else {
return Err(err);
}
};
Ok(real_target)
}
pub fn follow_symlink_chain(symlink: impl AsRef<Path>) -> (Vec<PathBuf>, Option<io::Error>) {
let mut stack = vec![symlink.as_ref().to_path_buf()];
loop {
let target = match follow_symlink(stack.last().unwrap()) {
Ok(target) => normalize_path(target),
Err(err) => return (stack, Some(err)),
};
if stack.contains(&target) {
stack.push(target);
return (
stack,
Some(io::Error::new(
io::ErrorKind::TooManyLinks,
format!(
"{symlink} is a cyclical",
symlink = symlink.as_ref().display()
),
)),
);
}
if target.is_symlink() {
stack.push(target);
continue;
} else {
stack.push(target);
return (stack, None);
}
}
}