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

160
Cargo.lock generated
View file

@ -124,6 +124,21 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bit-set"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
dependencies = [
"bit-vec",
]
[[package]]
name = "bit-vec"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
[[package]]
name = "bitflags"
version = "2.8.0"
@ -214,6 +229,15 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
dependencies = [
"num-traits",
]
[[package]]
name = "cipher"
version = "0.4.4"
@ -448,9 +472,9 @@ checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b"
[[package]]
name = "deranged"
version = "0.3.11"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
dependencies = [
"powerfmt",
"serde",
@ -569,6 +593,17 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "filetime_creation"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c25b5d475550e559de5b0c0084761c65325444e3b6c9e298af9cefe7a9ef3a5f"
dependencies = [
"cfg-if",
"filetime",
"windows-sys 0.52.0",
]
[[package]]
name = "flate2"
version = "1.0.35"
@ -752,6 +787,30 @@ version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
[[package]]
name = "jiff"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a194df1107f33c79f4f93d02c80798520551949d59dfad22b6157048a88cca93"
dependencies = [
"jiff-static",
"log",
"portable-atomic",
"portable-atomic-util",
"serde",
]
[[package]]
name = "jiff-static"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c6e1db7ed32c6c71b759497fae34bf7933636f75a251b9e736555da426f6442"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "jobserver"
version = "0.1.32"
@ -838,6 +897,7 @@ dependencies = [
"ruzstd",
"serde",
"serde_json",
"sevenz-rust2",
"tar",
"thiserror 2.0.11",
"tikv-jemallocator",
@ -874,6 +934,15 @@ dependencies = [
"crc",
]
[[package]]
name = "lzma-rust2"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0889a24ddb2304e1b3cdc0b0cd74119143d3403c19100f064809d38789bae155"
dependencies = [
"byteorder",
]
[[package]]
name = "lzma-sys"
version = "0.1.20"
@ -934,6 +1003,18 @@ dependencies = [
"minimal-lexical",
]
[[package]]
name = "nt-time"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5e71108c089b161344bacb1227dd2124fee63ed1792fdd8308e6689197c2754"
dependencies = [
"chrono",
"jiff",
"rand 0.9.1",
"time",
]
[[package]]
name = "num-conv"
version = "0.1.0"
@ -1012,6 +1093,15 @@ version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
[[package]]
name = "portable-atomic-util"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507"
dependencies = [
"portable-atomic",
]
[[package]]
name = "powerfmt"
version = "0.2.0"
@ -1052,8 +1142,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_chacha 0.3.1",
"rand_core 0.6.4",
]
[[package]]
name = "rand"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
dependencies = [
"rand_chacha 0.9.0",
"rand_core 0.9.3",
]
[[package]]
@ -1063,7 +1163,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
"rand_core 0.6.4",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core 0.9.3",
]
[[package]]
@ -1075,6 +1185,15 @@ dependencies = [
"getrandom 0.2.15",
]
[[package]]
name = "rand_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [
"getrandom 0.3.1",
]
[[package]]
name = "ratatui"
version = "0.29.0"
@ -1226,6 +1345,22 @@ dependencies = [
"serde",
]
[[package]]
name = "sevenz-rust2"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3e35b0d7be5029b57e0abf50185b1c2084efd1729083d5093c2548f2c6bf999"
dependencies = [
"bit-set",
"byteorder",
"crc32fast",
"filetime_creation",
"js-sys",
"lzma-rust2",
"nt-time",
"wasm-bindgen",
]
[[package]]
name = "sha1"
version = "0.10.6"
@ -1429,9 +1564,9 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.37"
version = "0.3.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
dependencies = [
"deranged",
"itoa",
@ -1444,15 +1579,15 @@ dependencies = [
[[package]]
name = "time-core"
version = "0.1.2"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
[[package]]
name = "time-macros"
version = "0.2.19"
version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49"
dependencies = [
"num-conv",
"time-core",
@ -1568,6 +1703,7 @@ checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
]
@ -1821,7 +1957,7 @@ dependencies = [
"lzma-rs",
"memchr",
"pbkdf2",
"rand",
"rand 0.8.5",
"sha1",
"thiserror 2.0.11",
"time",

View file

@ -2,7 +2,7 @@
name = "logsmash"
version = "0.1.10"
edition = "2021"
rust-version = "1.81.0"
rust-version = "1.85.0"
license = "GPL-3.0-only"
[dependencies]
@ -28,6 +28,7 @@ flate2 = "1.0.35"
xz2 = "0.1.7"
bzip2-rs = "0.1.2"
ruzstd = "0.8.0"
sevenz-rust2 = "0.13.2"
dialoguer = "0.11.0"
indicatif = { version = "0.17.11", features = ["rayon"] }

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") {