Expand description
§unrar-ng
Actively maintained fork of
unrar/ muja/unrar.rs. This fork continues active development with performance improvements, extraction progress callbacks, and updates to the latest UnRAR source releases.Add to your
Cargo.toml:[dependencies] unrar-ng = "0.7"Then
use unrar_ng::Archive;in your code.§Breaking change in 0.7
The library targets were renamed:
unrar→unrar_ng,unrar_sys→unrar_ng_sys. Code written against 0.6.x usinguse unrar::Archive;no longer compiles by default. Two migration paths:1. Recommended (clean) — update both Cargo.toml and source:
[dependencies] unrar-ng = "0.7"use unrar_ng::Archive;2. Minimal-change — keep
use unrar::Archive;source by aliasing the dep:[dependencies] unrar = { package = "unrar-ng", version = "0.7" } # Only if you also depend on the FFI crate directly: unrar_sys = { package = "unrar-ng-sys", version = "0.7" }Cargo’s dep-rename mechanism makes the consumer-side
extern crate/usename follow the dep key, regardless of the dependency’s[lib] name, so existinguse unrar::Archive;/use unrar_sys::*;lines continue to work.
High-level wrapper around the unrar C library provided by rarlab.
This library can only extract and list archives, it cannot create them.
§Why This Fork?
The primary motivation for this fork is batch extraction performance.
The original crate uses the UnRAR DLL’s per-file API (RARReadHeaderEx + RARProcessFile), which internally calls SearchBlock(HEAD_FILE) on every iteration. This causes redundant traversal of archive block headers, resulting in severe performance degradation when extracting archives with many small files.
unrar-ng adds a new batch extraction API (extract_all) that uses the same efficient traversal loop as the native unrar CLI, completely eliminating this overhead.
§Performance: 94,000 Files (Linux Kernel Source Archive)
| Method | Time | vs. Native CLI |
|---|---|---|
Native unrar x CLI | 13s | baseline |
Original unrar crate (per-file API) | 73s | 5.6x slower |
unrar-ng extract_all | 13s | same speed |
5.6x faster than the original crate — fully matching native CLI performance.
Test environment: tmpfs (pure in-memory I/O), 12th Gen Intel Core i7.
For a detailed technical analysis, see Batch Extraction Performance Optimization.
§Quick Example
use unrar_ng::Archive;
let archive = Archive::new("large_archive.rar")
.open_for_processing()
.expect("Failed to open archive");
archive.extract_all("./output")
.expect("Failed to extract");Please look inside the examples directory to see how to use this library. Specifically the lister example is well documented and advanced!
Basic example to list archive entries:
use unrar_ng::Archive;
fn main() {
for entry in Archive::new("archive.rar").open_for_listing().unwrap() {
println!("{}", entry.unwrap());
}
}Run this example: cargo run --example basic_list path/to/archive.rar.
You can create an archive by using the rar CLI: rar a archive.rar .
§Overview
The primary type in this crate is Archive
which denotes an archive on the file system. Archive itself makes no
FS operations, unless one of the open methods are called, returning
an OpenArchive.
§Archive
The Archive struct provides two major classes of methods:
- methods that do not touch the FS. These are opinionated utility methods
that are based on RAR path conventions out in the wild. Most commonly, multipart
files usually have extensions such as
.part08.raror.r08.rar. Since extracting must start at the first part, it may be helpful to figure that out using, for instance,archive.as_first_part() - methods that open the underlying path in the specified mode
(possible modes are
List,ListSplitandProcess). These methods have the wordopenin them, are fallible operations, returnOpenArchiveinside aResultand are as follows:open_for_listingandopen_for_listing_split: list the archive entries (skipping over content/payload)open_for_processing: process archive entries as well as content/payloadbreak_open: read archive even if an error is returned, if possible. The [OpenMode] must be provided explicitly.
§OpenArchive
An archive is opened in one of these three modes: List, ListSplit or Process.
This library does not provide random access into archives. Instead, files inside the archive
can only be processed as a stream, unidirectionally, front to back, alternating between
ReadHeader and ProcessFile operations (as dictated by the underlying C++ library).
That is the idea behind cursors:
§OpenArchive: Cursors
Via cursors, the archive keeps track what operation is permitted next:
The library enforces this by making
use of the typestate pattern. An archive, once
opened, starts in the CursorBeforeHeader state and, thus, must have its read_header method
called, which returns a new OpenArchive instance in the CursorBeforeFile state that only
exposes methods that internally map to the ProcessFile operation.
Which methods are accessible in each step depends on the archive’s current state and the
mode it was opened in.
§Available methods for Open mode/Cursor position combinations
Here is an overview of what methods are exposed for the OpenMode/Cursor combinations:
| Open mode↓ ╲ Cursor position→ | before header | before file |
|---|---|---|
List, ListSplit | read_header | skip |
Process | read_header | skip, read, extract, extract_to, extract_with_base |
§OpenArchive: Iterator
Archives opened in List or ListSplit mode also implement Iterator whereas archives in
Process mode do not (though this may change in future releases). That is because the first
two will read and return headers while being forced to skip over the payload whereas the latter
has more sophisticated processing possibilities that’s not easy to convey using an Iterator.
§Example
For more sophisticated examples, please look inside the examples/ folder.
Here’s what a function that returns the first content of a file could look like:
fn first_file_content<P: AsRef<Path>>(path: P) -> UnrarResult<Vec<u8>> {
let archive = Archive::new(&path).open_for_processing()?; // cursor: before header
let archive = archive.read_header()?.expect("empty archive"); // cursor: before file
dbg!(&archive.entry().filename);
let (data, _rest) = archive.read()?; // cursor: before header
Ok(data)
}§Features
- Multipart files
- Listing archives
- Extracting them
- Reading them into memory (without extracting)
- Testing them
- Encrypted archives with password
- Linked statically against the unrar source.
- Build unrar C++ code from source
- Basic functionality that operates on filenames / paths (without reading archives)
- Documentation / RustDoc
- Test Suite
- utilizes type system to enforce correct usage
- Well-designed errors (planned)
- TBD
§Non-Features
As this library is only a wrapper, these following features are not easily feasible and as such not planned:
- Creating archives
- Random access into arbitrary archive entries
- Pure Rust implementation
- Processing archives from a file descriptor / fs::File handle
- Processing archives from a byte stream
§Contributing
Feel free to contribute! If you detect a bug, open an issue.
Pull requests are also welcome!
§Help
If you need help using the library, feel free to create a new discussion or open an issue.
§License
The parts authored by this library’s contributors are licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
The embedded C/C++ library uses its own license. For more informations, see its license file.
Re-exports§
pub use error::UnrarResult;
Modules§
Structs§
- Archive
- A RAR archive on the file system.
- Cursor
Before File - Type parameter for OpenArchive denoting a
process_fileoperation must follow next. - Cursor
Before Header - Type parameter for OpenArchive denoting a
read_headeroperation must follow next. - File
Header - Metadata for an entry in a RAR archive
- List
- An OpenMode for listing RAR archive entries.
- List
Split - An OpenMode for listing RAR archive entries.
- Open
Archive - An open RAR archive that can be read or processed.
- Process
- An OpenMode for processing RAR archive entries.
Enums§
- Extract
Event - Extraction progress event for callbacks during batch extraction.
- Extract
Status - Outcome of
OpenArchive::extract_all_with_callback. - Volume
Info - Volume information on the file that was initially opened.