allow loading multiple from archives
All checks were successful
CI / build (push) Successful in 44s
CI / checks (push) Successful in 1m11s
CI / build-nixpkgs (push) Successful in 36s

This commit is contained in:
Robin Appelman 2025-09-27 14:31:55 +02:00
commit b62b0a8d8e
2 changed files with 35 additions and 30 deletions

1
.gitignore vendored
View file

@ -5,3 +5,4 @@ result
*.log *.log
profile.* profile.*
*.out.* *.out.*
/logs

View file

@ -7,7 +7,7 @@ use crate::logfile::archive::RarArchive;
use crate::logfile::archive::{Archive, ArchiveEntry, SevenZipArchive, TarArchive, ZipArchive}; use crate::logfile::archive::{Archive, ArchiveEntry, SevenZipArchive, TarArchive, ZipArchive};
use bzip2_rs::DecoderReader; use bzip2_rs::DecoderReader;
use csv::Reader; use csv::Reader;
use dialoguer::Select; use dialoguer::MultiSelect;
use flate2::read::GzDecoder; use flate2::read::GzDecoder;
pub use logline::{LineNumber, LogLine}; pub use logline::{LineNumber, LogLine};
use ruzstd::decoding::StreamingDecoder; use ruzstd::decoding::StreamingDecoder;
@ -44,15 +44,15 @@ impl LogFile {
pub fn open<R: Read + Seek>(path: &str, file: R) -> Result<LogFile, ReadError> { pub fn open<R: Read + Seek>(path: &str, file: R) -> Result<LogFile, ReadError> {
if path.ends_with(".zip") { if path.ends_with(".zip") {
let mut zip = ZipArchive::new(file)?; let mut zip = ZipArchive::new(file)?;
return select_file(&mut zip); return select_files(&mut zip);
} else if path.ends_with(".7z") { } else if path.ends_with(".7z") {
let mut zip = SevenZipArchive::new(file)?; let mut zip = SevenZipArchive::new(file)?;
return select_file(&mut zip); return select_files(&mut zip);
} else if path.ends_with(".rar") { } else if path.ends_with(".rar") {
#[cfg(all(unix, not(target_env = "musl")))] #[cfg(all(unix, not(target_env = "musl")))]
{ {
let mut rar = RarArchive::new(path)?; let mut rar = RarArchive::new(path)?;
return select_file(&mut rar); return select_files(&mut rar);
} }
#[cfg(target_env = "musl")] #[cfg(target_env = "musl")]
return Err(ReadError::UnSupportedFormat { return Err(ReadError::UnSupportedFormat {
@ -100,7 +100,7 @@ impl LogFile {
fn open_no_seek<R: Read>(path: &str, mut file: R) -> Result<LogFile, ReadError> { fn open_no_seek<R: Read>(path: &str, mut file: R) -> Result<LogFile, ReadError> {
if path.ends_with(".tar") { if path.ends_with(".tar") {
let mut zip = TarArchive::new(file)?; let mut zip = TarArchive::new(file)?;
select_file(&mut zip) select_files(&mut zip)
} else { } else {
let mut content = String::new(); let mut content = String::new();
file.read_to_string(&mut content)?; file.read_to_string(&mut content)?;
@ -132,32 +132,36 @@ impl LogFile {
} }
} }
fn select_file<A: Archive>(archive: &mut A) -> Result<LogFile, ReadError> { fn select_files<A: Archive>(archive: &mut A) -> Result<LogFile, ReadError> {
let entry = { let entries = archive
let mut entries = archive .entries()
.entries() .filter(|entry| !entry.name().starts_with("__MACOSX") && !entry.name().ends_with('/'))
.filter(|entry| !entry.name().starts_with("__MACOSX") && !entry.name().ends_with('/')) .collect::<Vec<_>>();
.collect::<Vec<_>>();
if entries.is_empty() { if entries.is_empty() {
return Err(ReadError::NoFiles); return Err(ReadError::NoFiles);
} }
let index = if entries.len() == 1 { let indices = if entries.len() == 1 {
0usize vec![0usize]
} else { } else {
let names = entries.iter().map(A::Entry::name).collect::<Vec<_>>(); let names = entries.iter().map(A::Entry::name).collect::<Vec<_>>();
Select::new() MultiSelect::new()
.with_prompt("Select file to load?") .with_prompt("Select file(s) to load?")
.items(&names) .items(&names)
.interact() .interact()
.unwrap() .unwrap()
};
entries.remove(index)
}; };
let name = entry.name().to_string();
let raw = entry.extract()?;
LogFile::open(&name, Cursor::new(raw)) let mut files = Vec::new();
for (index, entry) in entries.into_iter().enumerate() {
if indices.contains(&index) {
let name = entry.name().to_string();
let raw = entry.extract()?;
files.push(LogFile::open(&name, Cursor::new(raw))?);
}
}
Ok(LogFile::merge(files))
} }