idt/downloaders/http/
deflate.rs1use std::fs::File;
2use std::io::BufReader;
3use std::io::Read;
4use std::path::Path;
5use std::path::PathBuf;
6
7use flate2::read::GzDecoder;
8use rootcause::prelude::ResultExt;
9use tar::Archive;
10use xz2::read::XzDecoder;
11
12pub enum HttpDeflateOption<'a> {
13 ExtractTarGz {
14 dest_dir: &'a Path,
15 dest_name: Option<&'a str>,
20 },
21 ExtractTarXz {
22 dest_dir: &'a Path,
23 dest_name: Option<&'a str>,
24 },
25 ExtractZip {
26 dest_dir: &'a Path,
27 dest_name: Option<&'a str>,
28 },
29 WriteTo {
30 dest_path: &'a Path,
31 },
32}
33
34impl HttpDeflateOption<'_> {
35 pub fn process(&self, tmp_file: &Path) -> rootcause::Result<PathBuf> {
40 match self {
41 Self::ExtractTarGz { dest_dir, dest_name } => {
42 let input = File::open(tmp_file)
43 .context("error opening tmp file for tar.gz extraction")
44 .attach_with(|| format!("path={}", tmp_file.display()))?;
45 let decoder = GzDecoder::new(input);
46 let archive = Archive::new(decoder);
47
48 Ok(extract_tar(archive, tmp_file, dest_dir, *dest_name)?)
49 }
50 Self::ExtractTarXz { dest_dir, dest_name } => {
51 let input = File::open(tmp_file)
52 .context("error opening tmp file for tar.xz extraction")
53 .attach_with(|| format!("path={}", tmp_file.display()))?;
54 let decoder = XzDecoder::new(input);
55 let archive = Archive::new(decoder);
56
57 Ok(extract_tar(archive, tmp_file, dest_dir, *dest_name)?)
58 }
59 Self::ExtractZip { dest_dir, dest_name } => {
60 let input = File::open(tmp_file)
61 .context("error opening tmp file for zip extraction")
62 .attach_with(|| format!("path={}", tmp_file.display()))?;
63 let reader = BufReader::new(input);
64 let mut archive = zip::ZipArchive::new(reader)
65 .context("error reading zip archive")
66 .attach_with(|| format!("path={}", tmp_file.display()))?;
67
68 if let Some(dest_name) = dest_name {
69 let mut entry = archive
70 .by_name(dest_name)
71 .context("error finding entry in zip archive")
72 .attach_with(|| format!("path={}", tmp_file.display()))
73 .attach_with(|| format!("entry={dest_name}"))?;
74 let dest_path = dest_dir.join(dest_name);
75 let mut dest = File::create(&dest_path)
76 .context("error creating dest file for zip entry")
77 .attach_with(|| format!("path={}", dest_path.display()))?;
78 std::io::copy(&mut entry, &mut dest)
79 .context("error extracting zip entry")
80 .attach_with(|| format!("path={}", tmp_file.display()))
81 .attach_with(|| format!("entry={dest_name}"))?;
82
83 Ok(dest_path)
84 } else {
85 archive
86 .extract(dest_dir)
87 .context("error extracting zip archive")
88 .attach_with(|| format!("path={}", tmp_file.display()))
89 .attach_with(|| format!("dest_dir={}", dest_dir.display()))?;
90
91 Ok(dest_dir.into())
92 }
93 }
94 Self::WriteTo { dest_path } => {
95 std::fs::copy(tmp_file, dest_path)
97 .context("error copying tmp file to dest")
98 .attach_with(|| format!("src={}", tmp_file.display()))
99 .attach_with(|| format!("dest={}", dest_path.display()))?;
100
101 Ok(dest_path.into())
102 }
103 }
104 }
105}
106
107pub struct ChecksumSource<'a> {
109 pub checksums_url: &'a str,
111 pub filename: &'a str,
113}
114
115fn extract_tar<R: Read>(
118 mut archive: Archive<R>,
119 archive_path: &Path,
120 dest_dir: &Path,
121 dest_name: Option<&str>,
122) -> rootcause::Result<PathBuf> {
123 if let Some(dest_name) = dest_name {
124 for entry in archive
125 .entries()
126 .context("error reading tar entries")
127 .attach_with(|| format!("path={}", archive_path.display()))?
128 {
129 let mut entry = entry
130 .context("error reading tar entry")
131 .attach_with(|| format!("entry={dest_name}"))?;
132 let entry_path = entry
133 .path()
134 .context("error reading tar entry path")
135 .attach_with(|| format!("entry={dest_name}"))?;
136 if entry_path.to_str() == Some(dest_name) {
137 let dest_path = dest_dir.join(dest_name);
138 entry
139 .unpack(&dest_path)
140 .context("error extracting tar entry")
141 .attach_with(|| format!("entry={dest_name}"))?;
142 return Ok(dest_path);
143 }
144 }
145 Err(rootcause::report!("entry not found in tar archive")).attach_with(|| format!("entry={dest_name}"))
146 } else {
147 archive
148 .unpack(dest_dir)
149 .context("error extracting tar archive")
150 .attach_with(|| format!("dest_dir={}", dest_dir.display()))?;
151 Ok(dest_dir.into())
152 }
153}