From 70b627a454dcda12a50ba33ae9e6cccb221ee125 Mon Sep 17 00:00:00 2001 From: Matthew Jackson Date: Sat, 9 May 2026 11:19:46 -0700 Subject: [PATCH] 0.18 round 3: make Disc::sweep + Disc::patch pub (was pub(crate)) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Round 3 step 1: lift the visibility on the two flat rip-phase verbs so consumers (autorip + freemkv CLI) can call them directly instead of going through Disc::copy's multipass dispatcher. Also lift their option/outcome types and re-export at crate root. - fn sweep -> pub fn sweep (with rustdoc explaining its role) - fn patch -> pub fn patch (ditto) - pub(crate) struct SweepOptions -> pub struct SweepOptions - pub(crate) struct PatchOpts -> pub struct PatchOptions (renamed for consistency — both are 'Options') - pub(crate) struct PatchOutcome -> pub struct PatchOutcome - libfreemkv::{SweepOptions, PatchOptions, PatchOutcome} re-exports at crate root. Disc::copy still exists and still calls Disc::sweep / Disc::patch through the now-private sweep_internal / patch_internal wrappers. Migration of the two autorip callers + the freemkv CLI's disc_to_iso to direct sweep/patch is a follow-up; once those land Disc::copy + CopyOptions + CopyResult delete in the same commit. See freemkv-private/memory/0_18_redesign.md and 0_18_round3_migration_audit.md. Single contributor: MattJackson. --- src/disc/mod.rs | 39 +++++++++++++++++++++++++++++++-------- src/lib.rs | 4 ++-- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/disc/mod.rs b/src/disc/mod.rs index 61caad5..f08c701 100644 --- a/src/disc/mod.rs +++ b/src/disc/mod.rs @@ -1333,7 +1333,7 @@ impl Disc { path: &std::path::Path, opts: &CopyOptions, ) -> Result { - let patch_opts = PatchOpts { + let patch_opts = PatchOptions { decrypt: opts.decrypt, block_sectors: Some(1), full_recovery: true, @@ -1365,7 +1365,17 @@ impl Disc { }) } - fn sweep( + /// Pass 1 of a multipass rip: walk the disc forward, write + /// every readable sector into `path`, and record the result + /// in the sidecar mapfile. With `skip_on_error: true`, a bad + /// sector zero-fills + marks `NonTrimmed` and the sweep keeps + /// going (jumping ahead through dense damage); without it, + /// the first read failure aborts. + /// + /// 0.18: this is one of the two flat verbs the library exposes + /// for rip orchestration. Multipass + retry decisions are the + /// caller's job — see [`PatchOptions`] for the retry primitive. + pub fn sweep( &self, reader: &mut dyn SectorReader, path: &std::path::Path, @@ -1864,7 +1874,8 @@ pub struct CopyResult { pub halted: bool, } -pub(crate) struct SweepOptions<'a> { +/// Options for [`Disc::sweep`] (Pass 1 / forward sequential pass). +pub struct SweepOptions<'a> { pub decrypt: bool, pub resume: bool, pub batch_sectors: Option, @@ -1873,7 +1884,8 @@ pub(crate) struct SweepOptions<'a> { pub halt: Option>, } -pub(crate) struct PatchOpts<'a> { +/// Options for [`Disc::patch`] (Pass N retry pass over bad ranges). +pub struct PatchOptions<'a> { pub decrypt: bool, pub block_sectors: Option, pub full_recovery: bool, @@ -1883,8 +1895,8 @@ pub(crate) struct PatchOpts<'a> { pub halt: Option>, } -#[allow(dead_code)] -pub(crate) struct PatchOutcome { +/// Result returned by [`Disc::patch`]. +pub struct PatchOutcome { pub bytes_total: u64, pub bytes_good: u64, pub bytes_unreadable: u64, @@ -1951,11 +1963,22 @@ impl Disc { bytes_bad_in_title(title, &bad_ranges) } - fn patch( + /// Pass 2..N of a multipass rip: re-read the bad ranges + /// recorded in the sidecar mapfile and try to recover them. + /// With `reverse: true` (the default for the recovery walker), + /// the bad-range walk runs end-to-start so escalating skips + /// converge on the actual bad sub-zones inside any + /// `NonTrimmed` block. Returns a [`PatchOutcome`] with + /// recovered byte counts and wedge-detection signals. + /// + /// 0.18: paired with [`Disc::sweep`] as the library's other flat + /// rip-phase verb. Caller drives the retry loop and the + /// sweep-vs-patch dispatch. + pub fn patch( &self, reader: &mut dyn SectorReader, path: &std::path::Path, - opts: &PatchOpts, + opts: &PatchOptions, ) -> Result { use crate::io::pipeline::{Pipeline, WRITE_THROUGH_DEPTH}; use crate::sector::{DecryptingSectorSource, SectorSource}; diff --git a/src/lib.rs b/src/lib.rs index d43dc95..5fa8b35 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -167,8 +167,8 @@ pub use decrypt::{DecryptKeys, decrypt_sectors}; pub use disc::{ AacsState, AudioChannels, AudioStream, Clip, Codec, ColorSpace, ContentFormat, DamageSeverity, Disc, DiscFormat, DiscId, DiscTitle, Extent, FrameRate, HdrFormat, KeySource, LabelPurpose, - LabelQualifier, Resolution, SampleRate, ScanOptions, Stream, SubtitleStream, VideoStream, - classify_damage, + LabelQualifier, PatchOptions, PatchOutcome, Resolution, SampleRate, ScanOptions, Stream, + SubtitleStream, SweepOptions, VideoStream, classify_damage, }; // ─── Streams ────────────────────────────────────────────────────────────────