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