#[cfg(windows)]
use omnipath::WinPathExt;
use std::io;
use std::path::{Path, PathBuf, absolute};
use super::dots::{expand_dots, expand_ndots};
use super::tilde::expand_tilde;
fn join_path_relative<P, Q>(path: P, relative_to: Q, expand_tilde: bool) -> PathBuf
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
let path = path.as_ref();
let relative_to = relative_to.as_ref();
if path == Path::new(".") {
relative_to.into()
} else if path.to_string_lossy().as_ref().starts_with('~') && expand_tilde {
path.into()
} else {
relative_to.join(path)
}
}
fn canonicalize(path: impl AsRef<Path>) -> io::Result<PathBuf> {
let path = expand_tilde(path);
let path = expand_ndots(path);
canonicalize_path(&path)
}
#[cfg(windows)]
fn canonicalize_path(path: &std::path::Path) -> std::io::Result<std::path::PathBuf> {
path.canonicalize()?.to_winuser_path()
}
#[cfg(not(windows))]
fn canonicalize_path(path: &std::path::Path) -> std::io::Result<std::path::PathBuf> {
path.canonicalize()
}
pub fn canonicalize_with<P, Q>(path: P, relative_to: Q) -> io::Result<PathBuf>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
let path = join_path_relative(path, relative_to, true);
canonicalize(path)
}
pub fn absolute_with<P, Q>(path: P, relative_to: Q) -> io::Result<PathBuf>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
let path = join_path_relative(path, relative_to, true);
let path = expand_tilde(path);
let path = expand_ndots(path);
absolute(path)
}
pub fn expand_path(path: impl AsRef<Path>, need_expand_tilde: bool) -> PathBuf {
let path = if need_expand_tilde {
expand_tilde(path)
} else {
PathBuf::from(path.as_ref())
};
let path = expand_ndots(path);
expand_dots(path)
}
pub fn expand_path_with<P, Q>(path: P, relative_to: Q, expand_tilde: bool) -> PathBuf
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
let path = join_path_relative(path, relative_to, expand_tilde);
expand_path(path, expand_tilde)
}
pub fn expand_to_real_path<P>(path: P) -> PathBuf
where
P: AsRef<Path>,
{
let path = expand_tilde(path);
expand_ndots(path)
}
pub fn locate_in_dirs<I, P>(
filename: impl AsRef<Path>,
cwd: impl AsRef<Path>,
dirs: impl FnOnce() -> I,
) -> std::io::Result<PathBuf>
where
I: IntoIterator<Item = P>,
P: AsRef<Path>,
{
let filename = filename.as_ref();
let cwd = cwd.as_ref();
match canonicalize_with(filename, cwd) {
Ok(path) => Ok(path),
Err(err) => {
let mut found = None;
for dir in dirs() {
if let Ok(path) =
canonicalize_with(dir, cwd).and_then(|dir| canonicalize_with(filename, dir))
{
found = Some(path);
break;
}
}
found.ok_or(err)
}
}
}