diff --git a/src/helpers.rs b/src/helpers.rs index 5d6d350a..55089076 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,18 +1,16 @@ -use std::{ - path::{Path, PathBuf}, -}; +use std::path::{Path, PathBuf}; use cargo_toml::Manifest; use log::debug; -use reqwest::Method; +use reqwest::{Method, Response}; use serde::Serialize; use tinytemplate::TinyTemplate; use url::Url; -use crate::{BinstallError, Meta, PkgFmt}; +use crate::{BinstallError, Meta, PkgFmt, PkgFmtDecomposed}; mod async_extracter; -pub use async_extracter::extract_archive_stream; +pub use async_extracter::*; mod auto_abort_join_handle; pub use auto_abort_join_handle::AutoAbortJoinHandle; @@ -45,13 +43,41 @@ pub async fn remote_exists(url: Url, method: Method) -> Result Result { + reqwest::get(url.clone()) + .await + .and_then(|r| r.error_for_status()) + .map_err(|err| BinstallError::Http { + method: Method::GET, + url, + err, + }) +} + /// Download a file from the provided URL and extract it to the provided path. pub async fn download_and_extract>( url: Url, fmt: PkgFmt, path: P, ) -> Result<(), BinstallError> { - download_and_extract_with_filter:: bool, _>(url, fmt, path.as_ref(), None).await + debug!("Downloading from: '{url}'"); + + let resp = create_request(url).await?; + + let path = path.as_ref(); + debug!("Downloading to file: '{}'", path.display()); + + let stream = resp.bytes_stream(); + + match fmt.decompose() { + PkgFmtDecomposed::Tar(fmt) => extract_tar_based_stream(stream, path, fmt).await?, + PkgFmtDecomposed::Bin => extract_bin(stream, path).await?, + PkgFmtDecomposed::Zip => extract_zip(stream, path).await?, + } + + debug!("Download OK, written to file: '{}'", path.display()); + + Ok(()) } /// Download a file from the provided URL and extract part of it to @@ -72,19 +98,20 @@ pub async fn download_and_extract_with_filter< ) -> Result<(), BinstallError> { debug!("Downloading from: '{url}'"); - let resp = reqwest::get(url.clone()) - .await - .and_then(|r| r.error_for_status()) - .map_err(|err| BinstallError::Http { - method: Method::GET, - url, - err, - })?; + let resp = create_request(url).await?; let path = path.as_ref(); debug!("Downloading to file: '{}'", path.display()); - extract_archive_stream(resp.bytes_stream(), path, fmt, filter).await?; + let stream = resp.bytes_stream(); + + match fmt.decompose() { + PkgFmtDecomposed::Tar(fmt) => { + extract_tar_based_stream_with_filter(stream, path, fmt, filter).await? + } + PkgFmtDecomposed::Bin => extract_bin(stream, path).await?, + PkgFmtDecomposed::Zip => extract_zip(stream, path).await?, + } debug!("Download OK, written to file: '{}'", path.display()); diff --git a/src/helpers/async_extracter.rs b/src/helpers/async_extracter.rs index 6286eddd..7d0b4542 100644 --- a/src/helpers/async_extracter.rs +++ b/src/helpers/async_extracter.rs @@ -199,24 +199,70 @@ impl AsyncExtracter { } } -/// * `output` - If `fmt` is `PkgFmt::Bin`, then this is the filename -/// for the bin. -/// Otherwise, it is the directory where the extracted content will be put. -/// * `fmt` - The format of the archive to feed in. -/// * `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 bool + Send + 'static, E>( +pub async fn extract_bin( mut stream: impl Stream> + Unpin, output: &Path, - fmt: PkgFmt, - filter: Option, ) -> Result<(), BinstallError> where BinstallError: From, { - let mut extracter = AsyncExtracter::new(output, fmt, filter); + let mut extracter = AsyncExtracter::new:: bool>(output, PkgFmt::Bin, None); + + while let Some(res) = stream.next().await { + extracter.feed(res?).await?; + } + + extracter.done().await +} + +pub async fn extract_zip( + mut stream: impl Stream> + Unpin, + output: &Path, +) -> Result<(), BinstallError> +where + BinstallError: From, +{ + let mut extracter = AsyncExtracter::new:: bool>(output, PkgFmt::Zip, None); + + while let Some(res) = stream.next().await { + extracter.feed(res?).await?; + } + + extracter.done().await +} + +/// * `filter` - If Some, then it will pass the path of the file to it +/// and only extract ones which filter returns `true`. +pub async fn extract_tar_based_stream_with_filter< + Filter: FnMut(&Path) -> bool + Send + 'static, + E, +>( + mut stream: impl Stream> + Unpin, + output: &Path, + fmt: TarBasedFmt, + filter: Option, +) -> Result<(), BinstallError> +where + BinstallError: From, +{ + let mut extracter = AsyncExtracter::new(output, fmt.into(), filter); + + while let Some(res) = stream.next().await { + extracter.feed(res?).await?; + } + + extracter.done().await +} + +pub async fn extract_tar_based_stream( + mut stream: impl Stream> + Unpin, + output: &Path, + fmt: TarBasedFmt, +) -> Result<(), BinstallError> +where + BinstallError: From, +{ + let mut extracter = AsyncExtracter::new:: bool>(output, fmt.into(), None); while let Some(res) = stream.next().await { extracter.feed(res?).await?;