Skip to main content

zencodec_io/
destination.rs

1//! Actual forward-only and seek-back destinations for synchronous output.
2//! A wrapper binds access and the output origin to the retained writer.
3use std::io::{self, Seek, SeekFrom, Write};
4
5mod sealed {
6    pub trait Sealed {}
7}
8/// Writer access derived by wrapping an actual writer in [`Forward`] or [`SeekBack`].
9/// Sealed so callers cannot invent seek support independently of the writer.
10pub trait Destination: Write + sealed::Sealed {
11    /// Whether this destination can overwrite relative output positions.
12    fn can_patch(&self) -> bool;
13    /// Seek relative to the output origin; fails for a forward-only writer.
14    fn position(&mut self, relative: u64) -> io::Result<()>;
15}
16
17/// Forward-only output, including sockets and other `Write` implementations.
18pub struct Forward<W>(pub W);
19impl<W> sealed::Sealed for Forward<W> {}
20impl<W: Write> Write for Forward<W> {
21    fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
22        self.0.write(bytes)
23    }
24    fn flush(&mut self) -> io::Result<()> {
25        self.0.flush()
26    }
27}
28impl<W: Write> Destination for Forward<W> {
29    fn can_patch(&self) -> bool {
30        false
31    }
32    fn position(&mut self, _: u64) -> io::Result<()> {
33        Err(io::ErrorKind::Unsupported.into())
34    }
35}
36
37/// Seekable output whose offsets are relative to the writer's initial position.
38/// Existing prefixes remain outside all output patches.
39pub struct SeekBack<W> {
40    writer: W,
41    start: u64,
42}
43impl<W> sealed::Sealed for SeekBack<W> {}
44impl<W: Write + Seek> SeekBack<W> {
45    /// Capture the current position as the output origin.
46    pub fn new(mut writer: W) -> io::Result<Self> {
47        let start = writer.stream_position()?;
48        Ok(Self { writer, start })
49    }
50}
51impl<W: Write> Write for SeekBack<W> {
52    fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
53        self.writer.write(bytes)
54    }
55    fn flush(&mut self) -> io::Result<()> {
56        self.writer.flush()
57    }
58}
59impl<W: Write + Seek> Destination for SeekBack<W> {
60    fn can_patch(&self) -> bool {
61        true
62    }
63    fn position(&mut self, relative: u64) -> io::Result<()> {
64        let absolute = self
65            .start
66            .checked_add(relative)
67            .ok_or(io::ErrorKind::InvalidInput)?;
68        let actual = self.writer.seek(SeekFrom::Start(absolute))?;
69        if actual != absolute {
70            return Err(io::Error::from(io::ErrorKind::InvalidData));
71        }
72        Ok(())
73    }
74}