pg_embed/
pg_unpack.rs

1//!
2//! Unpack postgresql binaries
3//!
4use std::path::PathBuf;
5
6use archiver_rs::{Archive, Compressed};
7use futures::TryFutureExt;
8
9use crate::pg_errors::{PgEmbedError, PgEmbedErrorType};
10use crate::pg_types::PgResult;
11
12///
13/// Unzip the postgresql txz file
14///
15/// Returns `Ok(PathBuf(txz_file_path))` file path of the txz archive on success, otherwise returns an error.
16///
17fn unzip_txz(zip_file_path: &PathBuf, cache_dir: &PathBuf) -> Result<PathBuf, PgEmbedError> {
18    let mut zip = archiver_rs::Zip::open(zip_file_path.as_path()).map_err(|e| PgEmbedError {
19        error_type: PgEmbedErrorType::ReadFileError,
20        source: Some(Box::new(e)),
21        message: None,
22    })?;
23    let file_name = zip
24        .files()
25        .map_err(|e| PgEmbedError {
26            error_type: PgEmbedErrorType::UnpackFailure,
27            source: Some(Box::new(e)),
28            message: None,
29        })?
30        .into_iter()
31        .find(|name| name.ends_with(".txz"));
32    match file_name {
33        Some(file_name) => {
34            // decompress zip
35            let mut target_path = cache_dir.clone();
36            target_path.push(&file_name);
37            zip.extract_single(&target_path.as_path(), file_name.clone())
38                .map_err(|e| PgEmbedError {
39                    error_type: PgEmbedErrorType::UnpackFailure,
40                    source: Some(Box::new(e)),
41                    message: None,
42                })?;
43            Ok(target_path)
44        }
45        None => Err(PgEmbedError {
46            error_type: PgEmbedErrorType::InvalidPgPackage,
47            source: None,
48            message: Some(String::from("no postgresql txz in zip")),
49        }),
50    }
51}
52
53///
54/// Decompress the postgresql txz file
55///
56/// Returns `Ok(PathBuf(tar_file_path))` (*the file path to the postgresql tar file*) on success, otherwise returns an error.
57///
58fn decompress_xz(file_path: &PathBuf) -> Result<PathBuf, PgEmbedError> {
59    let mut xz = archiver_rs::Xz::open(file_path.as_path()).map_err(|e| PgEmbedError {
60        error_type: PgEmbedErrorType::ReadFileError,
61        source: Some(Box::new(e)),
62        message: None,
63    })?;
64    // rename file path suffix from .txz to .tar
65    let target_path = file_path.with_extension(".tar");
66    xz.decompress(&target_path.as_path())
67        .map_err(|e| PgEmbedError {
68            error_type: PgEmbedErrorType::UnpackFailure,
69            source: Some(Box::new(e)),
70            message: None,
71        })?;
72    Ok(target_path)
73}
74
75///
76/// Unpack the postgresql tar file
77///
78/// Returns `Ok(())` on success, otherwise returns an error.
79///
80fn decompress_tar(file_path: &PathBuf, cache_dir: &PathBuf) -> Result<(), PgEmbedError> {
81    let mut tar = archiver_rs::Tar::open(&file_path.as_path()).map_err(|e| PgEmbedError {
82        error_type: PgEmbedErrorType::ReadFileError,
83        source: Some(Box::new(e)),
84        message: None,
85    })?;
86
87    tar.extract(cache_dir.as_path()).map_err(|e| PgEmbedError {
88        error_type: PgEmbedErrorType::UnpackFailure,
89        source: Some(Box::new(e)),
90        message: None,
91    })?;
92
93    Ok(())
94}
95
96///
97/// Unpack the postgresql executables
98///
99/// Returns `Ok(())` on success, otherwise returns an error.
100///
101pub async fn unpack_postgres(zip_file_path: &PathBuf, cache_dir: &PathBuf) -> PgResult<()> {
102    let txz_file_path = unzip_txz(&zip_file_path, &cache_dir)?;
103    let tar_file_path = decompress_xz(&txz_file_path)?;
104    tokio::fs::remove_file(txz_file_path)
105        .map_err(|e| PgEmbedError {
106            error_type: PgEmbedErrorType::PgCleanUpFailure,
107            source: Some(Box::new(e)),
108            message: None,
109        })
110        .await?;
111    let _ = decompress_tar(&tar_file_path, &cache_dir);
112    tokio::fs::remove_file(tar_file_path)
113        .map_err(|e| PgEmbedError {
114            error_type: PgEmbedErrorType::PgCleanUpFailure,
115            source: Some(Box::new(e)),
116            message: None,
117        })
118        .await?;
119    Ok(())
120}