switch to rar-stream for rar archive support
All checks were successful
CI / build (push) Successful in 42s
CI / checks (push) Successful in 50s
CI / build-nixpkgs (push) Successful in 35s

This commit is contained in:
Robin Appelman 2026-04-10 19:14:30 +02:00
commit d03092aa16
4 changed files with 34 additions and 56 deletions

41
Cargo.lock generated
View file

@ -1027,6 +1027,7 @@ dependencies = [
"main_error", "main_error",
"matchit", "matchit",
"osc94", "osc94",
"rar-stream",
"ratatui", "ratatui",
"rayon", "rayon",
"regex", "regex",
@ -1039,7 +1040,6 @@ dependencies = [
"tikv-jemallocator", "tikv-jemallocator",
"time", "time",
"tinystr", "tinystr",
"unrar",
"xz2", "xz2",
"zip", "zip",
] ]
@ -1438,6 +1438,16 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
[[package]]
name = "rar-stream"
version = "5.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bad41fc4447843b83b5e858b778dedcbbaf116e2bd3d7e21ab19ed096a942ef"
dependencies = [
"crossbeam-channel",
"rayon",
]
[[package]] [[package]]
name = "ratatui" name = "ratatui"
version = "0.30.0" version = "0.30.0"
@ -2113,29 +2123,6 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3" checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3"
[[package]]
name = "unrar"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92ec61343a630d2b50d13216dea5125e157d3fc180a7d3f447d22fe146b648fc"
dependencies = [
"bitflags 2.11.0",
"regex",
"unrar_sys",
"widestring",
]
[[package]]
name = "unrar_sys"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b77675b883cfbe6bf41e6b7a5cd6008e0a83ba497de3d96e41a064bbeead765"
dependencies = [
"cc",
"libc",
"winapi",
]
[[package]] [[package]]
name = "utf8parse" name = "utf8parse"
version = "0.2.2" version = "0.2.2"
@ -2360,12 +2347,6 @@ dependencies = [
"wezterm-dynamic", "wezterm-dynamic",
] ]
[[package]]
name = "widestring"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471"
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"

View file

@ -29,6 +29,7 @@ xz2 = "0.1.7"
bzip2-rs = "0.1.2" bzip2-rs = "0.1.2"
ruzstd = "0.8.2" ruzstd = "0.8.2"
sevenz-rust2 = "0.20.2" sevenz-rust2 = "0.20.2"
rar-stream = "5.7.1"
dialoguer = "0.12.0" dialoguer = "0.12.0"
indicatif = { version = "0.18.3", features = ["rayon"] } indicatif = { version = "0.18.3", features = ["rayon"] }
csv = "1.4.0" csv = "1.4.0"
@ -39,8 +40,6 @@ matchit = "0.9.0"
[target.'cfg(target_env = "musl")'.dependencies] [target.'cfg(target_env = "musl")'.dependencies]
tikv-jemallocator = "0.6.1" tikv-jemallocator = "0.6.1"
[target.'cfg(all(unix, not(target_env = "musl")))'.dependencies]
unrar = "0.5.8"
[profile.dev.package."*"] [profile.dev.package."*"]
opt-level = 3 opt-level = 3

View file

@ -27,9 +27,8 @@ pub enum ReadError {
Zstd(#[from] FrameDecoderError), Zstd(#[from] FrameDecoderError),
#[error(transparent)] #[error(transparent)]
SevenZip(#[from] sevenz_rust2::Error), SevenZip(#[from] sevenz_rust2::Error),
#[cfg(all(unix, not(target_env = "musl")))]
#[error(transparent)] #[error(transparent)]
Rar(#[from] unrar::error::UnrarError), Rar(#[from] rar_stream::error::RarError),
#[error("archive contains no files")] #[error("archive contains no files")]
NoFiles, NoFiles,
#[error("log file contained non-utf8 characters: {0:#}")] #[error("log file contained non-utf8 characters: {0:#}")]

View file

@ -1,51 +1,50 @@
use crate::error::ReadError; use crate::error::ReadError;
use crate::logfile::archive::{Archive, ArchiveEntry}; use crate::logfile::archive::{Archive, ArchiveEntry};
use rar_stream::MemoryArchive;
use std::borrow::Cow; use std::borrow::Cow;
use std::fs::read;
pub struct RarArchive { pub struct RarArchive {
path: String, archive: MemoryArchive,
} }
impl RarArchive { impl RarArchive {
pub fn new(path: &str) -> Result<Self, ReadError> { pub fn new(path: &str) -> Result<Self, ReadError> {
Ok(RarArchive { path: path.into() }) let data = read(path)?;
Ok(RarArchive {
archive: MemoryArchive::from_vec(data)?,
})
} }
} }
pub struct RarEntry { pub struct RarEntry<'a> {
archive: String, archive: &'a MemoryArchive,
index: usize,
name: String, name: String,
} }
impl ArchiveEntry for RarEntry { impl ArchiveEntry for RarEntry<'_> {
fn name<'a>(&'a self) -> Cow<'a, str> { fn name<'a>(&'a self) -> Cow<'a, str> {
self.name.as_str().into() self.name.as_str().into()
} }
fn extract(self) -> Result<Vec<u8>, ReadError> { fn extract(self) -> Result<Vec<u8>, ReadError> {
let mut archive = unrar::Archive::new(&self.archive).open_for_processing()?; Ok(self.archive.extract(self.index)?)
while let Some(header) = archive.read_header()? {
if header.entry().filename.to_string_lossy() == self.name {
return Ok(header.read()?.0);
}
archive = header.skip()?;
}
Err(ReadError::NoFiles)
} }
} }
impl Archive for RarArchive { impl Archive for RarArchive {
type Entry<'a> = RarEntry; type Entry<'a> = RarEntry<'a>;
fn entries(&mut self) -> impl Iterator<Item = Self::Entry<'_>> { fn entries(&mut self) -> impl Iterator<Item = Self::Entry<'_>> {
unrar::Archive::new(&self.path) self.archive
.open_for_listing() .entries_iter()
.into_iter() .enumerate()
.flatten() .filter(|(_, entry)| !entry.is_directory)
.flatten() .map(|(index, entry)| RarEntry {
.map(|header| RarEntry { archive: &self.archive,
archive: self.path.clone(), index,
name: header.filename.to_string_lossy().into(), name: entry.name,
}) })
} }
} }