moonup 0.5.2

Manage multiple MoonBit installations
Documentation
use miette::IntoDiagnostic;
use rattler_digest::{HashingReader, Sha256, Sha256Hash};
use std::fs::File;
use std::io::{self, BufReader, Read, copy};
use std::path::Path;
use tokio::io::AsyncRead;
use tokio_util::io::SyncIoBridge;

/// Remove given `path` recursively.
///
/// This is a wrapper around `remove_dir_all` from the `remove_dir_all`
/// crate that provides a more reliable implementation of removing directories.
///
/// # Returns
///
/// Returns `Ok(())` if the given `path` does not exist.
#[inline(always)]
pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
    match remove_dir_all::remove_dir_all(path) {
        Ok(_) => Ok(()),
        Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(()),
        Err(err) => Err(err),
    }
}

/// Remove all files and subdirectories in given `path`.
///
/// This function will not remove the given `path` itself.
///
/// # Returns
///
/// Returns `Ok(())` if the given `path` does not exist.
#[inline(always)]
pub fn empty_dir<P: AsRef<Path> + ?Sized>(path: &P) -> io::Result<()> {
    match remove_dir_all::remove_dir_contents(path) {
        Ok(_) => Ok(()),
        Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(()),
        Err(err) => Err(err),
    }
}

fn save_file_sync(stream: impl Read, destination: &Path) -> miette::Result<Sha256Hash> {
    std::fs::create_dir_all(destination.parent().expect("invalid destination"))
        .into_diagnostic()?;

    let mut file = File::create(destination).into_diagnostic()?;
    let mut sha256_reader = HashingReader::<_, Sha256>::new(BufReader::new(stream));

    copy(&mut sha256_reader, &mut file).into_diagnostic()?;

    let (_, sha256) = sha256_reader.finalize();

    Ok(sha256)
}

fn compute_file_sha256_sync(path: &Path) -> miette::Result<Sha256Hash> {
    let file = File::open(path).into_diagnostic()?;
    let mut sha256_reader = HashingReader::<_, Sha256>::new(BufReader::new(file));

    copy(&mut sha256_reader, &mut io::sink()).into_diagnostic()?;

    let (_, sha256) = sha256_reader.finalize();
    Ok(sha256)
}

pub async fn save_file(
    reader: impl AsyncRead + Send + 'static,
    destination: &Path,
) -> miette::Result<Sha256Hash> {
    // Create a async -> sync bridge
    let reader = SyncIoBridge::new(Box::pin(reader));

    let destination = destination.to_owned();
    match tokio::task::spawn_blocking(move || save_file_sync(reader, &destination)).await {
        Ok(result) => result,
        Err(err) => Err(err).into_diagnostic(),
    }
}

/// Compute the SHA256 hash of the file at the given `path`.
pub async fn compute_file_sha256(path: &Path) -> miette::Result<Sha256Hash> {
    let path = path.to_owned();
    match tokio::task::spawn_blocking(move || compute_file_sha256_sync(&path)).await {
        Ok(result) => result,
        Err(err) => Err(err).into_diagnostic(),
    }
}