aboutsummaryrefslogtreecommitdiffstats
path: root/src/archive.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/archive.rs')
-rw-r--r--src/archive.rs118
1 files changed, 60 insertions, 58 deletions
diff --git a/src/archive.rs b/src/archive.rs
index b62c3bd..ca22d28 100644
--- a/src/archive.rs
+++ b/src/archive.rs
@@ -1,8 +1,6 @@
use actix_web::http::ContentEncoding;
-use bytes::Bytes;
use libflate::gzip::Encoder;
use serde::Deserialize;
-use std::io;
use std::path::Path;
use strum_macros::{Display, EnumIter, EnumString};
use tar::Builder;
@@ -10,70 +8,83 @@ use tar::Builder;
use crate::errors::ContextualError;
/// Available compression methods
-#[derive(Deserialize, Clone, EnumIter, EnumString, Display)]
+#[derive(Deserialize, Clone, Copy, EnumIter, EnumString, Display)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum CompressionMethod {
- /// TAR GZ
+ /// Gzipped tarball
TarGz,
+
+ /// Regular tarball
+ Tar,
}
impl CompressionMethod {
- pub fn extension(&self) -> String {
- match &self {
+ pub fn extension(self) -> String {
+ match self {
CompressionMethod::TarGz => "tar.gz",
+ CompressionMethod::Tar => "tar",
}
.to_string()
}
- pub fn content_type(&self) -> String {
- match &self {
+ pub fn content_type(self) -> String {
+ match self {
CompressionMethod::TarGz => "application/gzip",
+ CompressionMethod::Tar => "application/tar",
}
.to_string()
}
- pub fn content_encoding(&self) -> ContentEncoding {
- match &self {
+ pub fn content_encoding(self) -> ContentEncoding {
+ match self {
CompressionMethod::TarGz => ContentEncoding::Gzip,
+ CompressionMethod::Tar => ContentEncoding::Identity,
}
}
- /// Creates an archive of a folder, using the algorithm the user chose from the web interface
- /// This method returns the archive as a stream of bytes
- pub fn create_archive<T: AsRef<Path>>(
- &self,
+
+ pub fn create_archive<T, W>(
+ self,
dir: T,
skip_symlinks: bool,
- ) -> Result<(String, Bytes), ContextualError> {
+ out: W,
+ ) -> Result<(), ContextualError>
+ where
+ T: AsRef<Path>,
+ W: std::io::Write,
+ {
+ let dir = dir.as_ref();
match self {
- CompressionMethod::TarGz => tgz_compress(dir, skip_symlinks),
+ CompressionMethod::TarGz => tar_gz(dir, skip_symlinks, out),
+ CompressionMethod::Tar => tar_dir(dir, skip_symlinks, out),
}
}
}
-/// Compresses a given folder in .tar.gz format, and returns the result as a stream of bytes
-fn tgz_compress<T: AsRef<Path>>(
- dir: T,
- skip_symlinks: bool,
-) -> Result<(String, Bytes), ContextualError> {
- if let Some(inner_folder) = dir.as_ref().file_name() {
- if let Some(directory) = inner_folder.to_str() {
- let dst_filename = format!("{}.tar", directory);
- let dst_tgz_filename = format!("{}.gz", dst_filename);
- let mut tgz_data = Bytes::new();
+fn tar_gz<W>(dir: &Path, skip_symlinks: bool, out: W) -> Result<(), ContextualError>
+where
+ W: std::io::Write,
+{
+ let mut out = Encoder::new(out).map_err(|e| ContextualError::IOError("GZIP".to_string(), e))?;
- let tar_data =
- tar(dir.as_ref(), directory.to_string(), skip_symlinks).map_err(|e| {
- ContextualError::ArchiveCreationError("tarball".to_string(), Box::new(e))
- })?;
+ tar_dir(dir, skip_symlinks, &mut out)?;
- let gz_data = gzip(&tar_data).map_err(|e| {
- ContextualError::ArchiveCreationError("GZIP archive".to_string(), Box::new(e))
- })?;
+ out.finish()
+ .into_result()
+ .map_err(|e| ContextualError::IOError("GZIP finish".to_string(), e))?;
- tgz_data.extend_from_slice(&gz_data);
+ Ok(())
+}
- Ok((dst_tgz_filename, tgz_data))
+fn tar_dir<W>(dir: &Path, skip_symlinks: bool, out: W) -> Result<(), ContextualError>
+where
+ W: std::io::Write,
+{
+ if let Some(inner_folder) = dir.file_name() {
+ if let Some(directory) = inner_folder.to_str() {
+ tar(dir, directory.to_string(), skip_symlinks, out).map_err(|e| {
+ ContextualError::ArchiveCreationError("tarball".to_string(), Box::new(e))
+ })
} else {
// https://doc.rust-lang.org/std/ffi/struct.OsStr.html#method.to_str
Err(ContextualError::InvalidPathError(
@@ -88,45 +99,36 @@ fn tgz_compress<T: AsRef<Path>>(
}
}
-/// Creates a TAR archive of a folder, and returns it as a stream of bytes
-fn tar<T: AsRef<Path>>(
- src_dir: T,
+fn tar<W>(
+ src_dir: &Path,
inner_folder: String,
skip_symlinks: bool,
-) -> Result<Vec<u8>, ContextualError> {
- let mut tar_builder = Builder::new(Vec::new());
+ out: W,
+) -> Result<(), ContextualError>
+where
+ W: std::io::Write,
+{
+ let mut tar_builder = Builder::new(out);
tar_builder.follow_symlinks(!skip_symlinks);
+
// Recursively adds the content of src_dir into the archive stream
tar_builder
- .append_dir_all(inner_folder, src_dir.as_ref())
+ .append_dir_all(inner_folder, src_dir)
.map_err(|e| {
ContextualError::IOError(
format!(
"Failed to append the content of {} to the TAR archive",
- src_dir.as_ref().to_str().unwrap_or("file")
+ src_dir.to_str().unwrap_or("file")
),
e,
)
})?;
- let tar_content = tar_builder.into_inner().map_err(|e| {
+ // Finish the archive
+ tar_builder.into_inner().map_err(|e| {
ContextualError::IOError("Failed to finish writing the TAR archive".to_string(), e)
})?;
- Ok(tar_content)
-}
-
-/// Compresses a stream of bytes using the GZIP algorithm, and returns the resulting stream
-fn gzip(mut data: &[u8]) -> Result<Vec<u8>, ContextualError> {
- let mut encoder = Encoder::new(Vec::new())
- .map_err(|e| ContextualError::IOError("Failed to create GZIP encoder".to_string(), e))?;
- io::copy(&mut data, &mut encoder)
- .map_err(|e| ContextualError::IOError("Failed to write GZIP data".to_string(), e))?;
- let data = encoder
- .finish()
- .into_result()
- .map_err(|e| ContextualError::IOError("Failed to write GZIP trailer".to_string(), e))?;
-
- Ok(data)
+ Ok(())
}