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}