aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--Cargo.lock1
-rw-r--r--Cargo.toml3
-rw-r--r--src/archive.rs82
-rw-r--r--src/errors.rs90
-rw-r--r--src/listing.rs44
-rw-r--r--src/main.rs1
6 files changed, 171 insertions, 50 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 7bfbd62..6016cf5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -777,6 +777,7 @@ dependencies = [
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono-humanize 0.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
"htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libflate 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/Cargo.toml b/Cargo.toml
index 5f2fbcd..ad5a144 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -41,4 +41,5 @@ tar = "0.4"
tempfile = "3.0.7"
bytes = "0.4.12"
futures = "0.1.25"
-libflate = "0.1.20" \ No newline at end of file
+libflate = "0.1.20"
+failure = "0.1.5" \ No newline at end of file
diff --git a/src/archive.rs b/src/archive.rs
index bc3a1c8..afbcc6b 100644
--- a/src/archive.rs
+++ b/src/archive.rs
@@ -1,5 +1,6 @@
use actix_web::http::ContentEncoding;
use bytes::Bytes;
+use failure::ResultExt;
use libflate::gzip::Encoder;
use serde::Deserialize;
use std::fs::{File, OpenOptions};
@@ -7,6 +8,9 @@ use std::io::{self, Read};
use std::path::PathBuf;
use tar::Builder;
use tempfile::tempdir;
+use yansi::Color;
+
+use crate::errors;
/// Available compression methods
#[derive(Debug, Deserialize, Clone)]
@@ -45,43 +49,25 @@ impl CompressionMethod {
}
}
-/// Possible errors
-#[derive(Debug)]
-pub enum CompressionError {
- IOError(std::io::Error),
- NoneError(std::option::NoneError),
-}
-
-impl From<std::option::NoneError> for CompressionError {
- fn from(err: std::option::NoneError) -> CompressionError {
- CompressionError::NoneError(err)
- }
-}
-
-impl From<std::io::Error> for CompressionError {
- fn from(err: std::io::Error) -> CompressionError {
- CompressionError::IOError(err)
- }
-}
-
pub fn create_archive_file(
method: &CompressionMethod,
dir: &PathBuf,
-) -> Result<(String, Bytes), CompressionError> {
+) -> Result<(String, Bytes), errors::CompressionError> {
match method {
CompressionMethod::TarGz => tgz_compress(&dir),
}
}
/// Compresses a given folder in .tar.gz format
-fn tgz_compress(dir: &PathBuf) -> Result<(String, Bytes), CompressionError> {
+fn tgz_compress(dir: &PathBuf) -> Result<(String, Bytes), errors::CompressionError> {
let src_dir = dir.display().to_string();
let inner_folder = dir.file_name()?.to_str()?;
let dst_filename = format!("{}.tar", inner_folder);
let dst_tgz_filename = format!("{}.gz", dst_filename);
- let tar_content = tar(src_dir, dst_filename, inner_folder.to_string())?;
- let gz_data = gzip(&tar_content)?;
+ let tar_content = tar(src_dir, dst_filename, inner_folder.to_string())
+ .context(errors::CompressionErrorKind::TarContentError)?;
+ let gz_data = gzip(&tar_content).context(errors::CompressionErrorKind::GZipContentError)?;
let mut data = Bytes::new();
data.extend_from_slice(&gz_data);
@@ -94,10 +80,13 @@ fn tar(
src_dir: String,
dst_filename: String,
inner_folder: String,
-) -> Result<Vec<u8>, CompressionError> {
- let tmp_dir = tempdir()?;
+) -> Result<Vec<u8>, errors::CompressionError> {
+ let tmp_dir = tempdir().context(errors::CompressionErrorKind::CreateTemporaryFileError)?;
let dst_filepath = tmp_dir.path().join(dst_filename.clone());
- let tar_file = File::create(&dst_filepath)?;
+ let tar_file =
+ File::create(&dst_filepath).context(errors::CompressionErrorKind::CreateFileError {
+ path: color_path(&dst_filepath.display().to_string()),
+ })?;
// Create a TAR file of src_dir
let mut tar_builder = Builder::new(&tar_file);
@@ -106,22 +95,47 @@ fn tar(
// https://github.com/alexcrichton/tar-rs/issues/147
// https://github.com/alexcrichton/tar-rs/issues/174
tar_builder.follow_symlinks(false);
- tar_builder.append_dir_all(inner_folder, src_dir)?;
- tar_builder.into_inner()?;
+ tar_builder.append_dir_all(inner_folder, &src_dir).context(
+ errors::CompressionErrorKind::TarBuildingError {
+ message: format!(
+ "failed to append the content of {} in the TAR archive",
+ color_path(&src_dir)
+ ),
+ },
+ )?;
+ tar_builder
+ .into_inner()
+ .context(errors::CompressionErrorKind::TarBuildingError {
+ message: "failed to finish writing the TAR archive".to_string(),
+ })?;
// Read the content of the TAR file and store it as bytes
- let mut tar_file = OpenOptions::new().read(true).open(&dst_filepath)?;
+ let mut tar_file = OpenOptions::new().read(true).open(&dst_filepath).context(
+ errors::CompressionErrorKind::OpenFileError {
+ path: color_path(&dst_filepath.display().to_string()),
+ },
+ )?;
let mut tar_content = Vec::new();
- tar_file.read_to_end(&mut tar_content)?;
+ tar_file
+ .read_to_end(&mut tar_content)
+ .context(errors::CompressionErrorKind::TarContentError)?;
Ok(tar_content)
}
/// Compresses a stream of bytes using the GZIP algorithm
-fn gzip(mut data: &[u8]) -> Result<Vec<u8>, CompressionError> {
- let mut encoder = Encoder::new(Vec::new())?;
- io::copy(&mut data, &mut encoder)?;
- let data = encoder.finish().into_result()?;
+fn gzip(mut data: &[u8]) -> Result<Vec<u8>, errors::CompressionError> {
+ let mut encoder =
+ Encoder::new(Vec::new()).context(errors::CompressionErrorKind::GZipBuildingError)?;
+ io::copy(&mut data, &mut encoder).context(errors::CompressionErrorKind::GZipBuildingError)?;
+ let data = encoder
+ .finish()
+ .into_result()
+ .context(errors::CompressionErrorKind::GZipBuildingError)?;
Ok(data)
}
+
+fn color_path(path: &str) -> String {
+ Color::White.paint(path).bold().to_string()
+}
diff --git a/src/errors.rs b/src/errors.rs
new file mode 100644
index 0000000..c85d123
--- /dev/null
+++ b/src/errors.rs
@@ -0,0 +1,90 @@
+use failure::{Backtrace, Context, Fail};
+use std::fmt::{self, Debug, Display};
+use yansi::Color;
+
+/// Kinds of error which might happen during folder archive generation
+#[derive(Clone, Debug, PartialEq, Eq, Fail)]
+pub enum CompressionErrorKind {
+ #[fail(display = "Could not open file {}", path)]
+ OpenFileError { path: String },
+ #[fail(display = "Could not create temporary file")]
+ CreateTemporaryFileError,
+ #[fail(display = "Could not create file {}", path)]
+ CreateFileError { path: String },
+ #[fail(display = "Could not retrieve entity name from the given path.
+ This can either mean that the entity has non UTF-8 characters in its name,
+ or that its name ends with \"..\"")]
+ InvalidDirectoryName,
+ #[fail(display = "Failed to create the TAR archive: {}", message)]
+ TarBuildingError { message: String },
+ #[fail(display = "Failed to create the GZIP archive")]
+ GZipBuildingError,
+ #[fail(display = "Failed to retrieve TAR content")]
+ TarContentError,
+ #[fail(display = "Failed to retrieve GZIP content")]
+ GZipContentError,
+}
+
+pub fn print_chain(err: CompressionError) {
+ for cause in Fail::iter_causes(&err) {
+ println!(
+ "{} {}",
+ Color::Magenta.paint("Caused by:").to_string(),
+ cause
+ );
+ }
+}
+
+pub struct CompressionError {
+ inner: Context<CompressionErrorKind>,
+}
+
+impl CompressionError {
+ fn new(kind: CompressionErrorKind) -> CompressionError {
+ CompressionError {
+ inner: Context::new(kind),
+ }
+ }
+}
+
+impl Fail for CompressionError {
+ fn cause(&self) -> Option<&Fail> {
+ self.inner.cause()
+ }
+
+ fn backtrace(&self) -> Option<&Backtrace> {
+ self.inner.backtrace()
+ }
+}
+
+impl Display for CompressionError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ Display::fmt(&self.inner, f)
+ }
+}
+
+impl Debug for CompressionError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ Debug::fmt(&self.inner, f)
+ }
+}
+
+impl From<Context<CompressionErrorKind>> for CompressionError {
+ fn from(inner: Context<CompressionErrorKind>) -> CompressionError {
+ CompressionError { inner }
+ }
+}
+
+impl From<CompressionErrorKind> for CompressionError {
+ fn from(kind: CompressionErrorKind) -> CompressionError {
+ CompressionError {
+ inner: Context::new(kind),
+ }
+ }
+}
+
+impl From<std::option::NoneError> for CompressionError {
+ fn from(_: std::option::NoneError) -> CompressionError {
+ CompressionError::new(CompressionErrorKind::InvalidDirectoryName)
+ }
+}
diff --git a/src/listing.rs b/src/listing.rs
index 88dab40..ab3b28b 100644
--- a/src/listing.rs
+++ b/src/listing.rs
@@ -7,9 +7,10 @@ use serde::Deserialize;
use std::io;
use std::path::Path;
use std::time::SystemTime;
-use yansi::Paint;
+use yansi::{Color, Paint};
use crate::archive;
+use crate::errors;
use crate::renderer;
/// Query parameters
@@ -228,24 +229,37 @@ pub fn directory_listing<S>(
}
if let Some(compression_method) = &download {
+ println!(
+ "{info} Creating an archive ({extension}) of {path}...",
+ info = Color::Blue.paint("info:").bold(),
+ extension = Color::White.paint(compression_method.extension()).bold(),
+ path = Color::White.paint(&dir.path.display().to_string()).bold()
+ );
match archive::create_archive_file(&compression_method, &dir.path) {
- Ok((filename, content)) => Ok(HttpResponse::Ok()
- .content_type(compression_method.content_type())
- .content_length(content.len() as u64)
- .content_encoding(compression_method.content_encoding())
- .header("Content-Transfer-Encoding", "binary")
- .header(
- "Content-Disposition",
- format!("attachment; filename={:?}", filename),
- )
- .chunked()
- .body(Body::Streaming(Box::new(once(Ok(content)))))),
- Err(_) => {
+ Ok((filename, content)) => {
+ println!(
+ "{success} Archive successfully created !",
+ success = Color::Green.paint("success:").bold()
+ );
+ Ok(HttpResponse::Ok()
+ .content_type(compression_method.content_type())
+ .content_length(content.len() as u64)
+ .content_encoding(compression_method.content_encoding())
+ .header("Content-Transfer-Encoding", "binary")
+ .header(
+ "Content-Disposition",
+ format!("attachment; filename={:?}", filename),
+ )
+ .chunked()
+ .body(Body::Streaming(Box::new(once(Ok(content))))))
+ }
+ Err(err) => {
println!(
- "{error} an error occured while compressing {folder}",
+ "{error} {err}",
error = Paint::red("error:").bold(),
- folder = dir.path.display(),
+ err = Paint::white(&err).bold()
);
+ errors::print_chain(err);
Ok(HttpResponse::Ok()
.status(http::StatusCode::INTERNAL_SERVER_ERROR)
.body(""))
diff --git a/src/main.rs b/src/main.rs
index 1bddd6d..59389b1 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -15,6 +15,7 @@ mod args;
mod auth;
mod listing;
mod renderer;
+mod errors;
#[derive(Clone, Debug)]
/// Configuration of the Miniserve application