Skip to main content

ReadOptions

Struct ReadOptions 

Source
pub struct ReadOptions { /* private fields */ }
Expand description

Sector format, retry policy, and read speed options for track, streaming, and sector-range reads.

The defaults read audio sectors using the default retry policy and leave the drive’s current read speed unchanged. Use the builder methods to override only the options you need.

Implementations§

Source§

impl ReadOptions

Source

pub fn with_format(self, format: SectorReadFormat) -> Self

Select the sector format requested from the drive.

Examples found in repository?
examples/save_data_track.rs (line 99)
92fn stream_track_to_file(
93    reader: &CdReader,
94    toc: &Toc,
95    track_no: u8,
96    format: SectorReadFormat,
97    path: &Path,
98) -> Result<u64, Box<dyn std::error::Error>> {
99    let options = ReadOptions::default().with_format(format);
100    let mut stream = reader.open_track_stream_with_options(toc, track_no, &options)?;
101
102    let total_sectors = stream.total_sectors();
103    let mut writer = BufWriter::new(File::create(path)?);
104    let mut written = 0u64;
105
106    while let Some(chunk) = stream.next_chunk()? {
107        writer.write_all(&chunk)?;
108        written += chunk.len() as u64;
109
110        let done = stream.current_sector();
111        let pct = done as f32 / total_sectors as f32 * 100.0;
112        eprint!("\r  {done}/{total_sectors} sectors ({pct:5.1}%)");
113    }
114    eprintln!("\r  {total_sectors}/{total_sectors} sectors (100.0%)");
115
116    writer.flush()?;
117    Ok(written)
118}
More examples
Hide additional examples
examples/read_data_track.rs (line 33)
17fn main() -> Result<(), Box<dyn std::error::Error>> {
18    let reader = CdReader::open_default()?;
19    let toc = reader.read_toc()?;
20
21    let data_track = toc
22        .tracks
23        .iter()
24        .find(|t| !t.is_audio)
25        .ok_or("no data track on this disc (need a mixed-mode / enhanced CD)")?;
26
27    let pvd_lba = data_track.start_lba + PVD_SECTOR_OFFSET;
28    println!(
29        "Data track #{} starts at LBA {}; reading PVD at LBA {}\n",
30        data_track.number, data_track.start_lba, pvd_lba
31    );
32
33    let mut options = ReadOptions::default().with_format(SectorReadFormat::Mode1Raw);
34
35    // --- raw read (2352 B) -------------------------------------------------
36    let raw = reader.read_sector_range(pvd_lba, 1, &options)?;
37    if raw.len() != 2352 {
38        return Err(format!("raw read returned {} bytes, expected 2352", raw.len()).into());
39    }
40
41    let sync_ok = raw[0] == 0x00 && raw[1..11].iter().all(|&b| b == 0xFF) && raw[11] == 0x00;
42    let mode = raw[15];
43    println!("raw sync pattern : {}", pass(sync_ok));
44    println!("raw sector mode  : Mode {mode}");
45
46    // User data sits after sync(12) + header(4) for Mode 1, and additionally
47    // after an 8-byte subheader for Mode 2 Form 1.
48    let user_offset = match mode {
49        1 => 16,
50        2 => 24,
51        other => return Err(format!("unexpected sector mode {other}").into()),
52    };
53    let raw_user = &raw[user_offset..user_offset + 2048];
54
55    let iso_ok = raw_user[0] == 0x01 && &raw_user[1..6] == b"CD001";
56    println!("ISO 9660 'CD001' : {}", pass(iso_ok));
57
58    // --- cooked read (2048 B) ---------------------------------------------
59    // The cooked format is specifically Mode 1, so only cross-check it there.
60    if mode == 1 {
61        options = options.with_format(SectorReadFormat::Mode1Cooked);
62        let cooked = reader.read_sector_range(pvd_lba, 1, &options)?;
63        if cooked.len() != 2048 {
64            return Err(
65                format!("cooked read returned {} bytes, expected 2048", cooked.len()).into(),
66            );
67        }
68        let matches_raw = cooked == raw_user;
69        println!("cooked == raw[16..]: {}", pass(matches_raw));
70
71        if sync_ok && iso_ok && matches_raw {
72            println!("\nALL CHECKS PASSED — cooked and raw data reads are correct.");
73            return Ok(());
74        }
75    } else {
76        println!(
77            "\nData track is Mode {mode} (e.g. CD-Extra / Mode 2 Form 1). The cooked path \
78             targets Mode 1, so only the raw checks apply here."
79        );
80        if sync_ok && iso_ok {
81            println!("Raw read verified against on-disc ISO structure.");
82            return Ok(());
83        }
84    }
85
86    Err("one or more verification checks FAILED — see output above".into())
87}
Source

