#[cfg(feature = "async")]
pub mod unsync {
use std::path::{Path, PathBuf};
use crate::unsync::OpenOptions;
pub struct File(tokio::fs::File);
impl File {
pub async fn open(path: impl AsRef<Path>) -> std::io::Result<Self> {
Ok(Self(tokio::fs::File::open(path).await?))
}
pub async fn create(path: impl AsRef<Path>) -> std::io::Result<Self> {
Ok(Self(tokio::fs::File::create(path).await?))
}
#[must_use]
pub const fn options() -> OpenOptions {
OpenOptions::new()
}
pub async fn metadata(&self) -> std::io::Result<Metadata> {
Ok(Metadata(self.0.metadata().await?))
}
#[must_use]
pub const fn inner(&self) -> &tokio::fs::File {
&self.0
}
#[must_use]
pub fn into_inner(self) -> tokio::fs::File {
self.0
}
}
impl From<tokio::fs::File> for File {
fn from(file: tokio::fs::File) -> Self {
Self(file)
}
}
impl From<File> for tokio::fs::File {
fn from(file: File) -> Self {
file.0
}
}
impl tokio::io::AsyncRead for File {
fn poll_read(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
buf: &mut tokio::io::ReadBuf<'_>,
) -> std::task::Poll<std::io::Result<()>> {
std::pin::Pin::new(&mut self.0).poll_read(cx, buf)
}
}
impl tokio::io::AsyncWrite for File {
fn poll_write(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
buf: &[u8],
) -> std::task::Poll<std::io::Result<usize>> {
std::pin::Pin::new(&mut self.0).poll_write(cx, buf)
}
fn poll_flush(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<std::io::Result<()>> {
std::pin::Pin::new(&mut self.0).poll_flush(cx)
}
fn poll_shutdown(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<std::io::Result<()>> {
std::pin::Pin::new(&mut self.0).poll_shutdown(cx)
}
}
impl tokio::io::AsyncSeek for File {
fn start_seek(
mut self: std::pin::Pin<&mut Self>,
position: std::io::SeekFrom,
) -> std::io::Result<()> {
std::pin::Pin::new(&mut self.0).start_seek(position)
}
fn poll_complete(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<std::io::Result<u64>> {
std::pin::Pin::new(&mut self.0).poll_complete(cx)
}
}
#[derive(Debug, Clone)]
pub struct Metadata(std::fs::Metadata);
impl Metadata {
#[must_use]
pub fn len(&self) -> u64 {
self.0.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[must_use]
pub fn is_file(&self) -> bool {
self.0.is_file()
}
#[must_use]
pub fn is_dir(&self) -> bool {
self.0.is_dir()
}
#[must_use]
pub fn is_symlink(&self) -> bool {
self.0.is_symlink()
}
#[must_use]
pub const fn inner(&self) -> &std::fs::Metadata {
&self.0
}
}
impl From<std::fs::Metadata> for Metadata {
fn from(metadata: std::fs::Metadata) -> Self {
Self(metadata)
}
}
pub struct ReadDir(tokio::fs::ReadDir);
impl ReadDir {
pub async fn next_entry(&mut self) -> std::io::Result<Option<DirEntry>> {
Ok(self.0.next_entry().await?.map(DirEntry))
}
}
impl From<tokio::fs::ReadDir> for ReadDir {
fn from(read_dir: tokio::fs::ReadDir) -> Self {
Self(read_dir)
}
}
pub struct DirEntry(tokio::fs::DirEntry);
impl DirEntry {
#[must_use]
pub fn path(&self) -> PathBuf {
self.0.path()
}
#[must_use]
pub fn file_name(&self) -> std::ffi::OsString {
self.0.file_name()
}
pub async fn file_type(&self) -> std::io::Result<std::fs::FileType> {
self.0.file_type().await
}
pub async fn metadata(&self) -> std::io::Result<Metadata> {
Ok(Metadata(self.0.metadata().await?))
}
#[must_use]
pub const fn inner(&self) -> &tokio::fs::DirEntry {
&self.0
}
}
impl From<tokio::fs::DirEntry> for DirEntry {
fn from(entry: tokio::fs::DirEntry) -> Self {
Self(entry)
}
}
pub async fn read_dir<P: AsRef<Path>>(path: P) -> std::io::Result<ReadDir> {
Ok(ReadDir(tokio::fs::read_dir(path).await?))
}
pub async fn read<P: AsRef<Path>>(path: P) -> std::io::Result<Vec<u8>> {
::tokio::fs::read(path).await
}
pub async fn read_to_string<P: AsRef<Path>>(path: P) -> std::io::Result<String> {
::tokio::fs::read_to_string(path).await
}
pub async fn write<P: AsRef<Path>, C: AsRef<[u8]>>(
path: P,
contents: C,
) -> std::io::Result<()> {
::tokio::fs::write(path, contents).await
}
pub async fn create_dir<P: AsRef<Path>>(path: P) -> std::io::Result<()> {
tokio::fs::create_dir(path).await
}
pub async fn create_dir_all<P: AsRef<Path>>(path: P) -> std::io::Result<()> {
tokio::fs::create_dir_all(path).await
}
pub async fn remove_dir_all<P: AsRef<Path>>(path: P) -> std::io::Result<()> {
tokio::fs::remove_dir_all(path).await
}
pub async fn read_dir_sorted<P: AsRef<Path>>(path: P) -> std::io::Result<Vec<DirEntry>> {
let mut dir = ::tokio::fs::read_dir(path).await?;
let mut entries = Vec::new();
while let Some(entry) = dir.next_entry().await? {
entries.push(DirEntry(entry));
}
entries.sort_by_key(DirEntry::file_name);
Ok(entries)
}
pub async fn walk_dir_sorted<P: AsRef<Path>>(path: P) -> std::io::Result<Vec<DirEntry>> {
fn walk_recursive<'a>(
path: &'a Path,
entries: &'a mut Vec<DirEntry>,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = std::io::Result<()>> + Send + 'a>>
{
Box::pin(async move {
let mut dir = ::tokio::fs::read_dir(path).await?;
let mut dir_entries = Vec::new();
while let Some(entry) = dir.next_entry().await? {
dir_entries.push(DirEntry(entry));
}
dir_entries.sort_by_key(DirEntry::file_name);
for entry in dir_entries {
let path = entry.path();
entries.push(entry);
if path.is_dir() {
walk_recursive(&path, entries).await?;
}
}
Ok(())
})
}
let mut all_entries = Vec::new();
walk_recursive(path.as_ref(), &mut all_entries).await?;
all_entries.sort_by_key(DirEntry::path);
Ok(all_entries)
}
pub async fn exists<P: AsRef<Path>>(path: P) -> bool {
::tokio::fs::try_exists(path).await.unwrap_or(false)
}
pub async fn is_file<P: AsRef<Path>>(path: P) -> bool {
::tokio::fs::metadata(path).await.is_ok_and(|m| m.is_file())
}
pub async fn is_dir<P: AsRef<Path>>(path: P) -> bool {
::tokio::fs::metadata(path).await.is_ok_and(|m| m.is_dir())
}
pub async fn canonicalize<P: AsRef<Path>>(path: P) -> std::io::Result<std::path::PathBuf> {
::tokio::fs::canonicalize(path).await
}
impl From<OpenOptions> for tokio::fs::OpenOptions {
fn from(value: OpenOptions) -> Self {
let mut options = Self::new();
options
.create(value.create)
.append(value.append)
.read(value.read)
.write(value.write)
.truncate(value.truncate);
options
}
}
impl From<OpenOptions> for std::fs::OpenOptions {
fn from(value: OpenOptions) -> Self {
let mut options = Self::new();
options
.create(value.create)
.append(value.append)
.read(value.read)
.write(value.write)
.truncate(value.truncate);
options
}
}
#[cfg(not(feature = "simulator"))]
impl OpenOptions {
pub async fn open(self, path: impl AsRef<::std::path::Path>) -> ::std::io::Result<File> {
let options: tokio::fs::OpenOptions = self.into();
Ok(File(options.open(path).await?))
}
}
}