add 7z archive support

This commit is contained in:
Robin Appelman 2025-05-28 16:14:19 +02:00
commit f47002642d
5 changed files with 215 additions and 14 deletions

View file

@ -25,6 +25,8 @@ pub enum ReadError {
Zip(#[from] ZipError),
#[error(transparent)]
Zstd(#[from] FrameDecoderError),
#[error(transparent)]
SevenZip(#[from] sevenz_rust2::Error),
#[error("archive contains no files")]
NoFiles,
#[error("log file contained non-utf8 characters: {0:#}")]

View file

@ -1,5 +1,6 @@
use crate::error::ReadError;
use itertools::Either;
use sevenz_rust2::{Password, SevenZReader};
use std::borrow::Cow;
use std::io::{Read, Seek};
use std::iter::empty;
@ -129,3 +130,61 @@ impl<R: Read> Archive for TarArchive<R> {
}
}
}
pub struct SevenZipArchive<R: Read + Seek>(Mutex<SevenZReader<R>>);
impl<R: Read + Seek> SevenZipArchive<R> {
pub fn new(reader: R) -> Result<Self, ReadError> {
Ok(Self(Mutex::new(SevenZReader::new(
reader,
Password::empty(),
)?)))
}
}
pub struct SevenZipEntry<'a, R: Read + Seek> {
name: String,
reader: &'a Mutex<SevenZReader<R>>,
}
impl<R: Read + Seek> ArchiveEntry for SevenZipEntry<'_, R> {
fn name(&self) -> Cow<str> {
self.name.as_str().into()
}
fn extract(self) -> Result<Vec<u8>, ReadError> {
let mut buff = Vec::new();
let mut reader = self.reader.lock().unwrap();
reader.for_each_entries(|entry, reader| {
if entry.name() == self.name.as_str() {
reader.read_to_end(&mut buff)?;
}
Ok(true)
})?;
Ok(buff)
}
}
impl<R: Read + Seek> Archive for SevenZipArchive<R> {
type Entry<'a>
= SevenZipEntry<'a, R>
where
R: 'a;
fn entries(&mut self) -> impl Iterator<Item = Self::Entry<'_>> {
let names: Vec<_> = self
.0
.lock()
.unwrap()
.archive()
.files
.iter()
.filter(|file| !file.is_directory())
.map(|f| f.name.clone())
.collect();
names.into_iter().map(|name| SevenZipEntry {
name,
reader: &self.0,
})
}
}

View file

@ -2,7 +2,7 @@ mod archive;
pub mod logline;
use crate::error::ReadError;
use crate::logfile::archive::{Archive, ArchiveEntry, TarArchive, ZipArchive};
use crate::logfile::archive::{Archive, ArchiveEntry, SevenZipArchive, TarArchive, ZipArchive};
use bzip2_rs::DecoderReader;
use dialoguer::Select;
use flate2::read::GzDecoder;
@ -36,6 +36,9 @@ impl LogFile {
if path.ends_with(".zip") {
let mut zip = ZipArchive::new(file)?;
return select_file(&mut zip);
} else if path.ends_with(".7z") {
let mut zip = SevenZipArchive::new(file)?;
return select_file(&mut zip);
}
if let Some(path) = path.strip_suffix(".gz") {