Archive Module Phase 1-4完成(2916行代码,Upload Service集成)
Phase 1-3(2916行): - Phase 1: 核心框架(900行)- ProcessorRegistry, FormatDetector, ArchiveConfig - Phase 2: 核心处理器(1332行)- ZIP, TAR, GZIP, TAR.GZ完整实现 - Phase 3: 可选格式(312行)- RAR, XZ, 7z(默认禁用,法律/稳定性警告) Phase 4(230行): - Upload Service集成Archive Module - 自动检测压缩格式并解压 - 提取文件注册到数据库(file_registry, file_locations, file_nodes) - JSON响应包含extracted字段(count, bytes, directory) 核心修改: - server.rs: extract_and_register_archive函数(150行) - server.rs: upload_file自动解压逻辑(80行) - Cargo.toml: tempfile依赖移到dependencies - ArchiveProcessor trait: 所有方法改为&mut self - ZipProcessor: 解决ZipArchive borrow冲突 - TarProcessor: 修复entry可变引用问题 - ProcessorRegistry: 添加get_processor_mut方法 编译修复:16→0错误(45分钟) - Trait方法签名统一 - ZipArchive borrow checker问题解决 - TarProcessor entry可变引用修复 - Trait object lifetime bound修复 支持格式(12种): - 核心4种:ZIP, TAR, GZIP, TAR.GZ(已实现) - 可选3种:RAR, XZ, 7z(已实现,默认禁用) - 扩展5种:ZSTD, BZIP2, LZ4, TAR.BZ2, TAR.ZST(stub)
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
use anyhow::Result;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::Path;
|
||||
use log::warn;
|
||||
|
||||
/// Archive Configuration
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
||||
@@ -83,7 +83,7 @@ impl ArchiveEntry {
|
||||
}
|
||||
|
||||
/// Extract Result - Summary of Extraction Operation
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ExtractResult {
|
||||
pub total_files: u64,
|
||||
pub total_bytes: u64,
|
||||
|
||||
@@ -18,11 +18,16 @@ pub mod processors {
|
||||
#[cfg(test)]
|
||||
pub mod tests;
|
||||
|
||||
// Re-export public types
|
||||
pub use config::ArchiveConfig;
|
||||
pub use detector::FormatDetector;
|
||||
pub use metadata::{ArchiveEntry, ArchiveMetadata, ExtractResult};
|
||||
pub use processor::{ArchiveFormat, ArchiveProcessor};
|
||||
|
||||
use anyhow::Result;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::archive::{ArchiveFormat, ArchiveProcessor, FormatDetector, ArchiveConfig};
|
||||
use log::{info, warn};
|
||||
|
||||
/// Processor Registry - Plugin Architecture
|
||||
pub struct ProcessorRegistry {
|
||||
@@ -103,7 +108,18 @@ impl ProcessorRegistry {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get processor for detected format
|
||||
/// Get processor for detected format (mutable version for open/extraction)
|
||||
pub fn get_processor_mut(&mut self, path: &Path) -> Result<&mut (dyn ArchiveProcessor + '_)> {
|
||||
let detector = FormatDetector::new();
|
||||
let format = detector.detect(path)?;
|
||||
|
||||
match self.processors.get_mut(&format) {
|
||||
Some(p) => Ok(p.as_mut()),
|
||||
None => Err(anyhow::anyhow!("Format {} not supported or not enabled", format)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get processor for detected format (immutable version for listing)
|
||||
pub fn get_processor(&self, path: &Path) -> Result<&dyn ArchiveProcessor> {
|
||||
let detector = FormatDetector::new();
|
||||
let format = detector.detect(path)?;
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
|
||||
use anyhow::Result;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::SystemTime;
|
||||
|
||||
// Re-export types from metadata.rs
|
||||
pub use crate::archive::metadata::{ArchiveMetadata, ArchiveEntry, ExtractResult};
|
||||
|
||||
/// Archive Format Type Enumeration
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub enum ArchiveFormat {
|
||||
// Core formats (always enabled)
|
||||
Zip,
|
||||
@@ -56,13 +58,13 @@ pub trait ArchiveProcessor: Send + Sync {
|
||||
fn open(&mut self, path: &Path) -> Result<ArchiveMetadata>;
|
||||
|
||||
/// List all file entries in archive
|
||||
fn list_entries(&self) -> Result<Vec<ArchiveEntry>>;
|
||||
fn list_entries(&mut self) -> Result<Vec<ArchiveEntry>>;
|
||||
|
||||
/// Extract single file (on-demand decompression)
|
||||
fn extract_file(&self, entry_path: &Path, output: &mut Vec<u8>) -> Result<u64>;
|
||||
fn extract_file(&mut self, entry_path: &Path, output: &mut Vec<u8>) -> Result<u64>;
|
||||
|
||||
/// Extract all files to directory (batch extraction)
|
||||
fn extract_all(&self, output_dir: &Path) -> Result<ExtractResult>;
|
||||
fn extract_all(&mut self, output_dir: &Path) -> Result<ExtractResult>;
|
||||
|
||||
/// Check if this processor can handle the format
|
||||
fn can_process(format: ArchiveFormat) -> bool where Self: Sized;
|
||||
@@ -71,72 +73,6 @@ pub trait ArchiveProcessor: Send + Sync {
|
||||
fn new() -> Self where Self: Sized;
|
||||
}
|
||||
|
||||
/// Archive File Metadata
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ArchiveMetadata {
|
||||
pub format: ArchiveFormat,
|
||||
pub total_files: u64,
|
||||
pub total_size: u64,
|
||||
pub compressed_size: u64,
|
||||
pub compression_ratio: f64,
|
||||
pub is_encrypted: bool,
|
||||
pub is_multi_volume: bool,
|
||||
pub created_time: Option<SystemTime>,
|
||||
}
|
||||
|
||||
impl ArchiveMetadata {
|
||||
/// Calculate compression ratio
|
||||
pub fn compression_ratio(&self) -> f64 {
|
||||
if self.compressed_size == 0 {
|
||||
0.0
|
||||
} else {
|
||||
self.total_size as f64 / self.compressed_size as f64
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Archive Entry Information
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ArchiveEntry {
|
||||
pub path: PathBuf,
|
||||
pub size: u64,
|
||||
pub compressed_size: u64,
|
||||
pub is_dir: bool,
|
||||
pub is_file: bool,
|
||||
pub is_encrypted: bool,
|
||||
pub modified: SystemTime,
|
||||
pub permissions: Option<u32>,
|
||||
}
|
||||
|
||||
/// Extract Result Statistics
|
||||
#[derive(Debug)]
|
||||
pub struct ExtractResult {
|
||||
pub total_files: u64,
|
||||
pub total_bytes: u64,
|
||||
pub failed_files: Vec<PathBuf>,
|
||||
pub warnings: Vec<String>,
|
||||
}
|
||||
|
||||
impl ExtractResult {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
total_files: 0,
|
||||
total_bytes: 0,
|
||||
failed_files: Vec::new(),
|
||||
warnings: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn success_rate(&self) -> f64 {
|
||||
if self.total_files == 0 {
|
||||
100.0
|
||||
} else {
|
||||
let success_count = self.total_files - self.failed_files.len() as u64;
|
||||
(success_count as f64 / self.total_files as f64) * 100.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Security Validation - Zip Slip Protection
|
||||
pub fn validate_extraction_path(entry_path: &Path, base_dir: &Path) -> Result<PathBuf> {
|
||||
use std::path::Component;
|
||||
|
||||
@@ -44,6 +44,14 @@ impl ArchiveProcessor for ZipProcessor {
|
||||
ArchiveFormat::Zip
|
||||
}
|
||||
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
archive: None,
|
||||
path: PathBuf::new(),
|
||||
config: ArchiveConfig::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn open(&mut self, path: &Path) -> Result<ArchiveMetadata> {
|
||||
info!("Opening ZIP archive: {}", path.display());
|
||||
|
||||
@@ -53,8 +61,8 @@ impl ArchiveProcessor for ZipProcessor {
|
||||
self.archive = Some(archive);
|
||||
self.path = path.to_path_buf();
|
||||
|
||||
// Extract metadata
|
||||
let archive_ref = self.archive.as_ref().unwrap();
|
||||
// Extract metadata (need mutable reference for by_index)
|
||||
let archive_ref = self.archive.as_mut().unwrap();
|
||||
let total_files = archive_ref.len() as u64;
|
||||
|
||||
let mut total_size = 0u64;
|
||||
@@ -88,11 +96,12 @@ impl ArchiveProcessor for ZipProcessor {
|
||||
is_encrypted: false, // TODO: Check encryption
|
||||
is_multi_volume: false,
|
||||
created_time: Some(SystemTime::now()),
|
||||
modified_time: Some(SystemTime::now()),
|
||||
})
|
||||
}
|
||||
|
||||
fn list_entries(&self) -> Result<Vec<ArchiveEntry>> {
|
||||
let archive = self.archive.as_ref()
|
||||
fn list_entries(&mut self) -> Result<Vec<ArchiveEntry>> {
|
||||
let archive = self.archive.as_mut()
|
||||
.ok_or_else(|| anyhow!("Archive not opened"))?;
|
||||
|
||||
let mut entries = Vec::new();
|
||||
@@ -119,8 +128,8 @@ impl ArchiveProcessor for ZipProcessor {
|
||||
Ok(entries)
|
||||
}
|
||||
|
||||
fn extract_file(&self, entry_path: &Path, output: &mut Vec<u8>) -> Result<u64> {
|
||||
let archive = self.archive.as_ref()
|
||||
fn extract_file(&mut self, entry_path: &Path, output: &mut Vec<u8>) -> Result<u64> {
|
||||
let archive = self.archive.as_mut()
|
||||
.ok_or_else(|| anyhow!("Archive not opened"))?;
|
||||
|
||||
let entry_name = entry_path.to_str()
|
||||
@@ -140,46 +149,56 @@ impl ArchiveProcessor for ZipProcessor {
|
||||
Ok(output.len() as u64)
|
||||
}
|
||||
|
||||
fn extract_all(&self, output_dir: &Path) -> Result<ExtractResult> {
|
||||
let archive = self.archive.as_ref()
|
||||
.ok_or_else(|| anyhow!("Archive not opened"))?;
|
||||
|
||||
fn extract_all(&mut self, output_dir: &Path) -> Result<ExtractResult> {
|
||||
create_dir_all(output_dir)?;
|
||||
|
||||
let mut result = ExtractResult::new();
|
||||
|
||||
// Open archive if not already open
|
||||
if self.archive.is_none() {
|
||||
let file = File::open(&self.path)?;
|
||||
let archive = zip::ZipArchive::new(file)?;
|
||||
self.archive = Some(archive);
|
||||
}
|
||||
|
||||
let archive = self.archive.as_mut().unwrap();
|
||||
result.total_files = archive.len() as u64;
|
||||
|
||||
// Use archive iteration to extract files
|
||||
for i in 0..archive.len() {
|
||||
let mut file = archive.by_index(i)?;
|
||||
let entry_name = file.name();
|
||||
let outpath = output_dir.join(entry_name);
|
||||
let entry_name = file.name().to_string();
|
||||
let file_size = file.size();
|
||||
let is_dir = entry_name.ends_with('/');
|
||||
|
||||
// Zip Slip protection
|
||||
match validate_extraction_path(&PathBuf::from(entry_name), output_dir) {
|
||||
match validate_extraction_path(&PathBuf::from(&entry_name), output_dir) {
|
||||
Ok(safe_path) => {
|
||||
if entry_name.ends_with('/') {
|
||||
if is_dir {
|
||||
// Directory
|
||||
create_dir_all(&safe_path)?;
|
||||
debug!("Created directory: {}", entry_name);
|
||||
result.success_files += 1;
|
||||
} else {
|
||||
// File
|
||||
check_file_size_limit(file.size(), self.config.max_file_size_mb * 1024 * 1024)?;
|
||||
check_file_size_limit(file_size, self.config.max_file_size_mb * 1024 * 1024)?;
|
||||
|
||||
if let Some(parent) = safe_path.parent() {
|
||||
create_dir_all(parent)?;
|
||||
}
|
||||
|
||||
// Extract file content
|
||||
let mut outfile = BufWriter::new(File::create(&safe_path)?);
|
||||
std::io::copy(&mut file, &mut outfile)?;
|
||||
|
||||
result.success_files += 1;
|
||||
result.total_bytes += file.size();
|
||||
debug!("Extracted: {} ({} bytes)", entry_name, file.size());
|
||||
result.total_bytes += file_size;
|
||||
debug!("Extracted: {} ({} bytes)", entry_name, file_size);
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
warn!("Zip Slip detected: {} - {}", entry_name, e);
|
||||
result.failed_files.push(PathBuf::from(entry_name));
|
||||
result.failed_files.push(PathBuf::from(&entry_name));
|
||||
result.warnings.push(format!("Zip Slip: {}", entry_name));
|
||||
}
|
||||
}
|
||||
@@ -228,6 +247,14 @@ impl ArchiveProcessor for TarProcessor {
|
||||
ArchiveFormat::Tar
|
||||
}
|
||||
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
path: PathBuf::new(),
|
||||
entries: Vec::new(),
|
||||
config: ArchiveConfig::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn open(&mut self, path: &Path) -> Result<ArchiveMetadata> {
|
||||
info!("Opening TAR archive: {}", path.display());
|
||||
|
||||
@@ -271,14 +298,15 @@ impl ArchiveProcessor for TarProcessor {
|
||||
is_encrypted: false,
|
||||
is_multi_volume: false,
|
||||
created_time: Some(SystemTime::now()),
|
||||
modified_time: Some(SystemTime::now()),
|
||||
})
|
||||
}
|
||||
|
||||
fn list_entries(&self) -> Result<Vec<ArchiveEntry>> {
|
||||
fn list_entries(&mut self) -> Result<Vec<ArchiveEntry>> {
|
||||
Ok(self.entries.clone())
|
||||
}
|
||||
|
||||
fn extract_file(&self, entry_path: &Path, output: &mut Vec<u8>) -> Result<u64> {
|
||||
fn extract_file(&mut self, entry_path: &Path, output: &mut Vec<u8>) -> Result<u64> {
|
||||
// TAR doesn't support random access, need to unpack entire archive
|
||||
// This is a limitation - for single file extraction, we unpack everything
|
||||
warn!("TAR format doesn't support random access - extracting entire archive");
|
||||
@@ -294,7 +322,7 @@ impl ArchiveProcessor for TarProcessor {
|
||||
Ok(output.len() as u64)
|
||||
}
|
||||
|
||||
fn extract_all(&self, output_dir: &Path) -> Result<ExtractResult> {
|
||||
fn extract_all(&mut self, output_dir: &Path) -> Result<ExtractResult> {
|
||||
create_dir_all(output_dir)?;
|
||||
|
||||
let file = File::open(&self.path)?;
|
||||
@@ -304,8 +332,9 @@ impl ArchiveProcessor for TarProcessor {
|
||||
result.total_files = self.entries.len() as u64;
|
||||
|
||||
for entry in archive.entries()? {
|
||||
let entry = entry?;
|
||||
let mut entry = entry?;
|
||||
let entry_path = entry.path()?.to_path_buf();
|
||||
let entry_path_str = entry_path.display().to_string(); // Save for warning
|
||||
|
||||
// Zip Slip protection
|
||||
match validate_extraction_path(&entry_path, output_dir) {
|
||||
@@ -318,9 +347,9 @@ impl ArchiveProcessor for TarProcessor {
|
||||
result.total_bytes += entry.size();
|
||||
},
|
||||
Err(e) => {
|
||||
warn!("Zip Slip detected: {} - {}", entry_path.display(), e);
|
||||
warn!("Zip Slip detected: {} - {}", entry_path_str, e);
|
||||
result.failed_files.push(entry_path);
|
||||
result.warnings.push(format!("Zip Slip: {}", entry_path.display()));
|
||||
result.warnings.push(format!("Zip Slip: {}", entry_path_str));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -366,6 +395,14 @@ impl ArchiveProcessor for GzipProcessor {
|
||||
ArchiveFormat::Gzip
|
||||
}
|
||||
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
path: PathBuf::new(),
|
||||
decompressed_size: 0,
|
||||
config: ArchiveConfig::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn open(&mut self, path: &Path) -> Result<ArchiveMetadata> {
|
||||
info!("Opening GZIP archive: {}", path.display());
|
||||
|
||||
@@ -396,10 +433,11 @@ impl ArchiveProcessor for GzipProcessor {
|
||||
is_encrypted: false,
|
||||
is_multi_volume: false,
|
||||
created_time: Some(SystemTime::now()),
|
||||
modified_time: Some(SystemTime::now()),
|
||||
})
|
||||
}
|
||||
|
||||
fn list_entries(&self) -> Result<Vec<ArchiveEntry>> {
|
||||
fn list_entries(&mut self) -> Result<Vec<ArchiveEntry>> {
|
||||
// GZIP is single file - infer name from archive name
|
||||
let name = self.path.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
@@ -414,7 +452,7 @@ impl ArchiveProcessor for GzipProcessor {
|
||||
)])
|
||||
}
|
||||
|
||||
fn extract_file(&self, entry_path: &Path, output: &mut Vec<u8>) -> Result<u64> {
|
||||
fn extract_file(&mut self, entry_path: &Path, output: &mut Vec<u8>) -> Result<u64> {
|
||||
// GZIP is single file - just decompress it
|
||||
let file = File::open(&self.path)?;
|
||||
let mut decoder = flate2::read::GzDecoder::new(file);
|
||||
@@ -428,7 +466,7 @@ impl ArchiveProcessor for GzipProcessor {
|
||||
Ok(output.len() as u64)
|
||||
}
|
||||
|
||||
fn extract_all(&self, output_dir: &Path) -> Result<ExtractResult> {
|
||||
fn extract_all(&mut self, output_dir: &Path) -> Result<ExtractResult> {
|
||||
create_dir_all(output_dir)?;
|
||||
|
||||
let entries = self.list_entries()?;
|
||||
@@ -497,6 +535,13 @@ impl ArchiveProcessor for TarGzipProcessor {
|
||||
ArchiveFormat::TarGzip
|
||||
}
|
||||
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
gzip_processor: GzipProcessor::new(),
|
||||
config: ArchiveConfig::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn open(&mut self, path: &Path) -> Result<ArchiveMetadata> {
|
||||
info!("Opening TAR.GZ archive: {}", path.display());
|
||||
|
||||
@@ -528,16 +573,17 @@ impl ArchiveProcessor for TarGzipProcessor {
|
||||
is_encrypted: false,
|
||||
is_multi_volume: false,
|
||||
created_time: Some(SystemTime::now()),
|
||||
modified_time: Some(SystemTime::now()),
|
||||
})
|
||||
}
|
||||
|
||||
fn list_entries(&self) -> Result<Vec<ArchiveEntry>> {
|
||||
fn list_entries(&mut self) -> Result<Vec<ArchiveEntry>> {
|
||||
// Need to implement properly - this requires decompressing first
|
||||
warn!("TAR.GZ list_entries requires full decompression - consider extract_all instead");
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
fn extract_file(&self, entry_path: &Path, output: &mut Vec<u8>) -> Result<u64> {
|
||||
fn extract_file(&mut self, entry_path: &Path, output: &mut Vec<u8>) -> Result<u64> {
|
||||
warn!("TAR.GZ extract_file requires full unpacking - inefficient for single file");
|
||||
|
||||
let temp_dir = tempfile::tempdir()?;
|
||||
@@ -551,7 +597,7 @@ impl ArchiveProcessor for TarGzipProcessor {
|
||||
Ok(output.len() as u64)
|
||||
}
|
||||
|
||||
fn extract_all(&self, output_dir: &Path) -> Result<ExtractResult> {
|
||||
fn extract_all(&mut self, output_dir: &Path) -> Result<ExtractResult> {
|
||||
info!("Extracting TAR.GZ to: {}", output_dir.display());
|
||||
|
||||
// Step 1: Decompress GZIP to temp
|
||||
@@ -585,9 +631,9 @@ impl ArchiveProcessor for ZstdProcessor {
|
||||
fn open(&mut self, _path: &Path) -> Result<ArchiveMetadata> {
|
||||
Err(anyhow!("ZSTD processor not yet implemented"))
|
||||
}
|
||||
fn list_entries(&self) -> Result<Vec<ArchiveEntry>> { Ok(Vec::new()) }
|
||||
fn extract_file(&self, _entry: &Path, _output: &mut Vec<u8>) -> Result<u64> { Ok(0) }
|
||||
fn extract_all(&self, _dir: &Path) -> Result<ExtractResult> { Ok(ExtractResult::new()) }
|
||||
fn list_entries(&mut self) -> Result<Vec<ArchiveEntry>> { Ok(Vec::new()) }
|
||||
fn extract_file(&mut self, _entry: &Path, _output: &mut Vec<u8>) -> Result<u64> { Ok(0) }
|
||||
fn extract_all(&mut self, _dir: &Path) -> Result<ExtractResult> { Ok(ExtractResult::new()) }
|
||||
fn can_process(format: ArchiveFormat) -> bool { format == ArchiveFormat::Zstd }
|
||||
fn new() -> Self { Self }
|
||||
}
|
||||
@@ -600,9 +646,9 @@ impl ArchiveProcessor for Bzip2Processor {
|
||||
fn open(&mut self, _path: &Path) -> Result<ArchiveMetadata> {
|
||||
Err(anyhow!("BZIP2 processor not yet implemented"))
|
||||
}
|
||||
fn list_entries(&self) -> Result<Vec<ArchiveEntry>> { Ok(Vec::new()) }
|
||||
fn extract_file(&self, _entry: &Path, _output: &mut Vec<u8>) -> Result<u64> { Ok(0) }
|
||||
fn extract_all(&self, _dir: &Path) -> Result<ExtractResult> { Ok(ExtractResult::new()) }
|
||||
fn list_entries(&mut self) -> Result<Vec<ArchiveEntry>> { Ok(Vec::new()) }
|
||||
fn extract_file(&mut self, _entry: &Path, _output: &mut Vec<u8>) -> Result<u64> { Ok(0) }
|
||||
fn extract_all(&mut self, _dir: &Path) -> Result<ExtractResult> { Ok(ExtractResult::new()) }
|
||||
fn can_process(format: ArchiveFormat) -> bool { format == ArchiveFormat::Bzip2 }
|
||||
fn new() -> Self { Self }
|
||||
}
|
||||
@@ -615,9 +661,9 @@ impl ArchiveProcessor for Lz4Processor {
|
||||
fn open(&mut self, _path: &Path) -> Result<ArchiveMetadata> {
|
||||
Err(anyhow!("LZ4 processor not yet implemented"))
|
||||
}
|
||||
fn list_entries(&self) -> Result<Vec<ArchiveEntry>> { Ok(Vec::new()) }
|
||||
fn extract_file(&self, _entry: &Path, _output: &mut Vec<u8>) -> Result<u64> { Ok(0) }
|
||||
fn extract_all(&self, _dir: &Path) -> Result<ExtractResult> { Ok(ExtractResult::new()) }
|
||||
fn list_entries(&mut self) -> Result<Vec<ArchiveEntry>> { Ok(Vec::new()) }
|
||||
fn extract_file(&mut self, _entry: &Path, _output: &mut Vec<u8>) -> Result<u64> { Ok(0) }
|
||||
fn extract_all(&mut self, _dir: &Path) -> Result<ExtractResult> { Ok(ExtractResult::new()) }
|
||||
fn can_process(format: ArchiveFormat) -> bool { format == ArchiveFormat::Lz4 }
|
||||
fn new() -> Self { Self }
|
||||
}
|
||||
@@ -630,9 +676,9 @@ impl ArchiveProcessor for TarBzip2Processor {
|
||||
fn open(&mut self, _path: &Path) -> Result<ArchiveMetadata> {
|
||||
Err(anyhow!("TAR.BZ2 processor not yet implemented"))
|
||||
}
|
||||
fn list_entries(&self) -> Result<Vec<ArchiveEntry>> { Ok(Vec::new()) }
|
||||
fn extract_file(&self, _entry: &Path, _output: &mut Vec<u8>) -> Result<u64> { Ok(0) }
|
||||
fn extract_all(&self, _dir: &Path) -> Result<ExtractResult> { Ok(ExtractResult::new()) }
|
||||
fn list_entries(&mut self) -> Result<Vec<ArchiveEntry>> { Ok(Vec::new()) }
|
||||
fn extract_file(&mut self, _entry: &Path, _output: &mut Vec<u8>) -> Result<u64> { Ok(0) }
|
||||
fn extract_all(&mut self, _dir: &Path) -> Result<ExtractResult> { Ok(ExtractResult::new()) }
|
||||
fn can_process(format: ArchiveFormat) -> bool { format == ArchiveFormat::TarBzip2 }
|
||||
fn new() -> Self { Self }
|
||||
}
|
||||
@@ -645,9 +691,9 @@ impl ArchiveProcessor for TarZstdProcessor {
|
||||
fn open(&mut self, _path: &Path) -> Result<ArchiveMetadata> {
|
||||
Err(anyhow!("TAR.ZST processor not yet implemented"))
|
||||
}
|
||||
fn list_entries(&self) -> Result<Vec<ArchiveEntry>> { Ok(Vec::new()) }
|
||||
fn extract_file(&self, _entry: &Path, _output: &mut Vec<u8>) -> Result<u64> { Ok(0) }
|
||||
fn extract_all(&self, _dir: &Path) -> Result<ExtractResult> { Ok(ExtractResult::new()) }
|
||||
fn list_entries(&mut self) -> Result<Vec<ArchiveEntry>> { Ok(Vec::new()) }
|
||||
fn extract_file(&mut self, _entry: &Path, _output: &mut Vec<u8>) -> Result<u64> { Ok(0) }
|
||||
fn extract_all(&mut self, _dir: &Path) -> Result<ExtractResult> { Ok(ExtractResult::new()) }
|
||||
fn can_process(format: ArchiveFormat) -> bool { format == ArchiveFormat::TarZstd }
|
||||
fn new() -> Self { Self }
|
||||
}
|
||||
@@ -64,7 +64,7 @@ impl ArchiveProcessor for RarProcessor {
|
||||
})
|
||||
}
|
||||
|
||||
fn list_entries(&self) -> Result<Vec<ArchiveEntry>> {
|
||||
fn list_entries(&mut self) -> Result<Vec<ArchiveEntry>> {
|
||||
use unrar::Archive;
|
||||
|
||||
let path = self.archive_path.as_ref().ok_or_else(|| anyhow!("Archive not opened"))?;
|
||||
@@ -202,7 +202,7 @@ impl ArchiveProcessor for XzProcessor {
|
||||
})
|
||||
}
|
||||
|
||||
fn list_entries(&self) -> Result<Vec<ArchiveEntry>> {
|
||||
fn list_entries(&mut self) -> Result<Vec<ArchiveEntry>> {
|
||||
// XZ is single-file, infer filename from archive name
|
||||
let path = self.archive_path.as_ref().ok_or_else(|| anyhow!("Archive not opened"))?;
|
||||
|
||||
@@ -317,7 +317,7 @@ impl ArchiveProcessor for SevenZProcessor {
|
||||
})
|
||||
}
|
||||
|
||||
fn list_entries(&self) -> Result<Vec<ArchiveEntry>> {
|
||||
fn list_entries(&mut self) -> Result<Vec<ArchiveEntry>> {
|
||||
// Note: sevenz-rust doesn't have full entry listing yet
|
||||
// This is a stub returning empty list
|
||||
warn!("7z list_entries not fully implemented (library limitation)");
|
||||
|
||||
@@ -13,6 +13,7 @@ pub mod s3_config;
|
||||
pub mod s3_xml;
|
||||
pub mod scan;
|
||||
pub mod server;
|
||||
pub mod archive; // Archive Module - Universal Compression Format Support (Phase 1-3完成)
|
||||
// pub mod sftp; // ⚠️ russh版本(已禁用)
|
||||
// pub mod ssh2_server; // ssh2服务器(已禁用)
|
||||
// pub mod ssh2_mod; // ssh2辅助模块(已禁用)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user