Skip to main content

qubit_io/ext/
write_seek_ext.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
15use crate::WriteSeek;
16
17/// Extension methods for values that implement both [`Write`] and [`Seek`].
18///
19/// `WriteSeekExt` provides position-preserving write helpers for random-access
20/// output use cases such as patching headers, offsets, and indexes after a
21/// stream has already been written.
22pub trait WriteSeekExt: Write + Seek {
23    /// Writes all bytes at `offset` and restores the original position.
24    ///
25    /// This method seeks to `offset`, delegates to [`Write::write_all`], and
26    /// then restores the position that was current before the call.
27    ///
28    /// # Parameters
29    /// - `offset`: Absolute byte offset from the start of the stream.
30    /// - `buffer`: Bytes to write.
31    ///
32    /// # Errors
33    /// Returns an error when reading the current position, seeking to `offset`,
34    /// writing bytes, or restoring the original position fails. If restoration
35    /// fails, the restoration error is returned.
36    fn write_all_at_preserving_position(
37        &mut self,
38        offset: u64,
39        buffer: &[u8],
40    ) -> Result<()>;
41}
42
43impl<T> WriteSeekExt for T
44where
45    T: Write + Seek + ?Sized,
46{
47    #[inline]
48    fn write_all_at_preserving_position(
49        &mut self,
50        offset: u64,
51        buffer: &[u8],
52    ) -> Result<()> {
53        let mut writer = self;
54        write_all_at_preserving_position_impl(&mut writer, offset, buffer)
55    }
56}
57
58/// Writes all bytes at `offset` and restores the original stream position.
59///
60/// # Parameters
61/// - `writer`: Seekable writer to update.
62/// - `offset`: Absolute byte offset from the start of the stream.
63/// - `buffer`: Bytes to write.
64///
65/// # Errors
66/// Returns an error when position lookup, seeking, writing, or restoration
67/// fails.
68fn write_all_at_preserving_position_impl(
69    writer: &mut dyn WriteSeek,
70    offset: u64,
71    buffer: &[u8],
72) -> Result<()> {
73    let position = writer.stream_position()?;
74    let write_result = match writer.seek(SeekFrom::Start(offset)) {
75        Ok(_) => writer.write_all(buffer),
76        Err(error) => Err(error),
77    };
78    let restore_result = writer.seek(SeekFrom::Start(position));
79    match (write_result, restore_result) {
80        (Ok(()), Ok(_)) => Ok(()),
81        (Err(error), Ok(_)) => Err(error),
82        (_, Err(error)) => Err(error),
83    }
84}