qubit_io/wrappers/tee_reader.rs
1// =============================================================================
2// Copyright (c) 2026 Haixing Hu.
3//
4// SPDX-License-Identifier: Apache-2.0
5//
6// Licensed under the Apache License, Version 2.0.
7// =============================================================================
8use std::io::{
9 Read,
10 Result,
11 Seek,
12 SeekFrom,
13 Write,
14};
15
16use super::SyncSeekTeeReader;
17
18/// Reader wrapper that mirrors read bytes into a branch writer.
19///
20/// `TeeReader` forwards reads to the source reader and writes every
21/// successfully read byte into the branch writer while the stream is consumed.
22/// If the branch writer fails, the source bytes have already been read from the
23/// inner reader and the branch error is returned.
24///
25/// Seeking a `TeeReader` seeks only the source reader. It does not seek or
26/// otherwise modify the branch writer; bytes mirrored after the seek are simply
27/// appended or written according to the branch writer's own state.
28///
29/// `TeeReader` intentionally does not implement [`std::io::BufRead`]. Mirroring
30/// from `fill_buf` would copy bytes before the caller commits to consuming
31/// them, while mirroring from `consume` could not report branch write failures
32/// because `BufRead::consume` has no error return.
33///
34/// If buffered access is needed, wrap this reader outside the tee layer, for
35/// example `BufReader<TeeReader<R, W>>`. In that composition bytes are mirrored
36/// when the outer `BufReader` refills its internal buffer, which may be earlier
37/// than the application later consuming those bytes from `fill_buf`.
38///
39/// # Failure and retry semantics
40///
41/// A branch error does not restore bytes already consumed from the source.
42/// Callers should therefore treat a branch write error as terminal unless they
43/// can reposition or otherwise recover the source. An outer buffering layer
44/// may discard a failed refill even though the source has already advanced:
45///
46/// ```text
47/// source before refill: "abcdef"
48/// refill reads "abc": source advances to "def", branch returns an error
49/// retry: reading resumes at "def"; "abc" may be unavailable
50/// ```
51///
52/// After a branch error, the source and branch may be out of sync. This wrapper
53/// does not retain the consumed bytes or provide rollback.
54///
55/// # Examples
56/// ```
57/// use std::io::{
58/// Cursor,
59/// Read,
60/// };
61///
62/// use qubit_io::TeeReader;
63///
64/// let source = Cursor::new(b"abc".to_vec());
65/// let branch = Vec::new();
66/// let mut reader = TeeReader::new(source, branch);
67///
68/// let mut data = Vec::new();
69/// reader.read_to_end(&mut data)?;
70/// let (_source, branch) = reader.into_inner();
71///
72/// assert_eq!(b"abc", data.as_slice());
73/// assert_eq!(b"abc", branch.as_slice());
74/// # Ok::<(), std::io::Error>(())
75/// ```
76pub struct TeeReader<R, W> {
77 inner: R,
78 branch: W,
79}
80
81impl<R, W> TeeReader<R, W> {
82 /// Creates a tee reader.
83 ///
84 /// # Parameters
85 /// - `reader`: Source reader.
86 /// - `branch`: Writer that receives the bytes successfully read.
87 ///
88 /// # Returns
89 /// A new tee reader.
90 #[inline]
91 pub fn new(reader: R, branch: W) -> Self {
92 Self {
93 inner: reader,
94 branch,
95 }
96 }
97
98 /// Creates a tee reader that synchronizes branch seeks with source seeks.
99 ///
100 /// The returned wrapper mirrors read bytes like [`TeeReader`], but its
101 /// [`Seek`] implementation also seeks the branch writer to the source
102 /// reader's resulting absolute position. If the branch seek fails, the
103 /// source reader may already have moved.
104 ///
105 /// # Parameters
106 /// - `reader`: Source reader.
107 /// - `branch`: Writer that receives bytes successfully read and is sought
108 /// to the same absolute position as the source reader.
109 ///
110 /// # Returns
111 /// A sync-seek tee reader.
112 #[inline]
113 pub fn with_sync_branch_seek(
114 reader: R,
115 branch: W,
116 ) -> SyncSeekTeeReader<R, W> {
117 SyncSeekTeeReader::new(reader, branch)
118 }
119
120 /// Returns an immutable reference to the source reader.
121 ///
122 /// # Returns
123 /// The source reader reference.
124 #[inline]
125 pub fn inner(&self) -> &R {
126 &self.inner
127 }
128
129 /// Returns a mutable reference to the source reader.
130 ///
131 /// # Returns
132 /// The source reader reference.
133 #[inline]
134 pub fn inner_mut(&mut self) -> &mut R {
135 &mut self.inner
136 }
137
138 /// Returns an immutable reference to the branch writer.
139 ///
140 /// # Returns
141 /// The branch writer reference.
142 #[inline]
143 pub fn branch(&self) -> &W {
144 &self.branch
145 }
146
147 /// Returns a mutable reference to the branch writer.
148 ///
149 /// # Returns
150 /// The branch writer reference.
151 #[inline]
152 pub fn branch_mut(&mut self) -> &mut W {
153 &mut self.branch
154 }
155
156 /// Consumes this wrapper and returns the source reader and branch writer.
157 ///
158 /// # Returns
159 /// A tuple containing the source reader and branch writer.
160 #[inline]
161 pub fn into_inner(self) -> (R, W) {
162 (self.inner, self.branch)
163 }
164}
165
166impl<R, W> Read for TeeReader<R, W>
167where
168 R: Read,
169 W: Write,
170{
171 fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
172 let count = self.inner.read(buffer)?;
173 self.branch.write_all(&buffer[..count])?;
174 Ok(count)
175 }
176}
177
178impl<R, W> Seek for TeeReader<R, W>
179where
180 R: Seek,
181{
182 /// Seeks the source reader without touching the branch writer.
183 ///
184 /// # Parameters
185 /// - `position`: Target position for the source reader.
186 ///
187 /// # Returns
188 /// The new source reader position.
189 ///
190 /// # Errors
191 /// Returns the seek error reported by the source reader.
192 #[inline]
193 fn seek(&mut self, position: SeekFrom) -> Result<u64> {
194 self.inner.seek(position)
195 }
196}