use std::char::REPLACEMENT_CHARACTER;
use std::ffi::OsStr;
use std::hash::{DefaultHasher, Hasher};
use std::path::{Component, Path, PathBuf};
#[inline]
pub fn normalize_separators<T: AsRef<OsStr>>(path: T) -> String {
let path = path.as_ref().to_string_lossy();
#[cfg(not(windows))]
{
path.replace('\\', "/")
}
#[cfg(windows)]
{
path.replace('/', "\\")
}
}
#[inline]
pub fn standardize_separators<T: AsRef<OsStr>>(path: T) -> String {
path.as_ref().to_string_lossy().replace('\\', "/")
}
#[inline]
pub fn exe_name<T: AsRef<str>>(name: T) -> String {
let name = name.as_ref();
#[cfg(not(windows))]
{
name.into()
}
#[cfg(windows)]
{
if name.ends_with(".exe") {
name.into()
} else {
format!("{name}.exe")
}
}
}
pub fn encode_component<T: AsRef<OsStr>>(value: T) -> String {
let mut output = String::new();
for ch in value.as_ref().to_string_lossy().chars() {
match ch {
'@' | '*' | REPLACEMENT_CHARACTER => {
}
'/' | ':' => {
output.push('-');
}
_ => {
output.push(ch);
}
}
}
output.trim_matches(['-', '.']).to_owned()
}
pub fn hash_component<T: AsRef<OsStr>>(value: T) -> String {
let mut hasher = DefaultHasher::default();
hasher.write(value.as_ref().as_encoded_bytes());
format!("{}", hasher.finish())
}
pub fn clean<T: AsRef<Path>>(path: T) -> PathBuf {
let mut components = path.as_ref().components().peekable();
let mut cleaned = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
components.next();
PathBuf::from(c.as_os_str())
} else {
PathBuf::new()
};
let mut leading_parent_dots = 0;
let mut component_count = 0;
for component in components {
match component {
Component::Prefix(_) | Component::CurDir => {}
Component::RootDir => {
cleaned.push(component.as_os_str());
component_count += 1;
}
Component::ParentDir => {
if component_count == 1 && cleaned.is_absolute() {
} else if component_count == leading_parent_dots {
cleaned.push("..");
leading_parent_dots += 1;
component_count += 1;
} else {
cleaned.pop();
component_count -= 1;
}
}
Component::Normal(c) => {
cleaned.push(c);
component_count += 1;
}
}
}
if component_count == 0 {
cleaned.push(".");
}
cleaned
}
pub fn are_equal<L: AsRef<Path>, R: AsRef<Path>>(left: L, right: R) -> bool {
clean(left) == clean(right)
}
pub trait PathExt {
fn clean(&self) -> PathBuf;
fn matches(&self, other: &Path) -> bool;
}
impl PathExt for Path {
fn clean(&self) -> PathBuf {
clean(self)
}
fn matches(&self, other: &Path) -> bool {
are_equal(self, other)
}
}
impl PathExt for PathBuf {
fn clean(&self) -> PathBuf {
clean(self)
}
fn matches(&self, other: &Path) -> bool {
are_equal(self, other)
}
}