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}