From 72983e41138426755c1e4500319440995ce9996c Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Thu, 9 Jun 2022 13:26:47 +1000 Subject: [PATCH] Use `untar` in `extract_compressed_from_readable` So that we can specify the files we want to extract to avoid io and save disk usage. Signed-off-by: Jiahao XU --- src/helpers.rs | 12 +++++++++--- src/helpers/async_file_writer.rs | 27 +++++++++++++++++++++++---- src/helpers/extracter.rs | 21 +++++++++------------ 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/src/helpers.rs b/src/helpers.rs index 3eac6bc6..614819ab 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,4 +1,5 @@ use std::{ + borrow::Cow, fs, io::{stderr, stdin, Write}, path::{Path, PathBuf}, @@ -53,14 +54,19 @@ pub async fn remote_exists(url: Url, method: Method) -> Result>(url: &str, path: P) -> Result<(), BinstallError> { let url = Url::parse(url)?; - download_and_extract(url, PkgFmt::Bin, path.as_ref()).await + download_and_extract::<_, 0>(url, PkgFmt::Bin, path.as_ref(), None).await } /// Download a file from the provided URL and extract it to the provided path -pub async fn download_and_extract>( +/// +/// * `desired_outputs - If Some(_) and `fmt` is not `PkgFmt::Bin` or +/// `PkgFmt::Zip`, then it will filter the tar and only extract files +/// specified in it. +pub async fn download_and_extract, const N: usize>( url: Url, fmt: PkgFmt, path: P, + desired_outputs: Option<[Cow<'static, Path>; N]>, ) -> Result<(), BinstallError> { debug!("Downloading from: '{url}'"); @@ -77,7 +83,7 @@ pub async fn download_and_extract>( debug!("Downloading to file: '{}'", path.display()); let mut bytes_stream = resp.bytes_stream(); - let mut writer = AsyncFileWriter::new(path, fmt); + let mut writer = AsyncFileWriter::new(path, fmt, desired_outputs); while let Some(res) = bytes_stream.next().await { writer.write(res?).await?; diff --git a/src/helpers/async_file_writer.rs b/src/helpers/async_file_writer.rs index ab90f8a3..755cde72 100644 --- a/src/helpers/async_file_writer.rs +++ b/src/helpers/async_file_writer.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::fs; use std::io::{self, Seek, Write}; use std::path::Path; @@ -27,7 +28,13 @@ struct AsyncFileWriterInner { } impl AsyncFileWriterInner { - fn new(path: &Path, fmt: PkgFmt) -> Self { + /// * `desired_outputs - If Some(_), then it will filter the tar + /// and only extract files specified in it. + fn new( + path: &Path, + fmt: PkgFmt, + desired_outputs: Option<[Cow<'static, Path>; N]>, + ) -> Self { let path = path.to_owned(); let (tx, rx) = mpsc::channel::(100); @@ -65,7 +72,12 @@ impl AsyncFileWriterInner { unzip(file, &path)?; } - _ => extract_compressed_from_readable(ReadableRx::new(&mut rx), fmt, &path)?, + _ => extract_compressed_from_readable( + ReadableRx::new(&mut rx), + fmt, + &path, + desired_outputs.as_ref().map(|arr| &arr[..]), + )?, } Ok(()) @@ -141,8 +153,15 @@ impl AsyncFileWriterInner { pub struct AsyncFileWriter(ScopeGuard); impl AsyncFileWriter { - pub fn new(path: &Path, fmt: PkgFmt) -> Self { - let inner = AsyncFileWriterInner::new(path, fmt); + /// * `desired_outputs - If Some(_) and `fmt` is not `PkgFmt::Bin` or + /// `PkgFmt::Zip`, then it will filter the tar and only extract files + /// specified in it. + pub fn new( + path: &Path, + fmt: PkgFmt, + desired_outputs: Option<[Cow<'static, Path>; N]>, + ) -> Self { + let inner = AsyncFileWriterInner::new(path, fmt, desired_outputs); Self(guard(inner, AsyncFileWriterInner::abort)) } diff --git a/src/helpers/extracter.rs b/src/helpers/extracter.rs index d0e2ea6e..41797210 100644 --- a/src/helpers/extracter.rs +++ b/src/helpers/extracter.rs @@ -12,6 +12,8 @@ use zstd::stream::Decoder as ZstdDecoder; use crate::{BinstallError, PkgFmt}; +/// * `desired_outputs - If Some(_), then it will filter the tar +/// and only extract files specified in it. fn untar( dat: impl Read, path: &Path, @@ -40,37 +42,34 @@ fn untar( /// Extract files from the specified source onto the specified path. /// /// * `fmt` - must not be `PkgFmt::Bin` or `PkgFmt::Zip`. +/// * `desired_outputs - If Some(_), then it will filter the tar +/// and only extract files specified in it. pub(crate) fn extract_compressed_from_readable( dat: impl Read, fmt: PkgFmt, path: &Path, + desired_outputs: Option<&[Cow<'_, Path>]>, ) -> Result<(), BinstallError> { match fmt { PkgFmt::Tar => { // Extract to install dir debug!("Extracting from tar archive to `{path:?}`"); - let mut tar = Archive::new(dat); - - tar.unpack(path)?; + untar(dat, path, desired_outputs)? } PkgFmt::Tgz => { // Extract to install dir debug!("Decompressing from tgz archive to `{path:?}`"); let tar = GzDecoder::new(dat); - let mut tgz = Archive::new(tar); - - tgz.unpack(path)?; + untar(tar, path, desired_outputs)?; } PkgFmt::Txz => { // Extract to install dir debug!("Decompressing from txz archive to `{path:?}`"); let tar = XzDecoder::new(dat); - let mut txz = Archive::new(tar); - - txz.unpack(path)?; + untar(tar, path, desired_outputs)?; } PkgFmt::Tzstd => { // Extract to install dir @@ -81,9 +80,7 @@ pub(crate) fn extract_compressed_from_readable( // as &[] by ZstdDecoder::new, thus ZstdDecoder::new // should not return any error. let tar = ZstdDecoder::new(dat)?; - let mut txz = Archive::new(tar); - - txz.unpack(path)?; + untar(tar, path, desired_outputs)?; } PkgFmt::Zip => panic!("Unexpected PkgFmt::Zip!"), PkgFmt::Bin => panic!("Unexpected PkgFmt::Bin!"),