Skip to main content

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