fd/src/filesystem.rs
2020-04-03 23:01:57 +02:00

118 lines
3.1 KiB
Rust

use std::borrow::Cow;
use std::env::current_dir;
use std::ffi::OsStr;
use std::fs;
use std::io;
#[cfg(any(unix, target_os = "redox"))]
use std::os::unix::fs::PermissionsExt;
use std::path::{Component, Path, PathBuf};
use crate::walk;
pub fn path_absolute_form(path: &Path) -> io::Result<PathBuf> {
if path.is_absolute() {
return Ok(path.to_path_buf());
}
let path = path.strip_prefix(".").unwrap_or(path);
current_dir().map(|path_buf| path_buf.join(path))
}
pub fn absolute_path(path: &Path) -> io::Result<PathBuf> {
let path_buf = path_absolute_form(path)?;
#[cfg(windows)]
let path_buf = Path::new(
path_buf
.as_path()
.to_string_lossy()
.trim_start_matches(r"\\?\"),
)
.to_path_buf();
Ok(path_buf)
}
// Path::is_dir() is not guaranteed to be intuitively correct for "." and ".."
// See: https://github.com/rust-lang/rust/issues/45302
pub fn is_dir(path: &Path) -> bool {
path.is_dir() && (path.file_name().is_some() || path.canonicalize().is_ok())
}
#[cfg(any(unix, target_os = "redox"))]
pub fn is_executable(md: &fs::Metadata) -> bool {
md.permissions().mode() & 0o111 != 0
}
#[cfg(windows)]
pub fn is_executable(_: &fs::Metadata) -> bool {
false
}
pub fn is_empty(entry: &walk::DirEntry) -> bool {
if let Some(file_type) = entry.file_type() {
if file_type.is_dir() {
if let Ok(mut entries) = fs::read_dir(entry.path()) {
entries.next().is_none()
} else {
false
}
} else if file_type.is_file() {
entry.metadata().map(|m| m.len() == 0).unwrap_or(false)
} else {
false
}
} else {
false
}
}
#[cfg(any(unix, target_os = "redox"))]
pub fn osstr_to_bytes(input: &OsStr) -> Cow<[u8]> {
use std::os::unix::ffi::OsStrExt;
Cow::Borrowed(input.as_bytes())
}
#[cfg(windows)]
pub fn osstr_to_bytes(input: &OsStr) -> Cow<[u8]> {
let string = input.to_string_lossy();
match string {
Cow::Owned(string) => Cow::Owned(string.into_bytes()),
Cow::Borrowed(string) => Cow::Borrowed(string.as_bytes()),
}
}
/// Remove the `./` prefix from a path.
pub fn strip_current_dir(pathbuf: &PathBuf) -> &Path {
let mut iter = pathbuf.components();
let mut iter_next = iter.clone();
if iter_next.next() == Some(Component::CurDir) {
iter.next();
}
iter.as_path()
}
pub fn replace_path_separator<'a>(path: &str, new_path_separator: &str) -> String {
path.replace(std::path::MAIN_SEPARATOR, &new_path_separator)
}
#[cfg(test)]
mod tests {
use super::strip_current_dir;
use std::path::{Path, PathBuf};
#[test]
fn strip_current_dir_basic() {
assert_eq!(strip_current_dir(&PathBuf::from("./foo")), Path::new("foo"));
assert_eq!(strip_current_dir(&PathBuf::from("foo")), Path::new("foo"));
assert_eq!(
strip_current_dir(&PathBuf::from("./foo/bar/baz")),
Path::new("foo/bar/baz")
);
assert_eq!(
strip_current_dir(&PathBuf::from("foo/bar/baz")),
Path::new("foo/bar/baz")
);
}
}