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