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