Skip to main content

qubit_io/std_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// =============================================================================
8
9//! Position-preserving extension operations for seekable standard writers.
10
11use std::io::{
12    Result,
13    Seek,
14    SeekFrom,
15    Write,
16};
17
18use crate::std_io::WriteSeek;
19
20/// Extension methods for values that implement both [`Write`] and [`Seek`].
21///
22/// `WriteSeekExt` provides position-preserving write helpers for random-access
23/// output use cases such as patching headers, offsets, and indexes after a
24/// stream has already been written.
25pub trait WriteSeekExt: Write + Seek {
26    /// Writes all bytes at `offset` and restores the original position.
27    ///
28    /// This method seeks to `offset`, delegates to [`Write::write_all`], and
29    /// then restores the position that was current before the call.
30    ///
31    /// # Parameters
32    /// - `offset`: Absolute byte offset from the start of the stream.
33    /// - `buffer`: Bytes to write.
34    ///
35    /// # Returns
36    /// `Ok(())` after all bytes have been written and the position restored.
37    ///
38    /// # Errors
39    /// Returns an error when reading the current position, seeking to `offset`,
40    /// writing bytes, or restoring the original position fails. If restoration
41    /// fails, the restoration error is returned.
42    fn write_all_at_preserving_position(
43        &mut self,
44        offset: u64,
45        buffer: &[u8],
46    ) -> Result<()>;
47}
48
49/// Implements a position-preserving write through a type-erased stream.
50///
51/// # Parameters
52/// - `output`: Stream to seek, write, and restore.
53/// - `offset`: Absolute byte offset to write at.
54/// - `buffer`: Bytes to write.
55///
56/// # Returns
57/// `Ok(())` after all bytes have been written and the position restored.
58///
59/// # Errors
60/// Returns an error when querying, changing, or restoring the stream position,
61/// or when writing fails.
62fn write_all_at_preserving_position_impl(
63    output: &mut dyn WriteSeek,
64    offset: u64,
65    buffer: &[u8],
66) -> Result<()> {
67    let position = output.stream_position()?;
68    let write_result = match output.seek(SeekFrom::Start(offset)) {
69        Ok(_) => output.write_all(buffer),
70        Err(error) => Err(error),
71    };
72    let restore_result = output.seek(SeekFrom::Start(position));
73    match (write_result, restore_result) {
74        (Ok(()), Ok(_)) => Ok(()),
75        (Err(error), Ok(_)) => Err(error),
76        (_, Err(error)) => Err(error),
77    }
78}
79
80impl<T> WriteSeekExt for T
81where
82    T: Write + Seek + ?Sized,
83{
84    /// Writes at an absolute offset and restores the original position.
85    ///
86    /// # Parameters
87    /// - `offset`: Absolute byte offset to write at.
88    /// - `buffer`: Bytes to write.
89    ///
90    /// # Returns
91    /// `Ok(())` after all bytes have been written and the position restored.
92    ///
93    /// # Errors
94    /// Returns an error when querying, changing, or restoring the stream
95    /// position, or when writing fails.
96    #[inline(always)]
97    fn write_all_at_preserving_position(
98        &mut self,
99        offset: u64,
100        buffer: &[u8],
101    ) -> Result<()> {
102        let mut output = self;
103        write_all_at_preserving_position_impl(&mut output, offset, buffer)
104    }
105}