Skip to content

Commit c01f56f

Browse files
committed
💥 Unify solid entry reads with ReadOptions
Replace password-only solid entry APIs with ReadOptions-based variants so normal and solid reads share the same configuration and derived-key cache. Update CLI callers, examples, fuzz coverage, and tests for the unified API.
1 parent e4fffe4 commit c01f56f

17 files changed

Lines changed: 159 additions & 101 deletions

File tree

cli/src/command/core.rs

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use path_slash::*;
2828
pub(crate) use path_transformer::PathTransformers;
2929
use pna::{
3030
Archive, EntryBuilder, EntryPart, LinkTargetType, MIN_CHUNK_BYTES_SIZE, NormalEntry,
31-
PNA_HEADER, ReadEntry, SolidEntryBuilder, WriteOptions, prelude::*,
31+
PNA_HEADER, ReadEntry, ReadOptions, SolidEntryBuilder, WriteOptions, prelude::*,
3232
};
3333
use std::{
3434
borrow::Cow,
@@ -1290,7 +1290,7 @@ impl TransformStrategy for TransformStrategyUnSolid {
12901290
{
12911291
match read_entry? {
12921292
ReadEntry::Solid(s) => {
1293-
for n in s.entries(password)? {
1293+
for n in s.entries(ReadOptions::with_password(password))? {
12941294
if let Some(entry) = transformer(n.map(Into::into))? {
12951295
archive.add_entry(entry)?;
12961296
}
@@ -1334,7 +1334,7 @@ impl TransformStrategy for TransformStrategyKeepSolid {
13341334
.password(password)
13351335
.build(),
13361336
)?;
1337-
for n in s.entries(password)? {
1337+
for n in s.entries(ReadOptions::with_password(password))? {
13381338
if let Some(entry) = transformer(n.map(Into::into))? {
13391339
builder.add_entry(entry)?;
13401340
}
@@ -1426,43 +1426,39 @@ where
14261426
Ok(())
14271427
}
14281428

1429-
pub(crate) fn run_process_archive<'p, Provider, F>(
1429+
pub(crate) fn run_process_archive<F>(
14301430
archive_provider: impl IntoIterator<Item = impl Read>,
1431-
mut password_provider: Provider,
1431+
read_options: &ReadOptions,
14321432
mut processor: F,
14331433
allow_concatenated_archives: bool,
14341434
) -> io::Result<()>
14351435
where
1436-
Provider: FnMut() -> Option<&'p [u8]>,
14371436
F: FnMut(io::Result<NormalEntry>) -> io::Result<()>,
14381437
{
1439-
let password = password_provider();
14401438
run_read_entries(
14411439
archive_provider,
14421440
|entry| match entry? {
1443-
ReadEntry::Solid(solid) => solid.entries(password)?.try_for_each(&mut processor),
1441+
ReadEntry::Solid(solid) => solid.entries(read_options)?.try_for_each(&mut processor),
14441442
ReadEntry::Normal(regular) => processor(Ok(regular)),
14451443
},
14461444
allow_concatenated_archives,
14471445
)
14481446
}
14491447

1450-
pub(crate) fn run_process_archive_stoppable<'p, Provider, F>(
1448+
pub(crate) fn run_process_archive_stoppable<F>(
14511449
archive_provider: impl IntoIterator<Item = impl Read>,
1452-
mut password_provider: Provider,
1450+
read_options: &ReadOptions,
14531451
mut processor: F,
14541452
allow_concatenated_archives: bool,
14551453
) -> io::Result<()>
14561454
where
1457-
Provider: FnMut() -> Option<&'p [u8]>,
14581455
F: FnMut(io::Result<NormalEntry>) -> io::Result<ProcessAction>,
14591456
{
1460-
let password = password_provider();
14611457
run_read_entries_stoppable(
14621458
archive_provider,
14631459
|entry| match entry? {
14641460
ReadEntry::Solid(solid) => {
1465-
for n in solid.entries(password)? {
1461+
for n in solid.entries(read_options)? {
14661462
match processor(n)? {
14671463
ProcessAction::Continue => {}
14681464
ProcessAction::Stop => return Ok(ProcessAction::Stop),
@@ -1530,21 +1526,19 @@ where
15301526
}
15311527

15321528
#[cfg(feature = "memmap")]
1533-
pub(crate) fn run_entries<'d, 'p, Provider, F>(
1529+
pub(crate) fn run_entries<'d, F>(
15341530
archives: impl IntoIterator<Item = &'d [u8]>,
1535-
mut password_provider: Provider,
1531+
read_options: &ReadOptions,
15361532
mut processor: F,
15371533
) -> io::Result<()>
15381534
where
1539-
Provider: FnMut() -> Option<&'p [u8]>,
15401535
F: FnMut(io::Result<NormalEntry<Cow<'d, [u8]>>>) -> io::Result<()>,
15411536
{
1542-
let password = password_provider();
15431537
run_read_entries_mem(
15441538
archives,
15451539
|entry| match entry? {
15461540
ReadEntry::Solid(s) => s
1547-
.entries(password)?
1541+
.entries(read_options)?
15481542
.try_for_each(|r| processor(r.map(Into::into))),
15491543
ReadEntry::Normal(r) => processor(Ok(r)),
15501544
},
@@ -1615,21 +1609,19 @@ where
16151609
}
16161610

16171611
#[cfg(feature = "memmap")]
1618-
pub(crate) fn run_entries_stoppable<'d, 'p, Provider, F>(
1612+
pub(crate) fn run_entries_stoppable<'d, F>(
16191613
archives: impl IntoIterator<Item = &'d [u8]>,
1620-
mut password_provider: Provider,
1614+
read_options: &ReadOptions,
16211615
mut processor: F,
16221616
) -> io::Result<()>
16231617
where
1624-
Provider: FnMut() -> Option<&'p [u8]>,
16251618
F: FnMut(io::Result<NormalEntry<Cow<'d, [u8]>>>) -> io::Result<ProcessAction>,
16261619
{
1627-
let password = password_provider();
16281620
run_read_entries_mem_stoppable(
16291621
archives,
16301622
|entry| match entry? {
16311623
ReadEntry::Solid(s) => {
1632-
for n in s.entries(password)? {
1624+
for n in s.entries(read_options)? {
16331625
match processor(n.map(Into::into))? {
16341626
ProcessAction::Continue => {}
16351627
ProcessAction::Stop => return Ok(ProcessAction::Stop),
@@ -1898,9 +1890,10 @@ pub(crate) fn transform_archive_entries<R: io::Read>(
18981890
allow_concatenated_archives: bool,
18991891
) -> io::Result<Vec<io::Result<Option<NormalEntry>>>> {
19001892
let mut results = Vec::new();
1893+
let read_options = ReadOptions::with_password(password);
19011894
run_process_archive(
19021895
std::iter::once(reader),
1903-
|| password,
1896+
&read_options,
19041897
|entry| {
19051898
let entry = entry?;
19061899
if filter.excluded(entry.header().path()) {

cli/src/command/core/archive_source.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
use std::borrow::Cow;
33
use std::{fs, io};
44

5-
use pna::{NormalEntry, ReadEntry};
5+
use pna::{NormalEntry, ReadEntry, ReadOptions};
66

77
use super::TransformStrategy;
88

@@ -81,7 +81,8 @@ impl SplitArchiveReader {
8181
password: Option<&[u8]>,
8282
processor: impl FnMut(io::Result<NormalEntry>) -> io::Result<()>,
8383
) -> io::Result<()> {
84-
super::run_process_archive(self.files.drain(..), || password, processor, false)
84+
let read_options = ReadOptions::with_password(password);
85+
super::run_process_archive(self.files.drain(..), &read_options, processor, false)
8586
}
8687

8788
#[cfg(feature = "memmap")]
@@ -90,11 +91,12 @@ impl SplitArchiveReader {
9091
password: Option<&[u8]>,
9192
mut processor: impl FnMut(io::Result<NormalEntry<Cow<'s, [u8]>>>) -> io::Result<()>,
9293
) -> io::Result<()> {
94+
let read_options = ReadOptions::with_password(password);
9395
super::run_read_entries_mem(
9496
self.mmaps.iter().map(|m| m.as_ref()),
9597
|entry| match entry? {
9698
ReadEntry::Solid(s) => s
97-
.entries(password)?
99+
.entries(&read_options)?
98100
.try_for_each(|r| processor(r.map(Into::into))),
99101
ReadEntry::Normal(n) => processor(Ok(n)),
100102
},

cli/src/command/extract.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -613,7 +613,7 @@ where
613613
if fast_read && !globs.is_empty() {
614614
run_process_archive_stoppable(
615615
reader,
616-
password_provider,
616+
read_options,
617617
|entry| {
618618
let item = entry.map_err(|e| {
619619
io::Error::new(e.kind(), format!("reading archive entry: {e}"))
@@ -679,7 +679,7 @@ where
679679
} else {
680680
run_process_archive(
681681
reader,
682-
password_provider,
682+
read_options,
683683
|entry| {
684684
let item = entry.map_err(|e| {
685685
io::Error::new(e.kind(), format!("reading archive entry: {e}"))
@@ -740,7 +740,7 @@ where
740740
if fast_read && !globs.is_empty() {
741741
run_process_archive_stoppable(
742742
reader,
743-
password_provider,
743+
read_options,
744744
|entry| {
745745
let item = entry.map_err(|e| {
746746
io::Error::new(e.kind(), format!("reading archive entry: {e}"))
@@ -795,7 +795,7 @@ where
795795
} else {
796796
run_process_archive(
797797
reader,
798-
password_provider,
798+
read_options,
799799
|entry| {
800800
let item = entry.map_err(|e| {
801801
io::Error::new(e.kind(), format!("reading archive entry: {e}"))
@@ -883,7 +883,7 @@ where
883883
rayon::scope_fifo(|s| -> anyhow::Result<()> {
884884
if fast_read && !globs.is_empty() {
885885
#[hooq::skip_all]
886-
run_entries_stoppable(archives, password_provider, |entry| {
886+
run_entries_stoppable(archives, read_options, |entry| {
887887
let item = entry
888888
.map_err(|e| io::Error::new(e.kind(), format!("reading archive entry: {e}")))?;
889889
let item_path = item.name().to_string();
@@ -943,7 +943,7 @@ where
943943
.with_context(|| "streaming archive entries")?;
944944
} else {
945945
#[hooq::skip_all]
946-
run_entries(archives, password_provider, |entry| {
946+
run_entries(archives, read_options, |entry| {
947947
let item = entry
948948
.map_err(|e| io::Error::new(e.kind(), format!("reading archive entry: {e}")))?;
949949
let Some(name) = filter_entry(&item, &mut globs, &args) else {

cli/src/command/list.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,7 @@ fn list_archive(ctx: &crate::cli::GlobalContext, args: ListCommand) -> anyhow::R
551551
|entry| {
552552
match entry? {
553553
ReadEntry::Solid(solid) if options.solid => {
554-
for entry in solid.entries(password)? {
554+
for entry in solid.entries(&read_options)? {
555555
entries.push(TableRow::from_entry(
556556
&entry?,
557557
&read_options,
@@ -659,7 +659,7 @@ pub(crate) fn run_list_archive<'a>(
659659
|entry| {
660660
match entry? {
661661
ReadEntry::Solid(solid) if args.solid => {
662-
for entry in solid.entries(password)? {
662+
for entry in solid.entries(&read_options)? {
663663
entries.push(TableRow::from_entry(
664664
&entry?,
665665
&read_options,
@@ -695,7 +695,7 @@ pub(crate) fn run_list_archive<'a>(
695695
|entry| {
696696
match entry? {
697697
ReadEntry::Solid(solid) if args.solid => {
698-
for entry in solid.entries(password)? {
698+
for entry in solid.entries(&read_options)? {
699699
let entry = entry?;
700700
let entry_path = entry.name().to_string();
701701
if !globs.matches_any_pattern(&entry_path) {

cli/src/command/verify.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ fn verify_solid(
127127
fast: bool,
128128
report: &mut VerifyReport,
129129
) -> io::Result<()> {
130-
for entry in solid.entries(password)? {
130+
for entry in solid.entries(read_options)? {
131131
verify_entry(&entry?, password, read_options, fast, report);
132132
}
133133
Ok(())

cli/tests/cli/stdio/option_ignore_zeros.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ fn read_archive_entries(path: impl AsRef<Path>) -> Vec<(String, String)> {
6868
let mut archive = Archive::read_header(fs::File::open(path).unwrap()).unwrap();
6969
archive
7070
.entries()
71-
.extract_solid_entries(None)
71+
.extract_solid_entries(&ReadOptions::builder().build())
7272
.map(|entry| {
7373
let entry = entry.unwrap();
7474
let mut reader = entry.reader(ReadOptions::builder().build()).unwrap();
@@ -89,13 +89,18 @@ fn read_all_archive_entries_from_bytes(bytes: &[u8]) -> Vec<(String, String)> {
8989
Err(err) if err.kind() == std::io::ErrorKind::UnexpectedEof => break,
9090
Err(err) => panic!("unexpected archive read error: {err}"),
9191
};
92-
entries.extend(archive.entries().extract_solid_entries(None).map(|entry| {
93-
let entry = entry.unwrap();
94-
let mut reader = entry.reader(ReadOptions::builder().build()).unwrap();
95-
let mut content = String::new();
96-
reader.read_to_string(&mut content).unwrap();
97-
(entry.name().to_string(), content)
98-
}));
92+
entries.extend(
93+
archive
94+
.entries()
95+
.extract_solid_entries(&ReadOptions::builder().build())
96+
.map(|entry| {
97+
let entry = entry.unwrap();
98+
let mut reader = entry.reader(ReadOptions::builder().build()).unwrap();
99+
let mut content = String::new();
100+
reader.read_to_string(&mut content).unwrap();
101+
(entry.name().to_string(), content)
102+
}),
103+
);
99104
let _ = archive.into_inner();
100105
}
101106

cli/tests/cli/stdio/option_mac_metadata.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ fn read_archive_entries(bytes: &[u8]) -> Vec<(String, String)> {
1111
let mut archive = Archive::read_header(Cursor::new(bytes)).unwrap();
1212
archive
1313
.entries()
14-
.extract_solid_entries(None)
14+
.extract_solid_entries(&ReadOptions::builder().build())
1515
.map(|entry| {
1616
let entry = entry.unwrap();
1717
let mut reader = entry.reader(ReadOptions::builder().build()).unwrap();

cli/tests/cli/utils/archive.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use pna::ReadOptions;
12
use pna::prelude::*;
23
use std::{
34
fs::File,
@@ -113,7 +114,9 @@ pub fn extract_single_entry(
113114
name: &str,
114115
) -> io::Result<Option<pna::NormalEntry>> {
115116
let mut archive = pna::Archive::open(path)?;
116-
let entries = archive.entries().extract_solid_entries(None);
117+
let entries = archive
118+
.entries()
119+
.extract_solid_entries(&ReadOptions::builder().build());
117120
for entry in entries {
118121
let entry = entry?;
119122
if entry.header().path() == name {
@@ -140,7 +143,8 @@ where
140143
{
141144
let password = password.into().map(|p| p.as_bytes());
142145
let mut archive = pna::Archive::open(path)?;
143-
let entries = archive.entries().extract_solid_entries(password);
146+
let read_options = ReadOptions::with_password(password);
147+
let entries = archive.entries().extract_solid_entries(&read_options);
144148
for entry in entries {
145149
f(entry?);
146150
}

fuzz/fuzz_targets/split_archive.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ fuzz_target!(|data: (&[u8], usize)| {
3434
let archive_bytes = archive.finalize().unwrap();
3535
let mut archive = Archive::read_header_from_slice(&archive_bytes).unwrap();
3636

37-
for entry in archive.entries_slice().extract_solid_entries(None) {
37+
for entry in archive
38+
.entries_slice()
39+
.extract_solid_entries(&ReadOptions::builder().build())
40+
{
3841
let entry = entry.unwrap();
3942
let read_option = ReadOptions::builder().build();
4043
let mut reader = entry.reader(read_option).unwrap();

lib/examples/async_io.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ async fn extract(path: String) -> io::Result<()> {
3838
while let Some(entry) = archive.read_entry_async().await? {
3939
match entry {
4040
ReadEntry::Solid(solid_entry) => {
41-
for entry in solid_entry.entries(None)? {
41+
for entry in solid_entry.entries(ReadOptions::builder().build())? {
4242
let entry = entry?;
4343
let mut file = io::Cursor::new(Vec::new());
4444
let mut reader = entry.reader(ReadOptions::builder().build())?.compat();

0 commit comments

Comments
 (0)