Skip to main content

qubit_io/std_io/ext/
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//! Extension operations for inspecting standard seekable streams.
10
11use std::io::Result;
12use std::io::Seek;
13use std::io::SeekFrom;
14
15/// Extension methods for [`Seek`] values.
16///
17/// `SeekExt` provides stable, position-preserving helpers for seekable streams.
18/// The methods are implemented for every type that implements [`Seek`],
19/// including `dyn Seek` trait objects.
20///
21/// # Examples
22///
23/// ```
24/// use std::io::Cursor;
25/// use std::io::Seek;
26///
27/// use qubit_io::std_io::ext::SeekExt;
28///
29/// let mut stream = Cursor::new(b"hello".to_vec());
30/// stream.set_position(2);
31/// assert_eq!(stream.stream_size()?, 5);
32/// assert_eq!(stream.stream_position()?, 2);
33/// # Ok::<(), std::io::Error>(())
34/// ```
35pub trait SeekExt: Seek {
36    /// Gets the stream size without changing the final stream position.
37    ///
38    /// The original position is captured with [`Seek::stream_position`], then
39    /// the stream is moved to the end to measure its size, and finally the
40    /// original position is restored.
41    ///
42    /// # Compatibility
43    /// If the standard library adds an equivalent `Seek::stream_size` method,
44    /// this extension method may be deprecated in a future release.
45    ///
46    /// # Returns
47    /// The stream size in bytes.
48    ///
49    /// # Errors
50    /// Returns an error when reading the current position, seeking to the end,
51    /// or restoring the original position fails. If restoring fails, the
52    /// restore error is returned because the caller's stream position contract
53    /// was not preserved.
54    fn stream_size(&mut self) -> Result<u64>;
55}
56
57/// Implements stream-size inspection through a type-erased stream.
58///
59/// # Parameters
60/// - `stream`: Stream to measure and restore.
61///
62/// # Returns
63/// The stream size in bytes.
64///
65/// # Errors
66/// Returns an error when querying, changing, or restoring the stream position
67/// fails.
68fn stream_size_impl(stream: &mut dyn Seek) -> Result<u64> {
69    let position = stream.stream_position()?;
70    let size_result = stream.seek(SeekFrom::End(0));
71    let restore_result = stream.seek(SeekFrom::Start(position));
72    match (size_result, restore_result) {
73        (Ok(size), Ok(_)) => Ok(size),
74        (Err(error), Ok(_)) => Err(error),
75        (_, Err(error)) => Err(error),
76    }
77}
78
79impl<T> SeekExt for T
80where
81    T: Seek + ?Sized,
82{
83    /// Gets the stream size without changing its final position.
84    ///
85    /// # Returns
86    /// The stream size in bytes.
87    ///
88    /// # Errors
89    /// Returns an error when querying, changing, or restoring the stream
90    /// position fails.
91    #[inline(always)]
92    fn stream_size(&mut self) -> Result<u64> {
93        let mut stream = self;
94        stream_size_impl(&mut stream)
95    }
96}