use crate::prelude::*;
use std::ffi::OsStr;
use std::path::Path;
use std::path::PathBuf;
pub fn relative(path: &impl AsRef<Path>) -> FsResult<&Path> {
let cwd = fs_ext::current_dir()?;
path_ext::strip_prefix(path, &cwd)
}
pub fn join_relative(base: impl AsRef<Path>, rel: impl AsRef<Path>) -> PathBuf {
let base = base.as_ref();
let rel = rel.as_ref();
let rel = rel.strip_prefix("/").unwrap_or(rel);
base.join(rel)
}
pub fn strip_prefix<'a>(
path: &'a impl AsRef<Path>,
prefix: &impl AsRef<Path>,
) -> FsResult<&'a Path> {
path.as_ref()
.strip_prefix(prefix)
.map_err(|e| FsError::other(path.as_ref(), e))
}
pub fn assert_exists(path: impl AsRef<Path>) -> FsResult {
let path = path.as_ref();
if !path.exists() {
return Err(FsError::FileNotFound {
path: path.to_path_buf(),
});
}
Ok(())
}
pub fn canonicalize(path: impl AsRef<Path>) -> FsResult<PathBuf> {
path.as_ref()
.canonicalize()
.map_err(|e| FsError::io(path, e))
}
pub fn absolute(path: impl AsRef<Path>) -> FsResult<PathBuf> {
let path = path.as_ref();
#[cfg(not(target_arch = "wasm32"))]
{
std::path::absolute(path).map_err(|e| FsError::io(path, e))
}
#[cfg(target_arch = "wasm32")]
{
let path_str = path.to_string_lossy();
if path_str.starts_with('/') {
Ok(path.to_path_buf())
} else {
Ok(PathBuf::from(format!("/{}", path_str)))
}
}
}
pub fn is_relative_url(url: &str) -> bool {
const ABS_PREFIXES: [&str; 15] = [
"/",
"http://",
"https://",
"file://",
"data:",
"mailto:",
"tel:",
"javascript:",
"ftp://",
"ws://",
"wss://",
"blob:",
"cid:",
"about:",
"chrome:",
];
!ABS_PREFIXES.iter().any(|prefix| url.starts_with(prefix))
}
pub fn create_relative(
src: impl AsRef<Path>,
dst: impl AsRef<Path>,
) -> FsResult<PathBuf> {
let path = src.as_ref();
let dst = dst.as_ref();
pathdiff::diff_paths(dst, path).ok_or_else(|| {
FsError::other(
path,
format!("Could not create relative path to dest: {:?}", dst),
)
})
}
pub fn to_forward_slash(path: impl AsRef<Path>) -> PathBuf {
path.as_ref().to_string_lossy().replace("\\", "/").into()
}
pub fn file_stem(path: &impl AsRef<Path>) -> FsResult<&OsStr> {
let path = path.as_ref();
path.file_stem()
.ok_or_else(|| FsError::other(path, "No file stem"))
}
pub fn file_name(path: &impl AsRef<Path>) -> FsResult<&OsStr> {
let path = path.as_ref();
path.file_name()
.ok_or_else(|| FsError::other(path, "No file name"))
}
pub fn is_dir_or_extension(path: &impl AsRef<Path>, ext: &str) -> bool {
let path = path.as_ref();
match path.extension() {
Some(value) => value.to_str().unwrap() == ext,
None => path.is_dir(),
}
}
#[cfg(test)]
mod test {
use crate::prelude::*;
use std::path::PathBuf;
#[test]
fn works() {
assert_eq!(
path_ext::create_relative("src", "src/lib.rs").unwrap(),
PathBuf::from("lib.rs")
);
assert_eq!(
path_ext::create_relative("foo/bar/src", "foo/bar/Cargo.toml")
.unwrap(),
PathBuf::from("../Cargo.toml")
);
}
#[test]
fn join_relative() {
assert_eq!(
path_ext::join_relative("foo/bar", "baz/style.css"),
PathBuf::from("foo/bar/baz/style.css")
);
assert_eq!(
path_ext::join_relative("foo/bar", "/baz/style.css"),
PathBuf::from("foo/bar/baz/style.css")
);
}
#[test]
fn is_relative() {
assert_eq!(path_ext::is_relative_url("style.css"), true);
assert_eq!(path_ext::is_relative_url("../style.css"), true);
assert_eq!(path_ext::is_relative_url("/style.css"), false);
assert_eq!(path_ext::is_relative_url("https://example.com"), false);
}
}