From 1d139324c7a1f5a4a43c80821c20186b9951f6c0 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Fri, 10 Jun 2022 16:35:51 +1000 Subject: [PATCH] Rewrite `untar`: Takes a `filter` fn instead of array Signed-off-by: Jiahao XU --- src/drivers.rs | 9 ++++++-- src/fetchers/gh_crate_meta.rs | 2 +- src/fetchers/quickinstall.rs | 3 ++- src/helpers.rs | 14 ++++++------ src/helpers/async_extracter.rs | 40 ++++++++++++++++------------------ src/helpers/extracter.rs | 35 +++++++++++++++-------------- 6 files changed, 55 insertions(+), 48 deletions(-) diff --git a/src/drivers.rs b/src/drivers.rs index 355ce3f7..9c36b051 100644 --- a/src/drivers.rs +++ b/src/drivers.rs @@ -102,14 +102,19 @@ pub async fn fetch_crate_cratesio( debug!("Fetching crate from: {crate_url} and extracting Cargo.toml from it"); - let crate_dir = format!("{name}-{version_name}"); + let crate_dir: PathBuf = format!("{name}-{version_name}").into(); let crate_path = temp_dir.join(&crate_dir); + let cargo_toml = crate_dir.join("Cargo.toml"); + let src = crate_dir.join("src"); + let main = src.join("main.rs"); + let bin = src.join("bin"); + download_and_extract( Url::parse(&crate_url)?, PkgFmt::Tgz, &temp_dir, - Some([Path::new(&crate_dir).join("Cargo.toml").into()]), + Some(move |path: &Path| path == cargo_toml || path == main || path.starts_with(&bin)), ) .await?; diff --git a/src/fetchers/gh_crate_meta.rs b/src/fetchers/gh_crate_meta.rs index c6ed669b..bc4575e6 100644 --- a/src/fetchers/gh_crate_meta.rs +++ b/src/fetchers/gh_crate_meta.rs @@ -43,7 +43,7 @@ impl super::Fetcher for GhCrateMeta { async fn fetch_and_extract(&self, dst: &Path) -> Result<(), BinstallError> { let url = self.url()?; info!("Downloading package from: '{url}'"); - download_and_extract::<_, 0>(url, self.pkg_fmt(), dst, None).await + download_and_extract:: bool, _>(url, self.pkg_fmt(), dst, None).await } fn pkg_fmt(&self) -> PkgFmt { diff --git a/src/fetchers/quickinstall.rs b/src/fetchers/quickinstall.rs index f048de03..7d597d15 100644 --- a/src/fetchers/quickinstall.rs +++ b/src/fetchers/quickinstall.rs @@ -40,7 +40,8 @@ impl super::Fetcher for QuickInstall { async fn fetch_and_extract(&self, dst: &Path) -> Result<(), BinstallError> { let url = self.package_url(); info!("Downloading package from: '{url}'"); - download_and_extract::<_, 0>(Url::parse(&url)?, self.pkg_fmt(), dst, None).await + download_and_extract:: bool, _>(Url::parse(&url)?, self.pkg_fmt(), dst, None) + .await } fn pkg_fmt(&self) -> PkgFmt { diff --git a/src/helpers.rs b/src/helpers.rs index da0a54c0..d81bb775 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,5 +1,4 @@ use std::{ - borrow::Cow, io::{stderr, stdin, Write}, path::{Path, PathBuf}, }; @@ -46,14 +45,15 @@ pub async fn remote_exists(url: Url, method: Method) -> Result, const N: usize>( +/// * `filter` - If Some, then it will pass the path of the file to it +/// and only extract ones which filter returns `true`. +/// Note that this is a best-effort and it only works when `fmt` +/// is not `PkgFmt::Bin` or `PkgFmt::Zip`. +pub async fn download_and_extract bool + Send + 'static, P: AsRef>( url: Url, fmt: PkgFmt, path: P, - desired_outputs: Option<[Cow<'static, Path>; N]>, + filter: Option, ) -> Result<(), BinstallError> { debug!("Downloading from: '{url}'"); @@ -69,7 +69,7 @@ pub async fn download_and_extract, const N: usize>( let path = path.as_ref(); debug!("Downloading to file: '{}'", path.display()); - extract_archive_stream(resp.bytes_stream(), path, fmt, desired_outputs).await?; + extract_archive_stream(resp.bytes_stream(), path, fmt, filter).await?; debug!("Download OK, written to file: '{}'", path.display()); diff --git a/src/helpers/async_extracter.rs b/src/helpers/async_extracter.rs index 4eed3276..0b399ab4 100644 --- a/src/helpers/async_extracter.rs +++ b/src/helpers/async_extracter.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use std::fs; use std::io::{self, Seek, Write}; use std::path::Path; @@ -29,12 +28,14 @@ struct AsyncExtracterInner { } impl AsyncExtracterInner { - /// * `desired_outputs - If Some(_), then it will filter the tar - /// and only extract files specified in it. - fn new( + /// * `filter` - If Some, then it will pass the path of the file to it + /// and only extract ones which filter returns `true`. + /// Note that this is a best-effort and it only works when `fmt` + /// is not `PkgFmt::Bin` or `PkgFmt::Zip`. + fn new bool + Send + 'static>( path: &Path, fmt: PkgFmt, - desired_outputs: Option<[Cow<'static, Path>; N]>, + filter: Option, ) -> Self { let path = path.to_owned(); let (tx, rx) = mpsc::channel::(100); @@ -73,12 +74,9 @@ impl AsyncExtracterInner { unzip(file, &path)?; } - _ => extract_compressed_from_readable( - ReadableRx::new(&mut rx), - fmt, - &path, - desired_outputs.as_ref().map(|arr| &arr[..]), - )?, + _ => { + extract_compressed_from_readable(ReadableRx::new(&mut rx), fmt, &path, filter)? + } } Ok(()) @@ -181,16 +179,16 @@ impl AsyncExtracter { /// for the bin. /// Otherwise, it is the directory where the extracted content will be put. /// * `fmt` - The format of the archive to feed in. - /// * `desired_outputs - If Some(_), then it will filter the tar and - /// only extract files specified in it. + /// * `filter` - If Some, then it will pass the path of the file to it + /// and only extract ones which filter returns `true`. /// Note that this is a best-effort and it only works when `fmt` /// is not `PkgFmt::Bin` or `PkgFmt::Zip`. - fn new( + fn new bool + Send + 'static>( path: &Path, fmt: PkgFmt, - desired_outputs: Option<[Cow<'static, Path>; N]>, + filter: Option, ) -> Self { - let inner = AsyncExtracterInner::new(path, fmt, desired_outputs); + let inner = AsyncExtracterInner::new(path, fmt, filter); Self(guard(inner, AsyncExtracterInner::abort)) } @@ -209,20 +207,20 @@ impl AsyncExtracter { /// for the bin. /// Otherwise, it is the directory where the extracted content will be put. /// * `fmt` - The format of the archive to feed in. -/// * `desired_outputs - If Some(_), then it will filter the tar and -/// only extract files specified in it. +/// * `filter` - If Some, then it will pass the path of the file to it +/// and only extract ones which filter returns `true`. /// Note that this is a best-effort and it only works when `fmt` /// is not `PkgFmt::Bin` or `PkgFmt::Zip`. -pub async fn extract_archive_stream( +pub async fn extract_archive_stream bool + Send + 'static, E>( mut stream: impl Stream> + Unpin, output: &Path, fmt: PkgFmt, - desired_outputs: Option<[Cow<'static, Path>; N]>, + filter: Option, ) -> Result<(), BinstallError> where BinstallError: From, { - let mut extracter = AsyncExtracter::new(output, fmt, desired_outputs); + let mut extracter = AsyncExtracter::new(output, fmt, filter); while let Some(res) = stream.next().await { extracter.feed(res?).await?; diff --git a/src/helpers/extracter.rs b/src/helpers/extracter.rs index dd5831f4..fbb9d5c0 100644 --- a/src/helpers/extracter.rs +++ b/src/helpers/extracter.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use std::fs::{self, File}; use std::io::Read; use std::path::Path; @@ -12,23 +11,25 @@ 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( +/// * `filter` - If Some, then it will pass the path of the file to it +/// and only extract ones which filter returns `true`. +/// Note that this is a best-effort and it only works when `fmt` +/// is not `PkgFmt::Bin` or `PkgFmt::Zip`. +fn untar bool>( dat: impl Read, path: &Path, - desired_outputs: Option<&[Cow<'_, Path>]>, + filter: Option, ) -> Result<(), BinstallError> { let mut tar = Archive::new(dat); - if let Some(desired_outputs) = desired_outputs { - debug!("Untaring only {desired_outputs:#?}"); + if let Some(mut filter) = filter { + debug!("Untaring with filter"); for res in tar.entries()? { let mut entry = res?; let entry_path = entry.path()?; - if desired_outputs.contains(&entry_path) { + if filter(&entry_path) { debug!("Extracting {entry_path:#?}"); let dst = path.join(entry_path); @@ -49,34 +50,36 @@ 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( +/// * `filter` - If Some, then it will pass the path of the file to it +/// and only extract ones which filter returns `true`. +/// Note that this is a best-effort and it only works when `fmt` +/// is not `PkgFmt::Bin` or `PkgFmt::Zip`. +pub(crate) fn extract_compressed_from_readable bool>( dat: impl Read, fmt: PkgFmt, path: &Path, - desired_outputs: Option<&[Cow<'_, Path>]>, + filter: Option, ) -> Result<(), BinstallError> { match fmt { PkgFmt::Tar => { // Extract to install dir debug!("Extracting from tar archive to `{path:?}`"); - untar(dat, path, desired_outputs)? + untar(dat, path, filter)? } PkgFmt::Tgz => { // Extract to install dir debug!("Decompressing from tgz archive to `{path:?}`"); let tar = GzDecoder::new(dat); - untar(tar, path, desired_outputs)?; + untar(tar, path, filter)?; } PkgFmt::Txz => { // Extract to install dir debug!("Decompressing from txz archive to `{path:?}`"); let tar = XzDecoder::new(dat); - untar(tar, path, desired_outputs)?; + untar(tar, path, filter)?; } PkgFmt::Tzstd => { // Extract to install dir @@ -87,7 +90,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)?; - untar(tar, path, desired_outputs)?; + untar(tar, path, filter)?; } PkgFmt::Zip => panic!("Unexpected PkgFmt::Zip!"), PkgFmt::Bin => panic!("Unexpected PkgFmt::Bin!"),