use std::io::SeekFrom;
use std::path::{
Path,
PathBuf,
};
use std::pin::Pin;
use std::task::{
Context,
Poll,
};
use tokio::io::{
AsyncRead,
AsyncReadExt,
AsyncSeekExt,
AsyncWrite,
AsyncWriteExt,
ReadBuf,
};
use crate::{
FsError,
Operation,
};
pub struct AsyncFile {
inner: tokio::fs::File,
path: PathBuf,
}
impl AsyncFile {
pub async fn open(path: impl AsRef<Path>) -> Result<Self, FsError> {
let path = path.as_ref().to_owned();
let inner = tokio::fs::File::open(&path)
.await
.map_err(|error| FsError::io(Operation::Read, &path, error))?;
Ok(Self { inner, path })
}
pub async fn open_if_exists(path: impl AsRef<Path>) -> Result<Option<Self>, FsError> {
let path = path.as_ref().to_owned();
match tokio::fs::File::open(&path).await {
Ok(inner) => Ok(Some(Self { inner, path })),
Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(None),
Err(error) => Err(FsError::io(Operation::Read, &path, error)),
}
}
pub async fn create(path: impl AsRef<Path>) -> Result<Self, FsError> {
let path = path.as_ref().to_owned();
let inner = tokio::fs::File::create(&path)
.await
.map_err(|error| FsError::io(Operation::Write, &path, error))?;
Ok(Self { inner, path })
}
pub async fn create_new(path: impl AsRef<Path>) -> Result<Self, FsError> {
let path = path.as_ref().to_owned();
let inner = tokio::fs::OpenOptions::new()
.write(true)
.create_new(true)
.open(&path)
.await
.map_err(|error| FsError::io(Operation::Write, &path, error))?;
Ok(Self { inner, path })
}
pub async fn open_append(path: impl AsRef<Path>) -> Result<Self, FsError> {
let path = path.as_ref().to_owned();
let inner = tokio::fs::OpenOptions::new()
.create(true)
.append(true)
.read(true)
.open(&path)
.await
.map_err(|error| FsError::io(Operation::Write, &path, error))?;
Ok(Self { inner, path })
}
pub async fn open_write_at_end(path: impl AsRef<Path>) -> Result<Self, FsError> {
let path = path.as_ref().to_owned();
let mut inner = tokio::fs::OpenOptions::new()
.create(true)
.read(true)
.write(true)
.truncate(false)
.open(&path)
.await
.map_err(|error| FsError::io(Operation::Write, &path, error))?;
inner
.seek(SeekFrom::End(0))
.await
.map_err(|error| FsError::io(Operation::Write, &path, error))?;
Ok(Self { inner, path })
}
pub async fn read_exact(&mut self, buffer: &mut [u8]) -> Result<(), FsError> {
self.inner
.read_exact(buffer)
.await
.map(|_| ())
.map_err(|error| FsError::io(Operation::Read, &self.path, error))
}
pub async fn write_all(&mut self, buffer: &[u8]) -> Result<(), FsError> {
self.inner
.write_all(buffer)
.await
.map_err(|error| FsError::io(Operation::Write, &self.path, error))
}
pub async fn flush(&mut self) -> Result<(), FsError> {
self.inner
.flush()
.await
.map_err(|error| FsError::io(Operation::Write, &self.path, error))
}
pub async fn sync_all(&self) -> Result<(), FsError> {
self.inner
.sync_all()
.await
.map_err(|error| FsError::io(Operation::Write, &self.path, error))
}
pub async fn sync_data(&self) -> Result<(), FsError> {
self.inner
.sync_data()
.await
.map_err(|error| FsError::io(Operation::Write, &self.path, error))
}
pub async fn seek(&mut self, position: SeekFrom) -> Result<u64, FsError> {
self.inner
.seek(position)
.await
.map_err(|error| FsError::io(Operation::Read, &self.path, error))
}
pub async fn stream_position(&mut self) -> Result<u64, FsError> {
self.seek(SeekFrom::Current(0)).await
}
pub async fn metadata(&self) -> Result<std::fs::Metadata, FsError> {
self.inner
.metadata()
.await
.map_err(|error| FsError::io(Operation::Read, &self.path, error))
}
pub async fn set_len(&self, length: u64) -> Result<(), FsError> {
self.inner
.set_len(length)
.await
.map_err(|error| FsError::io(Operation::Write, &self.path, error))
}
#[must_use]
pub fn path(&self) -> &Path {
&self.path
}
}
impl AsyncRead for AsyncFile {
fn poll_read(
mut self: Pin<&mut Self>,
context: &mut Context<'_>,
buffer: &mut ReadBuf<'_>,
) -> Poll<std::io::Result<()>> {
Pin::new(&mut self.inner).poll_read(context, buffer)
}
}
impl AsyncWrite for AsyncFile {
fn poll_write(
mut self: Pin<&mut Self>,
context: &mut Context<'_>,
buffer: &[u8],
) -> Poll<std::io::Result<usize>> {
Pin::new(&mut self.inner).poll_write(context, buffer)
}
fn poll_flush(
mut self: Pin<&mut Self>,
context: &mut Context<'_>,
) -> Poll<std::io::Result<()>> {
Pin::new(&mut self.inner).poll_flush(context)
}
fn poll_shutdown(
mut self: Pin<&mut Self>,
context: &mut Context<'_>,
) -> Poll<std::io::Result<()>> {
Pin::new(&mut self.inner).poll_shutdown(context)
}
}