Skip to main content

qubit_io/traits/
async_input.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::Result;
10use std::pin::Pin;
11use std::task::{
12    Context,
13    Poll,
14};
15
16use crate::traits::{
17    normalize_async_error,
18    validate_read_count,
19};
20use crate::{
21    ReadExactFuture,
22    ReadFullyFuture,
23    ReadFuture,
24};
25
26/// Minimal runtime-independent asynchronous input interface over items.
27///
28/// `AsyncInput` expresses readiness through [`Poll`] and does not depend on a
29/// particular executor. Implementations may be pinned and therefore are never
30/// moved by the polling methods.
31pub trait AsyncInput {
32    /// The item type read from this input.
33    type Item;
34
35    /// Returns whether this input already buffers items internally.
36    ///
37    /// # Returns
38    ///
39    /// `true` when callers should avoid automatically adding another generic
40    /// item buffer.
41    #[inline(always)]
42    #[must_use]
43    fn is_buffered(&self) -> bool {
44        false
45    }
46
47    /// Polls an indexed read without checking the destination range.
48    ///
49    /// # Parameters
50    ///
51    /// * `cx` - Task context used to register interest when input is pending.
52    /// * `output` - Destination storage.
53    /// * `index` - Start index inside `output`.
54    /// * `count` - Maximum number of items to read.
55    ///
56    /// # Returns
57    ///
58    /// [`Poll::Pending`] when no result is currently available, or a ready I/O
59    /// result containing a count in `0..=count`. A ready zero count denotes
60    /// end of input when `count` is nonzero. A zero `count` must immediately
61    /// return `Poll::Ready(Ok(0))`.
62    ///
63    /// Before returning [`Poll::Pending`], the implementation must arrange for
64    /// `cx`'s waker to be notified when progress may be possible. Neither
65    /// `Poll::Pending` nor `Poll::Ready(Err(_))` may transfer items.
66    /// `WouldBlock` and `Interrupted` must not cross this asynchronous
67    /// boundary; implementations must respectively register readiness or retry
68    /// internally.
69    ///
70    /// # Errors
71    ///
72    /// Returns the input error reported by the implementation.
73    ///
74    /// # Safety
75    ///
76    /// The caller must guarantee that `index..index + count` is a valid range
77    /// inside `output` and that the addition does not overflow.
78    unsafe fn poll_read_unchecked(
79        self: Pin<&mut Self>,
80        cx: &mut Context<'_>,
81        output: &mut [Self::Item],
82        index: usize,
83        count: usize,
84    ) -> Poll<Result<usize>>;
85
86    /// Polls a read into the full destination slice.
87    ///
88    /// # Parameters
89    ///
90    /// * `cx` - Task context used to register interest when input is pending.
91    /// * `output` - Destination storage.
92    ///
93    /// # Returns
94    ///
95    /// [`Poll::Pending`] or a ready result containing the number of items read.
96    ///
97    /// # Errors
98    ///
99    /// Returns the implementation's input error. Returns
100    /// [`std::io::ErrorKind::InvalidData`] if the implementation reports more
101    /// items than requested.
102    #[inline]
103    fn poll_read(
104        self: Pin<&mut Self>,
105        cx: &mut Context<'_>,
106        output: &mut [Self::Item],
107    ) -> Poll<Result<usize>> {
108        if output.is_empty() {
109            return Poll::Ready(Ok(0));
110        }
111        let requested = output.len();
112        // SAFETY: The full output slice is a valid destination range.
113        match unsafe { self.poll_read_unchecked(cx, output, 0, requested) } {
114            Poll::Ready(Ok(read)) => {
115                Poll::Ready(validate_read_count(read, requested).map(|()| read))
116            }
117            Poll::Ready(Err(error)) => {
118                Poll::Ready(Err(normalize_async_error(error)))
119            }
120            Poll::Pending => Poll::Pending,
121        }
122    }
123
124    /// Creates a future that performs one asynchronous read operation.
125    ///
126    /// # Type Parameters
127    ///
128    /// * `'a` - Shared lifetime of the input borrow and destination slice.
129    ///
130    /// # Parameters
131    ///
132    /// * `output` - Destination storage.
133    ///
134    /// # Returns
135    ///
136    /// A future that resolves with the number of items read.
137    #[inline(always)]
138    fn read_async<'a>(
139        &'a mut self,
140        output: &'a mut [Self::Item],
141    ) -> ReadFuture<'a, Self>
142    where
143        Self: Sized + Unpin,
144    {
145        ReadFuture::new(Pin::new(self), output)
146    }
147
148    /// Creates a future that fills a destination as far as possible.
149    ///
150    /// The returned future stops when the destination is full or the input
151    /// reports EOF.
152    ///
153    /// # Type Parameters
154    ///
155    /// * `'a` - Shared lifetime of the input borrow and destination slice.
156    ///
157    /// # Parameters
158    ///
159    /// * `output` - Destination storage.
160    ///
161    /// # Returns
162    ///
163    /// A future that resolves with the total number of items read.
164    #[inline(always)]
165    fn read_fully_async<'a>(
166        &'a mut self,
167        output: &'a mut [Self::Item],
168    ) -> ReadFullyFuture<'a, Self>
169    where
170        Self: Sized + Unpin,
171    {
172        ReadFullyFuture::new(Pin::new(self), output)
173    }
174
175    /// Creates a future that fills the entire destination.
176    ///
177    /// The returned future reports [`std::io::ErrorKind::UnexpectedEof`] if
178    /// the input ends before the destination is full.
179    ///
180    /// # Type Parameters
181    ///
182    /// * `'a` - Shared lifetime of the input borrow and destination slice.
183    ///
184    /// # Parameters
185    ///
186    /// * `output` - Destination storage that must be filled completely.
187    ///
188    /// # Returns
189    ///
190    /// A future that resolves after filling `output` or encountering an error.
191    #[inline(always)]
192    fn read_exactly_async<'a>(
193        &'a mut self,
194        output: &'a mut [Self::Item],
195    ) -> ReadExactFuture<'a, Self>
196    where
197        Self: Sized + Unpin,
198    {
199        ReadExactFuture::new(Pin::new(self), output)
200    }
201}