use crate::{Archive, Result};
use std::path::{Path, PathBuf};
pub fn extract_from_multiple_archives<P: AsRef<Path> + Sync>(
archives: &[P],
file_name: &str,
) -> Result<Vec<(PathBuf, Vec<u8>)>> {
use rayon::prelude::*;
archives
.par_iter()
.map(|path| {
let path_ref = path.as_ref();
match Archive::open(path_ref) {
Ok(mut archive) => match archive.read_file(file_name) {
Ok(data) => Ok((path_ref.to_path_buf(), data)),
Err(e) => Err(e),
},
Err(e) => Err(e),
}
})
.collect()
}
type ArchiveExtractionResult = Vec<(PathBuf, Vec<(String, Vec<u8>)>)>;
pub fn extract_multiple_from_multiple_archives<P: AsRef<Path> + Sync>(
archives: &[P],
file_names: &[&str],
) -> Result<ArchiveExtractionResult> {
use rayon::prelude::*;
archives
.par_iter()
.map(|path| {
let path_ref = path.as_ref();
let mut archive = Archive::open(path_ref)?;
let files: Result<Vec<_>> = file_names
.iter()
.map(|&name| archive.read_file(name).map(|data| (name.to_string(), data)))
.collect();
Ok((path_ref.to_path_buf(), files?))
})
.collect()
}
pub fn search_in_multiple_archives<P: AsRef<Path> + Sync>(
archives: &[P],
pattern: &str,
) -> Result<Vec<(PathBuf, Vec<String>)>> {
use rayon::prelude::*;
archives
.par_iter()
.map(|path| {
let path_ref = path.as_ref();
let mut archive = Archive::open(path_ref)?;
let files = archive.list()?;
let matching: Vec<String> = files
.into_iter()
.filter(|entry| entry.name.contains(pattern))
.map(|entry| entry.name)
.collect();
Ok((path_ref.to_path_buf(), matching))
})
.collect()
}
pub fn process_archives_parallel<P, F, T>(archives: &[P], processor: F) -> Result<Vec<T>>
where
P: AsRef<Path> + Sync,
F: Fn(Archive) -> Result<T> + Sync,
T: Send,
{
use rayon::prelude::*;
archives
.par_iter()
.map(|path| {
let archive = Archive::open(path)?;
processor(archive)
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ArchiveBuilder;
use tempfile::TempDir;
fn create_test_archives(count: usize) -> Result<(TempDir, Vec<PathBuf>)> {
let temp_dir = TempDir::new()?;
let mut paths = Vec::new();
for i in 0..count {
let path = temp_dir.path().join(format!("test_{i}.mpq"));
let mut builder = ArchiveBuilder::new();
builder = builder
.add_file_data(
format!("Content from archive {i}").into_bytes(),
"common.txt",
)
.add_file_data(
format!("Unique to archive {i}").into_bytes(),
&format!("unique_{i}.txt"),
);
builder.build(&path)?;
paths.push(path);
}
Ok((temp_dir, paths))
}
#[test]
fn test_extract_from_multiple_archives() -> Result<()> {
let (_temp_dir, archives) = create_test_archives(3)?;
let results = extract_from_multiple_archives(&archives, "common.txt")?;
assert_eq!(results.len(), 3);
for (i, (path, data)) in results.iter().enumerate() {
assert_eq!(path, &archives[i]);
let content = String::from_utf8_lossy(data);
assert!(content.contains(&format!("Content from archive {i}")));
}
Ok(())
}
#[test]
fn test_search_in_multiple_archives() -> Result<()> {
let (_temp_dir, archives) = create_test_archives(3)?;
let results = search_in_multiple_archives(&archives, "unique")?;
assert_eq!(results.len(), 3);
for (i, (_path, matches)) in results.iter().enumerate() {
assert_eq!(matches.len(), 1);
assert_eq!(matches[0], format!("unique_{i}.txt"));
}
Ok(())
}
#[test]
fn test_process_archives_parallel() -> Result<()> {
let (_temp_dir, archives) = create_test_archives(3)?;
let counts = process_archives_parallel(&archives, |mut archive| Ok(archive.list()?.len()))?;
assert_eq!(counts.len(), 3);
for count in counts {
assert!(count >= 2);
}
Ok(())
}
}