downlowd 0.1.1

Download files with automatic retries and resumes.
use std::{
    fs::{self, Metadata},
    io::{self, Seek, SeekFrom, Write},
    path::{Path, PathBuf},
};

use crate::{Error, maybe_async, utils};

pub struct StdFile {
    path: PathBuf,
    inner: std::fs::File,
}

impl StdFile {
    fn convert_error(&self, err: io::Error, action: &'static str) -> Error {
        Error::Write {
            action,
            path: self.path.clone(),
            cause: err,
        }
    }
}

impl maybe_async::File for StdFile {
    async fn open_for_writing(path: &Path) -> Result<Self, Error> {
        let inner = utils::file::open_file_for_writing(path)?;
        Ok(Self {
            path: path.to_owned(),
            inner,
        })
    }

    async fn read_to_string(path: &Path) -> Result<String, std::io::Error> {
        fs::read_to_string(path)
    }

    async fn metadata(path: &Path) -> Result<Metadata, Error> {
        path.metadata().map_err(|e| Error::Write {
            action: "getting file metadata",
            path: path.to_path_buf(),
            cause: e,
        })
    }

    async fn remove_file(path: &std::path::Path) -> Result<(), Error> {
        fs::remove_file(path).map_err(|e| Error::Write {
            action: "removing file",
            path: path.to_path_buf(),
            cause: e,
        })
    }

    async fn get_length(&self) -> Result<u64, Error> {
        self.inner
            .metadata()
            .map_err(|e| self.convert_error(e, "reading length"))
            .map(|m| m.len())
    }

    async fn sync_all(&mut self) -> Result<(), Error> {
        self.inner
            .sync_all()
            .map_err(|err| self.convert_error(err, "sync_all"))
    }

    async fn rename(&mut self, path: &Path) -> Result<(), Error> {
        fs::rename(&self.path, path).map_err(|err| self.convert_error(err, "renaming file"))
    }

    async fn truncate(&mut self) -> Result<(), Error> {
        self.inner
            .set_len(0)
            .map_err(|err| self.convert_error(err, "truncating file"))?;
        self.inner
            .seek(SeekFrom::Start(0))
            .map_err(|err| self.convert_error(err, "seeking to start of file"))?;
        Ok(())
    }

    async fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> Result<(), Error> {
        self.inner
            .write_all(buf)
            .map_err(|err| self.convert_error(err, "writing to file"))
    }
}