#[cfg(test)]
use crate::path::PathBuf;
use crate::{
error::{Error, Operation, Result},
path::Path,
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub(crate) enum EntryKind {
File,
Directory,
Symlink,
Other,
}
#[derive(Clone, Debug)]
pub(crate) struct EntryInfo {
#[cfg(test)]
path: PathBuf,
kind: EntryKind,
}
impl EntryInfo {
#[must_use]
pub(crate) const fn kind(&self) -> EntryKind {
self.kind
}
}
pub(crate) fn ensure(path: &Path) -> Result<()> {
std::fs::create_dir_all(path)
.map_err(|source| Error::new(Operation::CreateDirectory, path, source))
}
#[cfg(test)]
pub(crate) fn create(path: &Path) -> Result<()> {
std::fs::create_dir(path).map_err(|source| Error::new(Operation::CreateDirectory, path, source))
}
pub(crate) fn inspect(path: &Path) -> Result<Option<EntryInfo>> {
inspect_with(path, true)
}
pub(crate) fn inspect_link(path: &Path) -> Result<Option<EntryInfo>> {
inspect_with(path, false)
}
fn inspect_with(path: &Path, follow: bool) -> Result<Option<EntryInfo>> {
let result = if follow {
std::fs::metadata(path)
} else {
std::fs::symlink_metadata(path)
};
let metadata = match result {
Ok(metadata) => metadata,
Err(source) if source.kind() == std::io::ErrorKind::NotFound => return Ok(None),
Err(source) => return Err(Error::new(Operation::Inspect, path, source)),
};
let file_type = metadata.file_type();
let kind = if file_type.is_file() {
EntryKind::File
} else if file_type.is_dir() {
EntryKind::Directory
} else if file_type.is_symlink() {
EntryKind::Symlink
} else {
EntryKind::Other
};
Ok(Some(EntryInfo {
#[cfg(test)]
path: path.to_path_buf(),
kind,
}))
}
#[cfg(test)]
pub(crate) fn entries(path: &Path) -> Result<Vec<EntryInfo>> {
let entries = std::fs::read_dir(path)
.map_err(|source| Error::new(Operation::ReadDirectory, path, source))?;
let mut result = Vec::new();
for entry in entries {
let entry = entry.map_err(|source| Error::new(Operation::ReadDirectory, path, source))?;
let entry_path = PathBuf::from(entry.path());
let info = inspect_link(&entry_path)?.ok_or_else(|| {
Error::new(
Operation::Inspect,
&entry_path,
std::io::Error::new(
std::io::ErrorKind::NotFound,
"directory entry disappeared during inspection",
),
)
})?;
result.push(info);
}
result.sort_by(|left, right| left.path.cmp(&right.path));
Ok(result)
}