qubit_io/wrappers/tee_writer.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 Result,
10 Seek,
11 SeekFrom,
12 Write,
13};
14
15/// Writer wrapper that mirrors accepted bytes into a branch writer.
16///
17/// `TeeWriter` writes to the primary writer first, then writes exactly the
18/// accepted bytes into the branch writer with [`Write::write_all`]. If the
19/// branch writer fails, the primary writer may already have accepted bytes and
20/// the branch error is returned.
21///
22/// Seeking moves the primary writer first, then seeks the branch writer to the
23/// primary writer's resulting absolute position. If the branch seek fails, the
24/// primary writer may already have moved.
25///
26/// # Examples
27/// ```
28/// use std::io::Write;
29///
30/// use qubit_io::TeeWriter;
31///
32/// let primary = Vec::new();
33/// let branch = Vec::new();
34/// let mut writer = TeeWriter::new(primary, branch);
35///
36/// writer.write_all(b"abc")?;
37/// let (primary, branch) = writer.into_inner();
38///
39/// assert_eq!(b"abc", primary.as_slice());
40/// assert_eq!(b"abc", branch.as_slice());
41/// # Ok::<(), std::io::Error>(())
42/// ```
43pub struct TeeWriter<P, B> {
44 primary: P,
45 branch: B,
46}
47
48impl<P, B> TeeWriter<P, B> {
49 /// Creates a tee writer.
50 ///
51 /// # Parameters
52 /// - `primary`: Primary destination writer.
53 /// - `branch`: Secondary writer that mirrors accepted bytes.
54 ///
55 /// # Returns
56 /// A new tee writer.
57 #[inline]
58 pub fn new(primary: P, branch: B) -> Self {
59 Self { primary, branch }
60 }
61
62 /// Returns an immutable reference to the primary writer.
63 ///
64 /// # Returns
65 /// The primary writer reference.
66 #[inline]
67 pub fn primary_ref(&self) -> &P {
68 &self.primary
69 }
70
71 /// Returns a mutable reference to the primary writer.
72 ///
73 /// # Returns
74 /// The primary writer reference.
75 #[inline]
76 pub fn primary_mut(&mut self) -> &mut P {
77 &mut self.primary
78 }
79
80 /// Returns an immutable reference to the branch writer.
81 ///
82 /// # Returns
83 /// The branch writer reference.
84 #[inline]
85 pub fn branch_ref(&self) -> &B {
86 &self.branch
87 }
88
89 /// Returns a mutable reference to the branch writer.
90 ///
91 /// # Returns
92 /// The branch writer reference.
93 #[inline]
94 pub fn branch_mut(&mut self) -> &mut B {
95 &mut self.branch
96 }
97
98 /// Consumes this wrapper and returns both wrapped writers.
99 ///
100 /// # Returns
101 /// A tuple containing the primary writer and branch writer.
102 #[inline]
103 pub fn into_inner(self) -> (P, B) {
104 (self.primary, self.branch)
105 }
106}
107
108impl<P, B> Write for TeeWriter<P, B>
109where
110 P: Write,
111 B: Write,
112{
113 fn write(&mut self, buffer: &[u8]) -> Result<usize> {
114 let count = self.primary.write(buffer)?;
115 self.branch.write_all(&buffer[..count])?;
116 Ok(count)
117 }
118
119 fn flush(&mut self) -> Result<()> {
120 self.primary.flush()?;
121 self.branch.flush()
122 }
123}
124
125impl<P, B> Seek for TeeWriter<P, B>
126where
127 P: Seek,
128 B: Seek,
129{
130 /// Seeks both wrapped writers to the same resulting absolute position.
131 ///
132 /// # Parameters
133 /// - `position`: Target position for the primary writer.
134 ///
135 /// # Returns
136 /// The new primary writer position.
137 ///
138 /// # Errors
139 /// Returns the primary seek error, or the branch seek error after the
140 /// primary writer has already moved.
141 fn seek(&mut self, position: SeekFrom) -> Result<u64> {
142 let primary_position = self.primary.seek(position)?;
143 self.branch.seek(SeekFrom::Start(primary_position))?;
144 Ok(primary_position)
145 }
146}