Audit pass against the project docs "no English text in library code" rule.
Found 9 call sites that violated the contract by stuffing English into
io::Error::new(kind, "…") or by abusing Error::DeviceNotFound { path }
as a free-form description field. Each is now a typed Error variant.
New variants and codes: ScsiInterfaceUnavailable (E1004), DeviceLocked
(E1005), IoKitPluginFailed (E1006), UnsupportedPlatform (E2003),
PlatformNotImplemented (E2004), MapfileInvalid (E6011), DiscUrlNotDirect
(E9009).
labels::apply() previously pushed Commentary/Descriptive/Score/IME and
" (Secondary)" English literals into AudioStream.label, leaking into
MKV titles + autorip UI. AudioStream now exposes structured `purpose:
LabelPurpose`, SubtitleStream `qualifier: LabelQualifier`. Callers
translate to localized text. label keeps codec-formatting only.
API hygiene: 11 mux/* modules dropped from `pub` to `pub(crate)` —
their *types* are still re-exported from lib.rs, but the modules were
leaking low-level EBML/TS/network primitives. Stream trait gets a real
rustdoc explaining read-vs-write split. lib.rs grouped re-exports into
documented sections. ScanOptions::with_keydb() removed (one-method-per-
action rule); use struct literal.
Dead-code sweep: removed lookahead.rs (orphan, never declared as mod),
tsreader.rs (TsDemuxReader unused), ebml::{write_int,read_vint,SEEK_*},
ts::{scan_first/last_pts,scan_duration,SCAN_HEAD/TAIL_SIZE,take/set_
remainder}, MkvMuxer codec_private_slots/filled fields and
fill_codec_private method (deferred-codecPrivate path never used since
the v0.10 PES rewrite). cargo clippy --all-targets -D warnings clean.
Tests: new error::tests for variant codes + Display "no English" guard +
io::ErrorKind mapping. 233 lib tests, all green (was 230).
Breaking: ScanOptions::with_keydb removed; mux/* modules pub(crate);
AudioStream and SubtitleStream gained required fields; UnsupportedDrive
{ product_revision: "Renesas not yet implemented" } no longer produced
(use PlatformNotImplemented).
89 lines
2.8 KiB
Rust
89 lines
2.8 KiB
Rust
// Mimics ISO dump exactly — read + write + progress
|
|
use libfreemkv::Drive;
|
|
use std::io::Write;
|
|
use std::path::Path;
|
|
use std::time::Instant;
|
|
|
|
fn main() {
|
|
let device = std::env::args()
|
|
.skip(1)
|
|
.find(|a| !a.starts_with('-'))
|
|
.unwrap_or_else(|| {
|
|
let drives = libfreemkv::find_drives();
|
|
if drives.is_empty() {
|
|
eprintln!("No drives found");
|
|
std::process::exit(1);
|
|
}
|
|
drives[0].device_path().to_string()
|
|
});
|
|
|
|
let mut drive = Drive::open(Path::new(&device)).unwrap_or_else(|e| {
|
|
eprintln!("Cannot open {}: {}", device, e);
|
|
std::process::exit(1);
|
|
});
|
|
eprintln!("wait_ready...");
|
|
let _ = drive.wait_ready();
|
|
eprintln!("read_capacity...");
|
|
let cap = drive.read_capacity().unwrap();
|
|
eprintln!("capacity: {} sectors", cap);
|
|
|
|
let batch = libfreemkv::disc::detect_max_batch_sectors(drive.device_path());
|
|
let mut buf = vec![0u8; batch as usize * 2048];
|
|
|
|
// Open /dev/null writer like ISO dump does
|
|
let file = std::fs::File::create("/dev/null").unwrap();
|
|
let mut writer = std::io::BufWriter::with_capacity(4 * 1024 * 1024, file);
|
|
|
|
eprintln!(
|
|
"Reading 1000 batches ({:.1} MB) with write + progress...",
|
|
1000.0 * batch as f64 * 2048.0 / 1_048_576.0
|
|
);
|
|
|
|
let start = Instant::now();
|
|
let mut ok = 0u32;
|
|
let mut fail = 0u32;
|
|
let mut bytes: u64 = 0;
|
|
|
|
// Recovery flag: true matches pre-0.11.13 bench behavior — full SCSI
|
|
// ECC retry loop on errors (slower, what the rip path used before the
|
|
// adaptive batch sizer landed). Flip to `false` for the fast-fail path
|
|
// that current rips use; benches are configurable via this constant.
|
|
const READ_WITH_RECOVERY: bool = true;
|
|
|
|
for i in 0..1000u32 {
|
|
let lba = i * batch as u32;
|
|
match drive.read(lba, batch, &mut buf, READ_WITH_RECOVERY) {
|
|
Ok(_) => {
|
|
writer.write_all(&buf).unwrap();
|
|
ok += 1;
|
|
}
|
|
Err(e) => {
|
|
fail += 1;
|
|
if fail <= 5 {
|
|
eprintln!(" FAIL LBA {}: {}", lba, e);
|
|
}
|
|
buf.fill(0);
|
|
writer.write_all(&buf).unwrap();
|
|
}
|
|
}
|
|
bytes += buf.len() as u64;
|
|
|
|
if i % 50 == 0 && i > 0 {
|
|
let elapsed = start.elapsed().as_secs_f64();
|
|
let mb = bytes as f64 / 1_048_576.0;
|
|
eprint!("\r {:.1} MB | {:.1} MB/s ", mb, mb / elapsed);
|
|
}
|
|
}
|
|
|
|
let elapsed = start.elapsed().as_secs_f64();
|
|
let mb = ok as f64 * batch as f64 * 2048.0 / 1_048_576.0;
|
|
eprintln!(
|
|
"\n{} ok, {} fail, {:.1} MB in {:.1}s = {:.1} MB/s",
|
|
ok,
|
|
fail,
|
|
mb,
|
|
elapsed,
|
|
mb / elapsed
|
|
);
|
|
}
|