use std::{io::Cursor, path::Path};
use anyhow::Result;
use flate2::read::GzDecoder;
use reqwest::Client;
use tar::Archive;
pub async fn download_and_extract(
client: &Client,
archive_url: &str,
dest: &Path,
subdir: Option<&str>,
) -> Result<()> {
let bytes = client
.get(archive_url)
.header("User-Agent", "anesis")
.send()
.await?
.error_for_status()?
.bytes()
.await?;
std::fs::create_dir_all(dest)?;
let gz = GzDecoder::new(Cursor::new(bytes));
let mut archive = Archive::new(gz);
for entry in archive.entries()? {
let mut entry = entry?;
let raw_path = entry.path()?.into_owned();
let mut components = raw_path.components();
components.next(); let stripped = components.as_path();
let rel = if let Some(dir) = subdir {
match stripped.strip_prefix(dir) {
Ok(r) => r.to_owned(),
Err(_) => continue,
}
} else {
stripped.to_owned()
};
if rel.as_os_str().is_empty() {
continue; }
let out_path = dest.join(&rel);
if let Some(parent) = out_path.parent() {
std::fs::create_dir_all(parent)?;
}
entry.unpack(&out_path)?;
}
Ok(())
}
#[doc(hidden)]
pub fn strip_archive_path_for_tests(
raw_path: &std::path::Path,
subdir: Option<&str>,
) -> Option<std::path::PathBuf> {
let mut components = raw_path.components();
components.next(); let stripped = components.as_path();
let rel: std::path::PathBuf = if let Some(dir) = subdir {
match stripped.strip_prefix(dir) {
Ok(r) => r.to_owned(),
Err(_) => return None,
}
} else {
stripped.to_owned()
};
if rel.as_os_str().is_empty() {
return None;
}
Some(rel)
}