use crate::error::PathError;
use crate::internal::validation::reject_nul_path;
use std::fs;
use std::path::{Path, PathBuf};
use std::time::SystemTime;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MetadataSummary {
pub len: u64,
pub readonly: bool,
pub modified: Option<SystemTime>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DirectoryInspection {
pub path: PathBuf,
pub exists: bool,
pub is_directory: bool,
pub is_symlink: bool,
pub is_readable: Option<bool>,
pub metadata: Option<MetadataSummary>,
}
pub fn directory_exists(path: impl AsRef<Path>) -> bool {
let path = path.as_ref();
if path.is_dir() {
return true;
}
fs::metadata(path).map(|m| m.is_dir()).unwrap_or(false)
}
#[inline]
pub fn is_existing_directory(path: impl AsRef<Path>) -> bool {
directory_exists(path)
}
pub fn inspect_directory(path: impl AsRef<Path>) -> Result<DirectoryInspection, PathError> {
let path = path.as_ref();
reject_nul_path(path)?;
let path_buf = path.to_path_buf();
let symlink_meta = match fs::symlink_metadata(path) {
Ok(m) => m,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
return Ok(DirectoryInspection {
path: path_buf,
exists: false,
is_directory: false,
is_symlink: false,
is_readable: None,
metadata: None,
});
}
Err(e) => return Err(PathError::filesystem(path, e)),
};
let is_symlink = symlink_meta.file_type().is_symlink();
let is_directory = if is_symlink {
fs::metadata(path).map(|m| m.is_dir()).unwrap_or(false)
} else {
symlink_meta.is_dir()
};
let is_readable = if is_directory {
Some(fs::read_dir(path).is_ok())
} else {
None
};
let metadata = Some(MetadataSummary {
len: symlink_meta.len(),
readonly: symlink_meta.permissions().readonly(),
modified: symlink_meta.modified().ok(),
});
Ok(DirectoryInspection {
path: path_buf,
exists: true,
is_directory,
is_symlink,
is_readable,
metadata,
})
}
pub fn require_directory(path: impl AsRef<Path>) -> Result<PathBuf, PathError> {
let path = path.as_ref();
reject_nul_path(path)?;
let inspection = inspect_directory(path)?;
if !inspection.exists {
return Err(PathError::invalid(format!(
"path does not exist: {}",
path.to_string_lossy()
)));
}
if !inspection.is_directory {
return Err(PathError::invalid(format!(
"path is not a directory: {}",
path.to_string_lossy()
)));
}
Ok(path.to_path_buf())
}