esthri 11.0.2

Extremely simple (memory stable) S3 client that supports get, put, head, list, and sync.
Documentation
use std::{
    io::SeekFrom,
    path::{Path, PathBuf},
};

use tokio::{
    fs::{File, OpenOptions},
    io::AsyncSeekExt,
    task,
};

use crate::Result;

pub struct TempFile {
    path: tempfile::TempPath,
    file: Option<File>,
}

pub const TEMP_FILE_PREFIX: &str = ".esthri_temp";

impl TempFile {
    pub async fn new(dir: PathBuf, suffix: Option<&str>) -> Result<Self> {
        let suffix = suffix.unwrap_or_default().to_owned();
        if !dir.exists() {
            tokio::fs::create_dir_all(&dir).await?;
        }
        let path = task::spawn_blocking(move || {
            let f = tempfile::Builder::new()
                .prefix(TEMP_FILE_PREFIX)
                .suffix(&suffix)
                .tempfile_in(dir)?;
            Result::Ok(f.into_temp_path())
        })
        .await??;
        let file = OpenOptions::new()
            .read(true)
            .write(true)
            .open(&path)
            .await?;
        Ok(Self {
            path,
            file: Some(file),
        })
    }

    pub async fn rewind(&mut self) -> Result<()> {
        self.file.as_mut().unwrap().seek(SeekFrom::Start(0)).await?;
        Ok(())
    }

    pub async fn persist(self, path: PathBuf) -> Result<()> {
        task::spawn_blocking(move || self.path.persist(path)).await??;
        Ok(())
    }

    pub fn file_mut(&mut self) -> &mut File {
        self.file.as_mut().unwrap()
    }

    pub fn take_file(&mut self) -> File {
        self.file.take().unwrap()
    }

    pub async fn take_std_file(&mut self) -> std::fs::File {
        self.file.take().unwrap().into_std().await
    }

    pub fn path(&self) -> &Path {
        &self.path
    }

    pub fn into_path(self) -> Box<dyn AsRef<Path>> {
        Box::new(self.path)
    }
}