Skip to main content

qubit_io/ext/
read_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    Read,
10    Result,
11    Seek,
12    SeekFrom,
13};
14
15use crate::ext::internal::read_ext_impl;
16
17/// Extension methods for values that implement both [`Read`] and [`Seek`].
18///
19/// `ReadSeekExt` provides position-preserving read helpers for common
20/// inspection use cases such as file signature checks, MIME detection, and
21/// random-offset probing.
22pub trait ReadSeekExt: Read + Seek {
23    /// Reads from the current position and restores the original position.
24    ///
25    /// This method has the same partial-EOF semantics as
26    /// [`crate::ReadExt::read_exact_or_eof`], but it leaves the stream
27    /// positioned where it was before the call when restoration succeeds.
28    ///
29    /// # Parameters
30    /// - `buffer`: Destination buffer to fill.
31    ///
32    /// # Returns
33    /// The number of bytes written into `buffer`.
34    ///
35    /// # Errors
36    /// Returns an error when reading the current position, reading bytes, or
37    /// restoring the original position fails. If both reading and restoration
38    /// fail, the restoration error is returned because the caller's stream
39    /// position contract was not preserved.
40    fn peek_exact_or_eof(&mut self, buffer: &mut [u8]) -> Result<usize>;
41
42    /// Reads from `offset` and restores the original position.
43    ///
44    /// This method seeks to `offset`, reads until `buffer` is full or EOF is
45    /// reached, and then restores the position that was current before the
46    /// call.
47    ///
48    /// # Parameters
49    /// - `offset`: Absolute byte offset from the start of the stream.
50    /// - `buffer`: Destination buffer to fill.
51    ///
52    /// # Returns
53    /// The number of bytes written into `buffer`.
54    ///
55    /// # Errors
56    /// Returns an error when reading the current position, seeking to `offset`,
57    /// reading bytes, or restoring the original position fails. If restoration
58    /// fails, the restoration error is returned.
59    fn read_exact_or_eof_at(
60        &mut self,
61        offset: u64,
62        buffer: &mut [u8],
63    ) -> Result<usize>;
64}
65
66impl<T> ReadSeekExt for T
67where
68    T: Read + Seek + ?Sized,
69{
70    #[inline]
71    fn peek_exact_or_eof(&mut self, buffer: &mut [u8]) -> Result<usize> {
72        let mut reader = self;
73        let position = reader.stream_position()?;
74        let read_result = read_ext_impl::read_exact_or_eof(&mut reader, buffer);
75        let restore_result = reader.seek(SeekFrom::Start(position));
76        match (read_result, restore_result) {
77            (Ok(count), Ok(_)) => Ok(count),
78            (Err(error), Ok(_)) => Err(error),
79            (_, Err(error)) => Err(error),
80        }
81    }
82
83    #[inline]
84    fn read_exact_or_eof_at(
85        &mut self,
86        offset: u64,
87        buffer: &mut [u8],
88    ) -> Result<usize> {
89        let mut reader = self;
90        let position = reader.stream_position()?;
91        let read_result = match reader.seek(SeekFrom::Start(offset)) {
92            Ok(_) => read_ext_impl::read_exact_or_eof(&mut reader, buffer),
93            Err(error) => Err(error),
94        };
95        let restore_result = reader.seek(SeekFrom::Start(position));
96        match (read_result, restore_result) {
97            (Ok(count), Ok(_)) => Ok(count),
98            (Err(error), Ok(_)) => Err(error),
99            (_, Err(error)) => Err(error),
100        }
101    }
102}