Skip to content

Commit 6d5ec7d

Browse files
committed
refactor: refactor zonky extractor to use generic tar_xz_extractor
1 parent 5853ef5 commit 6d5ec7d

File tree

5 files changed

+93
-64
lines changed

5 files changed

+93
-64
lines changed

examples/zonky/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use postgresql_embedded::{PostgreSQL, Result, Settings};
99
async fn main() -> Result<()> {
1010
let settings = Settings {
1111
releases_url: zonky::URL.to_string(),
12-
version: VersionReq::parse("=16.2.0")?,
12+
version: VersionReq::parse("=16.3.0")?,
1313
..Default::default()
1414
};
1515
let mut postgresql = PostgreSQL::new(settings);

postgresql_archive/Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ thiserror = { workspace = true }
3636
tokio = { workspace = true, features = ["full"], optional = true }
3737
tracing = { workspace = true, features = ["log"] }
3838
url = { workspace = true }
39-
xz2 = { workspace = true, optional = true }
39+
xz2 = { workspace = true }
4040
zip = { workspace = true }
4141

4242
[dev-dependencies]
@@ -71,7 +71,6 @@ theseus = [
7171
"sha2",
7272
]
7373
zonky = [
74-
"dep:xz2",
7574
"maven",
7675
]
7776

postgresql_archive/src/configuration/zonky/extractor.rs

Lines changed: 8 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
1-
use crate::extractor::ExtractDirectories;
1+
use crate::extractor::{tar_xz_extract, ExtractDirectories};
22
use crate::Error::Unexpected;
33
use crate::Result;
4-
use human_bytes::human_bytes;
5-
use num_format::{Locale, ToFormattedString};
6-
use std::fs::{create_dir_all, remove_dir_all, remove_file, rename, File};
7-
use std::io::{copy, BufReader, Cursor};
4+
use regex::Regex;
5+
use std::fs::{create_dir_all, remove_dir_all, remove_file, rename};
6+
use std::io::Cursor;
87
use std::path::{Path, PathBuf};
98
use std::thread::sleep;
109
use std::time::Duration;
11-
use tar::Archive;
1210
use tracing::{debug, instrument, warn};
13-
use xz2::bufread::XzDecoder;
1411
use zip::ZipArchive;
1512

1613
/// Extracts the compressed tar `bytes` to the [out_dir](Path).
@@ -22,7 +19,6 @@ use zip::ZipArchive;
2219
#[instrument(skip(bytes))]
2320
pub fn extract(bytes: &Vec<u8>, extract_directories: ExtractDirectories) -> Result<Vec<PathBuf>> {
2421
let out_dir = extract_directories.get_path(".")?;
25-
let mut files = Vec::new();
2622
let parent_dir = if let Some(parent) = out_dir.parent() {
2723
parent
2824
} else {
@@ -41,7 +37,7 @@ pub fn extract(bytes: &Vec<u8>, extract_directories: ExtractDirectories) -> Resu
4137
out_dir.to_string_lossy()
4238
);
4339
remove_file(&lock_file)?;
44-
return Ok(files);
40+
return Ok(Vec::new());
4541
}
4642

4743
let extract_dir = tempfile::tempdir_in(parent_dir)?.into_path();
@@ -66,51 +62,9 @@ pub fn extract(bytes: &Vec<u8>, extract_directories: ExtractDirectories) -> Resu
6662
return Err(Unexpected("Failed to find archive file".to_string()));
6763
}
6864

69-
let input = BufReader::new(Cursor::new(archive_bytes));
70-
let decoder = XzDecoder::new(input);
71-
let mut archive = Archive::new(decoder);
72-
let mut extracted_bytes = 0;
73-
74-
for archive_entry in archive.entries()? {
75-
let mut entry = archive_entry?;
76-
let entry_header = entry.header();
77-
let entry_type = entry_header.entry_type();
78-
let entry_size = entry_header.size()?;
79-
#[cfg(unix)]
80-
let file_mode = entry_header.mode()?;
81-
82-
let entry_header_path = entry_header.path()?.to_path_buf();
83-
let mut entry_name = extract_dir.clone();
84-
entry_name.push(entry_header_path);
85-
86-
if let Some(parent) = entry_name.parent() {
87-
if !parent.exists() {
88-
create_dir_all(parent)?;
89-
}
90-
}
91-
92-
if entry_type.is_dir() || entry_name.is_dir() {
93-
create_dir_all(&entry_name)?;
94-
} else if entry_type.is_file() {
95-
let mut output_file = File::create(&entry_name)?;
96-
copy(&mut entry, &mut output_file)?;
97-
extracted_bytes += entry_size;
98-
99-
#[cfg(unix)]
100-
{
101-
use std::os::unix::fs::PermissionsExt;
102-
output_file.set_permissions(std::fs::Permissions::from_mode(file_mode))?;
103-
}
104-
files.push(entry_name);
105-
} else if entry_type.is_symlink() {
106-
#[cfg(unix)]
107-
if let Some(symlink_target) = entry.link_name()? {
108-
let symlink_path = entry_name.clone();
109-
std::os::unix::fs::symlink(symlink_target.as_ref(), symlink_path)?;
110-
files.push(entry_name);
111-
}
112-
}
113-
}
65+
let mut archive_extract_directories = ExtractDirectories::default();
66+
archive_extract_directories.add_mapping(Regex::new(".*")?, extract_dir.clone());
67+
let files = tar_xz_extract(&archive_bytes, archive_extract_directories)?;
11468

