use core::mem;
use core::ops::Deref;
use core::pin::Pin;
use core::task::{Context, Poll};
use std::path::{Path, PathBuf};
use tokio::{
fs,
io::{self, AsyncRead, AsyncWrite, ReadBuf},
};
#[derive(Debug)]
pub struct TempPath(PathBuf);
impl TempPath {
pub async fn persist<P: AsRef<Path>>(
mut self,
new_path: P,
) -> Result<(), io::Error> {
let path = mem::replace(&mut self.0, PathBuf::new());
mem::forget(self);
fs::rename(path, new_path).await
}
}
impl Deref for TempPath {
type Target = Path;
fn deref(&self) -> &Path {
&self.0
}
}
impl AsRef<Path> for TempPath {
fn as_ref(&self) -> &Path {
&self.0
}
}
impl Drop for TempPath {
fn drop(&mut self) {
let _ = std::fs::remove_file(&self.0);
}
}
pub struct NamedTempFile {
path: TempPath,
file: fs::File,
}
impl NamedTempFile {
pub async fn new<P>(temp_path: P) -> Result<Self, io::Error>
where
P: AsRef<Path> + Send + 'static,
{
let path = TempPath(temp_path.as_ref().to_owned());
let file = fs::File::create(temp_path).await?;
Ok(NamedTempFile { path, file })
}
pub fn into_parts(self) -> (fs::File, TempPath) {
(self.file, self.path)
}
pub async fn persist<P: AsRef<Path>>(
self,
new_path: P,
) -> Result<fs::File, io::Error> {
let (file, path) = self.into_parts();
path.persist(new_path).await?;
Ok(file)
}
}
impl AsRef<Path> for NamedTempFile {
fn as_ref(&self) -> &Path {
&self.path
}
}
impl AsRef<fs::File> for NamedTempFile {
fn as_ref(&self) -> &fs::File {
&self.file
}
}
impl AsMut<fs::File> for NamedTempFile {
fn as_mut(&mut self) -> &mut fs::File {
&mut self.file
}
}
impl AsyncRead for NamedTempFile {
#[inline]
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
Pin::new(&mut self.file).poll_read(cx, buf)
}
}
impl AsyncWrite for NamedTempFile {
#[inline]
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context,
buf: &[u8],
) -> Poll<Result<usize, io::Error>> {
Pin::new(&mut self.file).poll_write(cx, buf)
}
#[inline]
fn poll_flush(
mut self: Pin<&mut Self>,
cx: &mut Context,
) -> Poll<Result<(), io::Error>> {
Pin::new(&mut self.file).poll_flush(cx)
}
#[inline]
fn poll_shutdown(
mut self: Pin<&mut Self>,
cx: &mut Context,
) -> Poll<Result<(), io::Error>> {
Pin::new(&mut self.file).poll_shutdown(cx)
}
}