oxide_cli/utils/
archive.rs1use std::{io::Cursor, path::Path};
2
3use anyhow::Result;
4use flate2::read::GzDecoder;
5use reqwest::Client;
6use tar::Archive;
7
8pub async fn download_and_extract(
13 client: &Client,
14 archive_url: &str,
15 dest: &Path,
16 subdir: Option<&str>,
17) -> Result<()> {
18 let bytes = client
19 .get(archive_url)
20 .header("User-Agent", "oxide")
21 .send()
22 .await?
23 .error_for_status()?
24 .bytes()
25 .await?;
26
27 std::fs::create_dir_all(dest)?;
28
29 let gz = GzDecoder::new(Cursor::new(bytes));
30 let mut archive = Archive::new(gz);
31
32 for entry in archive.entries()? {
33 let mut entry = entry?;
34 let raw_path = entry.path()?.into_owned();
35
36 let mut components = raw_path.components();
39 components.next(); let stripped = components.as_path();
41
42 let rel = if let Some(dir) = subdir {
45 match stripped.strip_prefix(dir) {
46 Ok(r) => r.to_owned(),
47 Err(_) => continue,
48 }
49 } else {
50 stripped.to_owned()
51 };
52
53 if rel.as_os_str().is_empty() {
54 continue; }
56
57 let out_path = dest.join(&rel);
58 if let Some(parent) = out_path.parent() {
59 std::fs::create_dir_all(parent)?;
60 }
61 entry.unpack(&out_path)?;
62 }
63
64 Ok(())
65}