cobble-core 1.2.0

Library for managing, installing and launching Minecraft instances and more.
Documentation
use crate::error::InstallationResult;
use crate::minecraft::install::LibraryInstallationUpdate;
use crate::minecraft::InstallationUpdate;
use async_zip::read::seek::ZipFileReader;
use std::path::{Path, PathBuf};
use tokio::fs::{create_dir_all, File};
use tokio::sync::mpsc::Sender;

// #[instrument(
//     name = "extract_native",
//     level = "trace",
//     skip_all,
//     fields(
//         path = %path.as_ref().to_string_lossy(),
//         destination = %destination.as_ref().to_string_lossy(),
//         excludes,
//     )
// )]
pub async fn extract_native(
    path: impl AsRef<Path> + Send,
    destination: impl AsRef<Path> + Send,
    excludes: Vec<String>,
    mut update_sender: Option<Sender<InstallationUpdate>>,
) -> InstallationResult<()> {
    trace!("Opening jar archive");
    let file_path = path.as_ref();
    let mut file = File::open(file_path).await?;
    let mut archive = ZipFileReader::new(&mut file).await?;

    trace!("Running through jar archive entries");

    'files: for i in 0..archive.entries().len() {
        if let Some(s) = &update_sender {
            trace!("Sending progress");

            let name = path
                .as_ref()
                .file_name()
                .map(|s| s.to_string_lossy().to_string())
                .unwrap_or_default();

            if s.send(InstallationUpdate::Library((
                name,
                LibraryInstallationUpdate::Extracting,
            )))
            .await
            .is_err()
            {
                trace!("Sending failed because receiver is no longer around. Dropping sender...");
                update_sender = None;
            }
        }

        let entry_reader = archive.entry_reader(i).await?;
        let entry = entry_reader.entry();

        let file_path = PathBuf::from(entry.filename());

        // Check excludes
        for exclude in &excludes {
            if file_path.starts_with(exclude) {
                trace!("Skip extracting file '{}'", file_path.to_string_lossy());
                continue 'files;
            }
        }

        let mut out_path = PathBuf::from(destination.as_ref());
        out_path.push(file_path);

        if out_path.is_file() {
            trace!("File '{}' already exists", out_path.to_string_lossy());
            continue 'files;
        }

        trace!("Extracting file to '{}'", out_path.to_string_lossy());
        if let Some(parent) = out_path.parent() {
            create_dir_all(parent).await?;
        }
        let mut out_file = File::create(&out_path).await?;
        entry_reader.copy_to_end_crc(&mut out_file, 64000).await?;
    }

    Ok(())
}