v0.10.1: Streams are PES, Disc::copy() for sector dumps, zero English

Architecture:
- One stream per format, bidirectional PES (read/write on same type)
- IsoStream merged into DiscStream (one type, any SectorReader)
- Disc::copy() for disc→ISO raw sector dump
- IOStream trait deleted, all byte-level Read/Write removed
- ContentReader/OpenDisc/open_title/open_input/open_output deleted
- CountingStream wrapper for progress tracking

Error codes:
- All io::Error English strings replaced with Error enum variants
- From<Error> for io::Error conversion
- Unused variants removed, new stream/mux variants added

Deleted: mkvout.rs, pesout.rs, isowriter.rs, mkv-muxer-plan.md
Updated: all docs, README stream table, CHANGELOG

238 tests, 0 clippy warnings.
This commit is contained in:
MattJackson
2026-04-15 19:46:01 +00:00
parent e6d5fb72a1
commit ed43ced710
31 changed files with 1006 additions and 3689 deletions
+9 -92
View File
@@ -1,17 +1,13 @@
//! StdioStream — raw byte pipe via stdin/stdout. Format-agnostic.
//! StdioStream — PES frames via stdin/stdout.
use super::IOStream;
use crate::disc::DiscTitle;
use std::io::{self, Read, Write};
use std::io::{self, Write};
/// Stdio stream — reads from stdin, writes to stdout.
///
/// No headers, no metadata, no format opinions. Just bytes.
/// The format is determined by whatever is on the other end.
/// Stdio stream — reads PES from stdin, writes PES to stdout.
pub struct StdioStream {
disc_title: DiscTitle,
reader: Option<io::Stdin>,
writer: Option<io::Stdout>,
writer: Option<io::BufWriter<io::Stdout>>,
}
impl StdioStream {
@@ -25,32 +21,26 @@ impl StdioStream {
}
/// Create a stdio stream for writing (stdout).
pub fn output() -> Self {
pub fn output(title: &DiscTitle) -> Self {
Self {
disc_title: DiscTitle::empty(),
disc_title: title.clone(),
reader: None,
writer: Some(io::stdout()),
writer: Some(io::BufWriter::new(io::stdout())),
}
}
/// Set metadata (for output — passed through from input side).
pub fn meta(mut self, dt: &DiscTitle) -> Self {
self.disc_title = dt.clone();
self
}
}
impl crate::pes::Stream for StdioStream {
fn read(&mut self) -> io::Result<Option<crate::pes::PesFrame>> {
match &mut self.reader {
Some(r) => crate::pes::PesFrame::deserialize(r),
None => Err(io::Error::new(io::ErrorKind::Unsupported, "stdio opened for writing")),
None => Err(crate::error::Error::StreamWriteOnly.into()),
}
}
fn write(&mut self, frame: &crate::pes::PesFrame) -> io::Result<()> {
match &mut self.writer {
Some(w) => frame.serialize(w),
None => Err(io::Error::new(io::ErrorKind::Unsupported, "stdio opened for reading")),
None => Err(crate::error::Error::StreamReadOnly.into()),
}
}
fn finish(&mut self) -> io::Result<()> {
@@ -59,76 +49,3 @@ impl crate::pes::Stream for StdioStream {
}
fn info(&self) -> &DiscTitle { &self.disc_title }
}
impl IOStream for StdioStream {
fn info(&self) -> &DiscTitle {
&self.disc_title
}
fn finish(&mut self) -> io::Result<()> {
if let Some(ref mut w) = self.writer {
w.flush()?;
}
Ok(())
}
}
impl Read for StdioStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self.reader {
Some(ref mut r) => r.read(buf),
None => Err(io::Error::new(
io::ErrorKind::Unsupported,
"stdio:// opened for output — cannot read",
)),
}
}
}
impl Write for StdioStream {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match self.writer {
Some(ref mut w) => w.write(buf),
None => Err(io::Error::new(
io::ErrorKind::Unsupported,
"stdio:// opened for input — cannot write",
)),
}
}
fn flush(&mut self) -> io::Result<()> {
match self.writer {
Some(ref mut w) => w.flush(),
None => Ok(()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::{Read, Write};
#[test]
fn stdio_output_write_errors_on_read() {
let mut stream = StdioStream::output();
let mut buf = [0u8; 10];
let err = stream.read(&mut buf).unwrap_err();
assert_eq!(err.kind(), io::ErrorKind::Unsupported);
assert!(err.to_string().contains("cannot read"), "got: {}", err);
}
#[test]
fn stdio_input_read_errors_on_write() {
let mut stream = StdioStream::input();
let err = stream.write(&[0u8; 10]).unwrap_err();
assert_eq!(err.kind(), io::ErrorKind::Unsupported);
assert!(err.to_string().contains("cannot write"), "got: {}", err);
}
#[test]
fn stdio_total_bytes_returns_none() {
let input = StdioStream::input();
assert_eq!(input.total_bytes(), None);
let output = StdioStream::output();
assert_eq!(output.total_bytes(), None);
}
}