11569
if out_dir.exists() {
11670
debug!(
@@ -133,13 +87,6 @@ pub fn extract(bytes: &Vec<u8>, extract_directories: ExtractDirectories) -> Resu
13387
remove_file(lock_file)?;
13488
}
13589

136-
let number_of_files = files.len();
137-
debug!(
138-
"Extracting {} files totalling {}",
139-
number_of_files.to_formatted_string(&Locale::en),
140-
human_bytes(extracted_bytes as f64)
141-
);
142-
14390
Ok(files)
14491
}
14592

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
mod model;
22
pub mod registry;
33
mod tar_gz_extractor;
4+
mod tar_xz_extractor;
45
mod zip_extractor;
56

67
pub use model::ExtractDirectories;
78
pub use tar_gz_extractor::extract as tar_gz_extract;
9+
pub use tar_xz_extractor::extract as tar_xz_extract;
810
pub use zip_extractor::extract as zip_extract;
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
use crate::extractor::ExtractDirectories;
2+
use crate::Error::Unexpected;
3+
use crate::Result;
4+
use human_bytes::human_bytes;
5+
use num_format::{Locale, ToFormattedString};
6+
use std::fs::{create_dir_all, File};
7+
use std::io::{copy, BufReader, Cursor};
8+
use std::path::PathBuf;
9+
use tar::Archive;
10+
use tracing::{debug, instrument, warn};
11+
use xz2::bufread::XzDecoder;
12+
13+
/// Extracts the compressed tar `bytes` to paths defined in `extract_directories`.
14+
///
15+
/// # Errors
16+
/// Returns an error if the extraction fails.
17+
#[allow(clippy::cast_precision_loss)]
18+
#[instrument(skip(bytes))]
19+
pub fn extract(bytes: &Vec<u8>, extract_directories: ExtractDirectories) -> Result<Vec<PathBuf>> {
20+
let mut files = Vec::new();
21+
let input = BufReader::new(Cursor::new(bytes));
22+
let decoder = XzDecoder::new(input);
23+
let mut archive = Archive::new(decoder);
24+
let mut extracted_bytes = 0;
25+
26+
for archive_entry in archive.entries()? {
27+
let mut entry = archive_entry?;
28+
let entry_header = entry.header();
29+
let entry_type = entry_header.entry_type();
30+
let entry_size = entry_header.size()?;
31+
#[cfg(unix)]
32+
let file_mode = entry_header.mode()?;
33+
34+
let entry_header_path = entry_header.path()?.to_path_buf();
35+
let prefix = match entry_header_path.components().next() {
36+
Some(component) => component.as_os_str().to_str().unwrap_or_default(),
37+
None => {
38+
return Err(Unexpected(
39+
"Failed to get file header path prefix".to_string(),
40+
));
41+
}
42+
};
43+
let stripped_entry_header_path = entry_header_path.strip_prefix(prefix)?.to_path_buf();
44+
let Ok(extract_dir) = extract_directories.get_path(prefix) else {
45+
continue;
46+
};
47+
let mut entry_name = extract_dir.clone();
48+
entry_name.push(stripped_entry_header_path);
49+
50+
if entry_type.is_dir() || entry_name.is_dir() {
51+
create_dir_all(&entry_name)?;
52+
} else if entry_type.is_file() {
53+
let mut output_file = File::create(&entry_name)?;
54+
copy(&mut entry, &mut output_file)?;
55+
extracted_bytes += entry_size;
56+
57+
#[cfg(unix)]
58+
{
59+
use std::os::unix::fs::PermissionsExt;
60+
output_file.set_permissions(std::fs::Permissions::from_mode(file_mode))?;
61+
}
62+
files.push(entry_name);
63+
} else if entry_type.is_symlink() {
64+
#[cfg(unix)]
65+
if let Some(symlink_target) = entry.link_name()? {
66+
let symlink_path = entry_name.clone();
67+
std::os::unix::fs::symlink(symlink_target.as_ref(), symlink_path)?;
68+
files.push(entry_name);
69+
}
70+
}
71+
}
72+
73+
let number_of_files = files.len();
74+
debug!(
75+
"Extracted {} files totalling {}",
76+
number_of_files.to_formatted_string(&Locale::en),
77+
human_bytes(extracted_bytes as f64)
78+
);
79+
80+
Ok(files)
81+
}

0 commit comments

Comments
 (0)