Skip to main content

qubit_io/traits/
seekable.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
9use std::io::{
10    Result,
11    Seek,
12    SeekFrom,
13};
14
15/// Minimal seek interface measured in stream items.
16///
17/// Unlike [`Seek`], which measures positions and offsets in bytes,
18/// `Seekable` measures them in units of [`Self::Unit`]. For byte streams,
19/// set `Unit = u8`; offsets passed through [`SeekFrom`] then count units
20/// rather than bytes.
21///
22/// The return value of [`Seekable::seek_to`] is the new absolute position from
23/// the start of the stream, in items.
24///
25/// # Method name overlap
26///
27/// `Seekable::seek_to` has the same method name as [`Seek::seek`]. In generic
28/// code where both traits are in scope for the same value, use fully qualified
29/// syntax to choose item-oriented seeking or byte-oriented seeking explicitly:
30///
31/// ```
32/// use std::io::{
33///     Result,
34///     Seek,
35///     SeekFrom,
36/// };
37///
38/// use qubit_io::Seekable;
39///
40/// fn seek_items<T>(stream: &mut T, position: SeekFrom) -> Result<u64>
41/// where
42///     T: Seekable + Seek,
43/// {
44///     <T as Seekable>::seek_to(stream, position)
45/// }
46///
47/// fn seek_bytes<T>(stream: &mut T, position: SeekFrom) -> Result<u64>
48/// where
49///     T: Seekable + Seek,
50/// {
51///     Seek::seek(stream, position)
52/// }
53/// ```
54///
55/// # Coherence note
56///
57/// The blanket impl below maps [`std::io::Seek`] to `Unit = u8` for binary
58/// compatibility. If a concrete type already implements `Seek`, it already has
59/// an implicit `Seekable<Unit = u8>` impl from this blanket, so another
60/// `Seekable` impl with the same `(Self, Unit)` pair would be a coherence
61/// conflict.
62///
63/// For example, this is rejected by the compiler:
64///
65/// ```rust,compile_fail
66/// use std::io::{Result, Seek, SeekFrom};
67///
68/// struct LegacyStream;
69///
70/// impl Seek for LegacyStream {
71///     fn seek(&mut self, _pos: SeekFrom) -> Result<u64> {
72///         Ok(0)
73///     }
74/// }
75///
76/// impl crate::Seekable for LegacyStream {
77///     type Unit = u8;
78///     fn seek_to(&mut self, _pos: SeekFrom) -> Result<u64> {
79///         Ok(0)
80///     }
81/// }
82/// ```
83///
84/// ```text
85/// error[E0119]: conflicting implementations of trait `crate::Seekable`
86/// for type `LegacyStream`
87/// ```
88///
89/// The stable workaround is to keep byte-positioned seeking on the original
90/// type and introduce a wrapper/newtype when another item interpretation is
91/// needed: implement `Seekable` for the wrapper with a different `Unit`, and
92/// keep `std::io::Seek`/byte semantics on the original type.
93pub trait Seekable {
94    /// The unit type used to measure seek positions and offsets.
95    type Unit;
96
97    /// Seeks to a position in the stream.
98    ///
99    /// # Parameters
100    ///
101    /// * `position` - Target position relative to the start, end, or current
102    ///   logical position. All offsets are counted in units of [`Self::Unit`].
103    ///
104    /// # Returns
105    ///
106    /// The new absolute stream position, in items.
107    ///
108    /// # Errors
109    ///
110    /// Returns the seek error reported by the implementation.
111    fn seek_to(&mut self, position: SeekFrom) -> Result<u64>;
112}
113
114impl<S> Seekable for S
115where
116    S: Seek + ?Sized,
117{
118    type Unit = u8;
119
120    /// Seeks a standard [`Seek`] value using byte offsets.
121    #[inline(always)]
122    fn seek_to(&mut self, position: SeekFrom) -> Result<u64> {
123        Seek::seek(self, position)
124    }
125}