libfreemkv 0.8.1

Open source raw disc access library for optical drives
Documentation
//! StdioStream — raw byte pipe via stdin/stdout. Format-agnostic.

use super::IOStream;
use crate::disc::DiscTitle;
use std::io::{self, Read, 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.
pub struct StdioStream {
    disc_title: DiscTitle,
    reader: Option<io::Stdin>,
    writer: Option<io::Stdout>,
}

impl StdioStream {
    /// Create a stdio stream for reading (stdin).
    pub fn input() -> Self {
        Self {
            disc_title: DiscTitle::empty(),
            reader: Some(io::stdin()),
            writer: None,
        }
    }

    /// Create a stdio stream for writing (stdout).
    pub fn output() -> Self {
        Self {
            disc_title: DiscTitle::empty(),
            reader: None,
            writer: Some(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 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);
    }
}