use crate::errors::{
FilesystemError, FilesystemIOErrorKind as IOErr, OtherFilesystemErrorKind as OtherErr,
};
use std::path::{Path, PathBuf};
use tokio::fs;
pub fn os_str_as_str(os_str: &std::ffi::OsStr) -> Result<&str, FilesystemError> {
os_str
.to_str()
.ok_or_else(OtherErr::InvalidUnicodeOsStr(os_str.to_owned()).attach())
}
pub fn get_file_name(path: &Path) -> Result<&str, FilesystemError> {
path
.file_name()
.ok_or_else(OtherErr::PathWithoutFilename(path.to_owned()).attach())
.and_then(|stem| os_str_as_str(stem))
}
pub fn get_file_stem(path: &Path) -> Result<&str, FilesystemError> {
path
.file_stem()
.ok_or_else(OtherErr::PathWithoutFilename(path.to_owned()).attach())
.and_then(|stem| os_str_as_str(stem))
}
pub fn get_file_extension(path: &Path) -> Result<&str, FilesystemError> {
path
.extension()
.ok_or_else(OtherErr::PathWithoutExtension(path.to_owned()).attach())
.and_then(|extension| os_str_as_str(extension))
}
pub async fn exists(path: &Path) -> Result<bool, FilesystemError> {
fs::try_exists(path)
.await
.map_err(IOErr::CouldntCheckIfExists(path.to_owned()).attach())
}
pub fn parent(path: &Path) -> Result<&Path, FilesystemError> {
path
.parent()
.ok_or_else(OtherErr::PathWithoutParent(path.to_owned()).attach())
}
pub async fn read_dir(path: &Path) -> Result<fs::ReadDir, FilesystemError> {
fs::read_dir(path)
.await
.map_err(IOErr::CouldntReadDirectory(path.to_owned()).attach())
}
pub async fn next_entry(
read_dir: &mut fs::ReadDir,
path: &Path,
) -> Result<Option<fs::DirEntry>, FilesystemError> {
read_dir
.next_entry()
.await
.map_err(IOErr::CouldntReadDirectoryNextEntry(path.to_owned()).attach())
}
pub async fn file_type(
dir_entry: &fs::DirEntry,
path: &Path,
) -> Result<std::fs::FileType, FilesystemError> {
dir_entry
.file_type()
.await
.map_err(IOErr::CouldntGetFileType(path.to_owned()).attach())
}
pub async fn get_canonical_path(path: &Path) -> Result<PathBuf, FilesystemError> {
fs::canonicalize(path)
.await
.map_err(IOErr::CouldntGetCanonical(path.to_owned()).attach())
}
pub fn get_basedirs() -> Result<directories::BaseDirs, FilesystemError> {
directories::BaseDirs::new().ok_or_else(OtherErr::MissingHomeDirectory.attach())
}
pub async fn create_dir(path: &Path) -> Result<(), FilesystemError> {
fs::create_dir_all(path)
.await
.map_err(IOErr::CouldntCreateDirectory(path.to_owned()).attach())
}
pub async fn copy_file(from: &Path, to: &Path) -> Result<u64, FilesystemError> {
fs::copy(from, to).await.map_err(
IOErr::CouldntCopyFile {
from: from.to_owned(),
to: to.to_owned(),
}
.attach(),
)
}
pub async fn rename(from: &Path, to: &Path) -> Result<(), FilesystemError> {
fs::rename(from, to).await.map_err(
IOErr::CouldntMove {
from: from.to_owned(),
to: to.to_owned(),
}
.attach(),
)
}
pub async fn remove_file(path: &Path) -> Result<(), FilesystemError> {
fs::remove_file(path)
.await
.map_err(IOErr::CouldntRemoveFile(path.to_owned()).attach())
}
pub async fn remove_empty_dir(path: &Path) -> Result<(), FilesystemError> {
fs::remove_dir(path)
.await
.map_err(IOErr::CouldntRemoveEmptyDir(path.to_owned()).attach())
}
pub async fn remove_dir_all(path: &Path) -> Result<(), FilesystemError> {
fs::remove_dir_all(path)
.await
.map_err(IOErr::CouldntRemoveDirWithContents(path.to_owned()).attach())
}
pub async fn read_path_metadata(path: &Path) -> Result<std::fs::Metadata, FilesystemError> {
fs::metadata(path)
.await
.map_err(IOErr::CouldntReadPathMetadata(path.to_owned()).attach())
}
pub async fn read_file_metadata(file: &fs::File) -> Result<std::fs::Metadata, FilesystemError> {
file
.metadata()
.await
.map_err(IOErr::CouldntReadFileMetadata.attach())
}
pub async fn is_dir(path: &Path) -> Result<Option<bool>, FilesystemError> {
if exists(path).await? {
read_path_metadata(path)
.await
.map(|metadata| Some(metadata.is_dir()))
} else {
Ok(None)
}
}
pub async fn is_folder_empty(folder: &Path) -> Result<bool, FilesystemError> {
match is_dir(folder).await? {
None => Ok(true),
Some(false) => Err(OtherErr::ShouldBeAFolder(folder.to_owned()).into()),
Some(true) => match next_entry(&mut read_dir(folder).await?, folder).await? {
None => Ok(true),
Some(_) => Ok(false),
},
}
}
pub async fn ensure_is_dir(path: &Path) -> Result<(), FilesystemError> {
if let Some(true) = is_dir(path).await? {
Ok(())
} else {
Err(OtherErr::ShouldBeAFolder(path.to_owned()).into())
}
}
pub async fn ensure_is_empty(path: &Path) -> Result<(), FilesystemError> {
if is_folder_empty(path).await? {
Ok(())
} else {
Err(OtherErr::ShouldBeEmpty(path.to_owned()).into())
}
}
pub async fn set_permissions(
path: &Path,
permissions: std::fs::Permissions,
) -> Result<(), FilesystemError> {
fs::set_permissions(path, permissions)
.await
.map_err(IOErr::CouldntSetPermissions(path.to_owned()).attach())
}
pub async fn open_file(
path: &Path,
options: &mut fs::OpenOptions,
) -> Result<fs::File, FilesystemError> {
options
.open(path)
.await
.map_err(IOErr::CouldntOpenFile(path.to_owned()).attach())
}
pub async fn set_file_len(file: &fs::File, size: u64) -> Result<(), FilesystemError> {
file
.set_len(size)
.await
.map_err(IOErr::SetFileLength(size).attach())
}
pub async fn file_sync_all(file: &fs::File) -> Result<(), FilesystemError> {
file.sync_all().await.map_err(IOErr::SyncFile.attach())
}
#[cfg_attr(not(unix), allow(unused_variables))]
pub async fn make_executable(path: &Path) -> Result<(), FilesystemError> {
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let metadata = read_path_metadata(path).await?;
let mut permissions = metadata.permissions();
let mode = permissions.mode();
if mode & 0o111 == 0o111 {
return Ok(());
}
permissions.set_mode(mode | 0o111);
set_permissions(path, permissions).await?;
}
Ok(())
}
pub async fn fill_buffer(
buf: &mut (impl tokio::io::AsyncBufReadExt + Unpin),
) -> Result<&[u8], FilesystemError> {
buf
.fill_buf()
.await
.map_err(IOErr::CouldntFillAsyncBuffer.attach())
}
pub async fn write_all(
writer: &mut (impl tokio::io::AsyncWriteExt + Unpin),
buffer: &[u8],
) -> Result<(), FilesystemError> {
writer
.write_all(buffer)
.await
.map_err(IOErr::CouldntWriteBuffer.attach())
}
pub fn spawn_command(
command: &mut tokio::process::Command,
) -> Result<tokio::process::Child, FilesystemError> {
command.spawn().map_err(IOErr::CouldnSpawnProcess.attach())
}
pub async fn wait_child(
child: &mut tokio::process::Child,
) -> Result<std::process::ExitStatus, FilesystemError> {
child
.wait()
.await
.map_err(IOErr::CouldntWaitForChild.attach())
}