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;
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());
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(())
}