use crate::crypto::DEFAULT_CHUNK_SIZE;
use crate::decompress::DecompressionResult;
use crate::error::Result;
use crate::progress::ProgressCallback;
use std::path::PathBuf;
pub struct DecompressionBuilder {
input_path: PathBuf,
password: Option<String>,
chunk_size: usize,
progress_callback: Option<ProgressCallback>,
output_path: Option<PathBuf>,
}
impl DecompressionBuilder {
pub fn new<P: Into<PathBuf>>(input_path: P) -> Self {
Self {
input_path: input_path.into(),
password: None,
chunk_size: DEFAULT_CHUNK_SIZE,
progress_callback: None,
output_path: None,
}
}
pub fn with_password<S: Into<String>>(mut self, password: S) -> Self {
self.password = Some(password.into());
self
}
pub fn with_chunk_size(mut self, chunk_size: usize) -> Self {
self.chunk_size = chunk_size;
self
}
pub fn with_progress_callback<F>(mut self, callback: F) -> Self
where
F: FnMut(&crate::progress::ProgressInfo) + Send + Sync + 'static,
{
self.progress_callback = Some(Box::new(callback));
self
}
pub fn with_output_path<P: Into<PathBuf>>(mut self, output_path: P) -> Self {
self.output_path = Some(output_path.into());
self
}
pub fn decompress(self) -> Result<DecompressionResult> {
crate::compress::validate_chunk_size(self.chunk_size)?;
crate::decompress::simple::decompress_file_with_progress(
&self.input_path,
self.password.as_deref(),
self.chunk_size,
self.progress_callback,
)
}
}
impl std::fmt::Debug for DecompressionBuilder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DecompressionBuilder")
.field("input_path", &self.input_path)
.field("password", &self.password.is_some())
.field("chunk_size", &self.chunk_size)
.field("progress_callback", &self.progress_callback.is_some())
.field("output_path", &self.output_path)
.finish()
}
}
impl From<&str> for DecompressionBuilder {
fn from(path: &str) -> Self {
Self::new(path)
}
}
impl From<String> for DecompressionBuilder {
fn from(path: String) -> Self {
Self::new(path)
}
}
impl From<PathBuf> for DecompressionBuilder {
fn from(path: PathBuf) -> Self {
Self::new(path)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_builder_creation() {
let builder = DecompressionBuilder::new("test.small");
assert_eq!(builder.input_path, PathBuf::from("test.small"));
assert!(builder.password.is_none());
assert_eq!(builder.chunk_size, DEFAULT_CHUNK_SIZE);
assert!(builder.progress_callback.is_none());
assert!(builder.output_path.is_none());
}
#[test]
fn test_builder_from_conversions() {
let builder1 = DecompressionBuilder::from("test.small");
assert_eq!(builder1.input_path, PathBuf::from("test.small"));
let builder2 = DecompressionBuilder::from("test.small".to_string());
assert_eq!(builder2.input_path, PathBuf::from("test.small"));
let path = PathBuf::from("test.small");
let builder3 = DecompressionBuilder::from(path.clone());
assert_eq!(builder3.input_path, path);
}
#[test]
fn test_builder_chain() {
let builder = DecompressionBuilder::new("test.small")
.with_password("secret")
.with_chunk_size(1024 * 1024)
.with_output_path("output.txt");
assert_eq!(builder.input_path, PathBuf::from("test.small"));
assert_eq!(builder.password, Some("secret".to_string()));
assert_eq!(builder.chunk_size, 1024 * 1024);
assert_eq!(builder.output_path, Some(PathBuf::from("output.txt")));
}
#[test]
fn test_invalid_chunk_size() {
let builder = DecompressionBuilder::new("test.small").with_chunk_size(32 * 1024);
let result = builder.decompress();
assert!(result.is_err());
match result.unwrap_err() {
crate::error::MismallError::Compression {
error: crate::error::CompressionError::InvalidChunkSize(size),
..
} => {
assert_eq!(size, 32 * 1024);
}
_ => panic!("Expected InvalidChunkSize error"),
}
}
}