From 70dae04ff639581da85c7742085099bd704c5fd8 Mon Sep 17 00:00:00 2001 From: Seva Zaikov Date: Sat, 11 Jul 2026 22:05:39 -0700 Subject: [PATCH] add builder API for ReadOptions --- examples/custom_retry.rs | 16 ++++++------- examples/read_data_track.rs | 7 ++---- src/data_reader.rs | 46 +++++++++++++++++++++++++++++++------ src/lib.rs | 7 +++--- src/stream.rs | 7 +++--- 5 files changed, 55 insertions(+), 28 deletions(-) diff --git a/examples/custom_retry.rs b/examples/custom_retry.rs index 4f6ee03..78646e2 100644 --- a/examples/custom_retry.rs +++ b/examples/custom_retry.rs @@ -17,16 +17,14 @@ fn main() -> Result<(), Box> { // More attempts, longer backoff, and sector reduction down to 1 // for maximum resilience on scratched media. - let options = ReadOptions { - retry: RetryConfig { - max_attempts: 8, - initial_backoff_ms: 50, - max_backoff_ms: 1000, - reduce_chunk_on_retry: true, - min_sectors_per_read: 1, - }, - ..ReadOptions::default() + let retry = RetryConfig { + max_attempts: 8, + initial_backoff_ms: 50, + max_backoff_ms: 1000, + reduce_chunk_on_retry: true, + min_sectors_per_read: 1, }; + let options = ReadOptions::default().with_retry(retry); println!( "Reading track {} with aggressive retry...", diff --git a/examples/read_data_track.rs b/examples/read_data_track.rs index 5036848..d2866d0 100644 --- a/examples/read_data_track.rs +++ b/examples/read_data_track.rs @@ -30,10 +30,7 @@ fn main() -> Result<(), Box> { data_track.number, data_track.start_lba, pvd_lba ); - let mut options = ReadOptions { - mode: SectorReadMode::DataRaw, - ..ReadOptions::default() - }; + let mut options = ReadOptions::default().with_mode(SectorReadMode::DataRaw); // --- raw read (2352 B) ------------------------------------------------- let raw = reader.read_sector_range(pvd_lba, 1, &options)?; @@ -61,7 +58,7 @@ fn main() -> Result<(), Box> { // --- cooked read (2048 B) --------------------------------------------- // Our cooked mode maps to Mode 1; only meaningful to cross-check there. if mode == 1 { - options.mode = SectorReadMode::DataCooked; + options = options.with_mode(SectorReadMode::DataCooked); let cooked = reader.read_sector_range(pvd_lba, 1, &options)?; if cooked.len() != 2048 { return Err( diff --git a/src/data_reader.rs b/src/data_reader.rs index 94bfb1b..25d20fa 100644 --- a/src/data_reader.rs +++ b/src/data_reader.rs @@ -1,12 +1,35 @@ use crate::retry::RetryConfig; /// Sector format and retry options for track and sector-range reads. +/// +/// The defaults read audio sectors using the default retry policy. Use the +/// builder methods to override only the options you need. #[derive(Debug, Clone)] pub struct ReadOptions { - /// Sector format requested from the drive. - pub mode: SectorReadMode, - /// Retry policy applied to each read command. - pub retry: RetryConfig, + mode: SectorReadMode, + retry: RetryConfig, +} + +impl ReadOptions { + /// Select the sector format requested from the drive. + pub fn with_mode(mut self, mode: SectorReadMode) -> Self { + self.mode = mode; + self + } + + /// Set the retry policy applied to each read command. + pub fn with_retry(mut self, retry: RetryConfig) -> Self { + self.retry = retry; + self + } + + pub(crate) fn mode(&self) -> SectorReadMode { + self.mode + } + + pub(crate) fn retry(&self) -> &RetryConfig { + &self.retry + } } impl Default for ReadOptions { @@ -104,10 +127,19 @@ mod tests { use super::{ReadOptions, SectorReadMode, build_read_cd_cdb}; #[test] - fn read_options_default_to_audio() { - let options = ReadOptions::default(); + fn read_options_builders_override_individual_defaults() { + assert_eq!(ReadOptions::default().mode(), SectorReadMode::Audio); + + let retry = crate::RetryConfig { + max_attempts: 9, + ..crate::RetryConfig::default() + }; + let options = ReadOptions::default() + .with_mode(SectorReadMode::DataRaw) + .with_retry(retry); - assert_eq!(options.mode, SectorReadMode::Audio); + assert_eq!(options.mode(), SectorReadMode::DataRaw); + assert_eq!(options.retry().max_attempts, 9); } #[test] diff --git a/src/lib.rs b/src/lib.rs index 7965f8e..10b72f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -271,12 +271,13 @@ impl CdReader { sectors: u32, options: &ReadOptions, ) -> Result, CdReaderError> { + let mode = options.mode(); read_loop::read_sectors_chunked( start_lba, sectors, - options.mode, - &options.retry, - |lba, chunk_sectors| self.drive.read_cd_chunk(lba, chunk_sectors, options.mode), + mode, + options.retry(), + |lba, chunk_sectors| self.drive.read_cd_chunk(lba, chunk_sectors, mode), ) } } diff --git a/src/stream.rs b/src/stream.rs index b9d6d14..9e8dab8 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -68,10 +68,9 @@ impl<'a> TrackStream<'a> { /// depend on the [`SectorReadMode`] selected in [`TrackStreamOptions`]. pub fn next_chunk(&mut self) -> Result>, CdReaderError> { self.next_chunk_with(|lba, sectors, mode, retry| { - let options = ReadOptions { - mode, - retry: retry.clone(), - }; + let options = ReadOptions::default() + .with_mode(mode) + .with_retry(retry.clone()); self.reader.read_sector_range(lba, sectors, &options) }) }