pub fn with_retry(self, retry: RetryConfig) -> Self

Set the retry policy applied to each read command.

Examples found in repository?
examples/custom_retry.rs (line 31)
12fn main() -> Result<(), Box<dyn std::error::Error>> {
13    let output_dir = common::fresh_output_dir("custom_retry")?;
14    let reader = CdReader::open_default()?;
15    let toc = reader.read_toc()?;
16
17    let first_audio = toc
18        .tracks
19        .iter()
20        .find(|t| t.is_audio)
21        .ok_or("no audio tracks found")?;
22
23    // More attempts, longer backoff, and sector reduction down to 1
24    // for maximum resilience on scratched media.
25    let retry = RetryConfig::default()
26        .with_max_attempts(8)
27        .with_initial_backoff(Duration::from_millis(50))
28        .with_max_backoff(Duration::from_secs(1))
29        .with_chunk_reduction(true)
30        .with_min_sectors_per_read(1);
31    let options = ReadOptions::default().with_retry(retry);
32
33    println!(
34        "Reading track {} with aggressive retry...",
35        first_audio.number
36    );
37    let data = reader.read_track_with_options(&toc, first_audio.number, &options)?;
38
39    let wav = create_wav(data);
40    let output_path = output_dir.join(format!("track{:02}.wav", first_audio.number));
41    std::fs::write(&output_path, wav)?;
42    println!("Saved {}", output_path.display());
43
44    Ok(())
45}
Source

pub fn with_read_speed(self, read_speed: ReadSpeed) -> Self

Set the read speed to request from the drive. See ReadSpeed for details. Streaming reads apply this request once when the stream is opened.

§Note

For simplicity, this crate doesn’t restore the previous speed setting.

Examples found in repository?
examples/read_speed.rs (line 36)
7fn main() -> Result<(), Box<dyn std::error::Error>> {
8    let reader = CdReader::open_default()?;
9    let toc = reader.read_toc()?;
10
11    let first_audio = toc
12        .tracks
13        .iter()
14        .find(|track| track.is_audio)
15        .ok_or("no audio tracks found")?;
16
17    // Keep "unchanged" immediately after 1x so it tests whether the previous speed
18    // request remains in effect when no new speed command is sent.
19    let speed_tests = [
20        ("1x", ReadSpeed::CustomMultiplier(1)),
21        ("unchanged (after 1x)", ReadSpeed::Unchanged),
22        ("10x", ReadSpeed::CustomMultiplier(10)),
23        ("30x", ReadSpeed::CustomMultiplier(30)),
24        ("optimal", ReadSpeed::Optimal),
25    ];
26
27    println!(
28        "Reading audio track {} {} times\n",
29        first_audio.number,
30        speed_tests.len()
31    );
32
33    let mut timings = Vec::with_capacity(speed_tests.len());
34
35    for (label, read_speed) in speed_tests {
36        let options = ReadOptions::default().with_read_speed(read_speed);
37
38        println!("Reading at {label}...");
39        let started = Instant::now();
40        let data = reader.read_track_with_options(&toc, first_audio.number, &options)?;
41        let elapsed = started.elapsed();
42
43        println!(
44            "Read {} bytes in {:.3} seconds\n",
45            data.len(),
46            elapsed.as_secs_f64()
47        );
48        timings.push((label, elapsed));
49    }
50
51    println!("Timing summary:");
52    for (label, elapsed) in timings {
53        println!("  {label:<22} {:>10.3} seconds", elapsed.as_secs_f64());
54    }
55
56    Ok(())
57}

Trait Implementations§

Source§

impl Clone for ReadOptions

Source§

fn clone(&self) -> ReadOptions

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ReadOptions

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ReadOptions

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.