-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
290 additions
and
75 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
use std::fs::File; | ||
use std::path::PathBuf; | ||
|
||
use cab::CabinetBuilder; | ||
|
||
use crate::archiver::{Archiver, ArchiverOpts}; | ||
use crate::cli::{ToteError, Result}; | ||
use crate::format::Format; | ||
|
||
pub(super) struct CabArchiver { | ||
} | ||
|
||
impl Archiver for CabArchiver { | ||
fn perform(&self, opts: &ArchiverOpts) -> Result<()> { | ||
match opts.destination() { | ||
Err(e) => Err(e), | ||
Ok(file) => { | ||
write_impl(file, opts.targets(), opts.recursive, opts.base_dir.clone()) | ||
} | ||
} | ||
} | ||
|
||
fn format(&self) -> Format { | ||
Format::Cab | ||
} | ||
} | ||
|
||
fn write_impl(file: File, targets: Vec<PathBuf>, recursive: bool, base_dir: PathBuf) -> Result<()> { | ||
let mut builder = CabinetBuilder::new(); | ||
let folder = builder.add_folder(cab::CompressionType::MsZip); | ||
let list = correct_targets(targets, recursive, base_dir); | ||
for (_from, dest_file) in list.clone() { | ||
folder.add_file(dest_file); | ||
} | ||
let mut writer = match builder.build(file) { | ||
Ok(w) => w, | ||
Err(e) => return Err(ToteError::Archiver(e.to_string())), | ||
}; | ||
let mut iter = list.iter(); | ||
while let Some(mut w) = writer.next_file().unwrap() { | ||
let (from, _) = iter.next().unwrap(); | ||
if let Ok(mut reader) = File::open(from) { | ||
std::io::copy(&mut reader, &mut w).unwrap(); | ||
} | ||
} | ||
match writer.finish() { | ||
Ok(_) => Ok(()), | ||
Err(e) => Err(ToteError::Archiver(e.to_string())), | ||
} | ||
} | ||
|
||
fn correct_targets(targets: Vec<PathBuf>, recursive: bool, base_dir: PathBuf) -> Vec<(PathBuf, String)> { | ||
let mut result = vec![]; | ||
for target in targets { | ||
let path = target.as_path(); | ||
if path.is_dir() && recursive { | ||
process_dir(&mut result, path.to_path_buf(), &base_dir); | ||
} else { | ||
process_file(&mut result, path.to_path_buf(), &base_dir); | ||
} | ||
} | ||
result | ||
} | ||
|
||
fn process_dir(result: &mut Vec<(PathBuf, String)>, path: PathBuf, base_dir: &PathBuf) { | ||
for entry in path.read_dir().unwrap() { | ||
if let Ok(e) = entry { | ||
let p = e.path(); | ||
if p.is_dir() { | ||
process_dir(result, e.path(), &base_dir) | ||
} else if p.is_file() { | ||
process_file(result, e.path(), &base_dir) | ||
} | ||
} | ||
} | ||
} | ||
|
||
fn process_file(result: &mut Vec<(PathBuf, String)>, target: PathBuf, base_dir: &PathBuf) { | ||
let target_path = match target.strip_prefix(base_dir) { | ||
Ok(p) => p.to_path_buf(), | ||
Err(_) => target.clone(), | ||
}; | ||
let name = target_path.to_str().unwrap(); | ||
result.push((target, name.to_string())); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
use std::path::PathBuf; | ||
use std::fs::{create_dir_all, File}; | ||
|
||
use cab::Cabinet; | ||
|
||
use crate::cli::{Result, ToteError}; | ||
use crate::extractor::{Extractor, ExtractorOpts}; | ||
|
||
pub(super) struct CabExtractor {} | ||
|
||
fn list_impl<F, T>(archive_file: &PathBuf, mapper: F) -> Result<Vec<T>> | ||
where F: Fn(&cab::FileEntry) -> T { | ||
let cabinet = open_cabinet(&archive_file)?; | ||
let mut result = vec![]; | ||
for folder in cabinet.folder_entries() { | ||
for file in folder.file_entries() { | ||
result.push(mapper(file)); | ||
} | ||
} | ||
Ok(result) | ||
} | ||
|
||
impl Extractor for CabExtractor { | ||
fn list_archives(&self, archive_file: PathBuf) -> Result<Vec<String>> { | ||
list_impl(&archive_file, |file| file.name().to_string()) | ||
} | ||
|
||
fn perform(&self, archive_file: PathBuf, opts: &ExtractorOpts) -> Result<()> { | ||
let list = match list_impl(&archive_file, | ||
|file| (file.name().to_string(), file.uncompressed_size())) { | ||
Ok(l) => l, | ||
Err(e) => return Err(e), | ||
}; | ||
let mut cabinet = open_cabinet(&archive_file)?; | ||
for file in list { | ||
let file_name = file.0.clone(); | ||
let dest_file = opts.destination(&archive_file)?.join(&file_name); | ||
opts.v.verbose(format!("extracting {} ({} bytes)", &file_name, file.1)); | ||
create_dir_all(dest_file.parent().unwrap()).unwrap(); | ||
let mut dest = match File::create(dest_file) { | ||
Ok(f) => f, | ||
Err(e) => return Err(ToteError::IO(e)), | ||
}; | ||
let mut file_from = cabinet.read_file(&file_name).unwrap(); | ||
match std::io::copy(&mut file_from, &mut dest) { | ||
Ok(_) => {} | ||
Err(e) => return Err(ToteError::IO(e)), | ||
} | ||
} | ||
Ok(()) | ||
} | ||
|
||
fn format(&self) -> crate::format::Format { | ||
crate::format::Format::Cab | ||
} | ||
} | ||
|
||
fn open_cabinet(archive_file: &PathBuf) -> Result<Cabinet<File>> { | ||
let cab_file = match File::open(archive_file) { | ||
Ok(f) => f, | ||
Err(e) => return Err(ToteError::IO(e)), | ||
}; | ||
match Cabinet::new(cab_file) { | ||
Ok(c) => Ok(c), | ||
Err(e) => Err(ToteError::IO(e)), | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use crate::{format::Format, verboser::create_verboser}; | ||
use super::*; | ||
#[test] | ||
fn test_list_archives() { | ||
let extractor = CabExtractor{}; | ||
let file = PathBuf::from("testdata/test.cab"); | ||
match extractor.list_archives(file) { | ||
Ok(r) => { | ||
assert_eq!(r.len(), 16); | ||
assert_eq!(r.get(0), Some("Cargo.toml".to_string()).as_ref()); | ||
assert_eq!(r.get(1), Some("LICENSE".to_string()).as_ref()); | ||
assert_eq!(r.get(2), Some("build.rs".to_string()).as_ref()); | ||
assert_eq!(r.get(3), Some("README.md".to_string()).as_ref()); | ||
}, | ||
Err(_) => assert!(false), | ||
} | ||
} | ||
|
||
#[test] | ||
fn test_extract_archive() { | ||
let e = CabExtractor{}; | ||
let file = PathBuf::from("testdata/test.cab"); | ||
let opts = ExtractorOpts { | ||
dest: PathBuf::from("results/cab"), | ||
use_archive_name_dir: true, | ||
overwrite: true, | ||
v: create_verboser(false), | ||
}; | ||
match e.perform(file, &opts) { | ||
Ok(_) => { | ||
assert!(true); | ||
assert!(PathBuf::from("results/cab/test/Cargo.toml").exists()); | ||
std::fs::remove_dir_all(PathBuf::from("results/cab")).unwrap(); | ||
}, | ||
Err(_) => assert!(false), | ||
}; | ||
} | ||
|
||
#[test] | ||
fn test_format() { | ||
let extractor = CabExtractor {}; | ||
assert_eq!(extractor.format(), Format::Cab); | ||
} | ||
} |
Oops, something went wrong.