Skip to main content

qubit_io/traits/
async_output.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::Context;
12use std::task::Poll;
13
14use crate::FlushFuture;
15use crate::WriteFullyFuture;
16use crate::WriteFuture;
17use crate::traits::normalize_async_error;
18use crate::traits::validate_write_count;
19
20/// Minimal runtime-independent asynchronous output interface over items.
21///
22/// `AsyncOutput` expresses readiness through [`Poll`] and does not depend on a
23/// particular executor. File publication operations such as commit and abort
24/// intentionally do not belong to this byte-transfer abstraction.
25pub trait AsyncOutput {
26    /// The item type written to this output.
27    type Item;
28
29    /// Returns whether this output already buffers items internally.
30    ///
31    /// # Returns
32    ///
33    /// `true` when callers should avoid automatically adding another generic
34    /// item buffer.
35    #[inline(always)]
36    #[must_use]
37    fn is_buffered(&self) -> bool {
38        false
39    }
40
41    /// Polls an indexed write without checking the source range.
42    ///
43    /// # Parameters
44    ///
45    /// * `cx` - Task context used to register interest when output is pending.
46    /// * `input` - Source storage.
47    /// * `index` - Start index inside `input`.
48    /// * `count` - Maximum number of items to write.
49    ///
50    /// # Returns
51    ///
52    /// [`Poll::Pending`] when no result is currently available, or a ready I/O
53    /// result containing a count in `0..=count`. A zero `count` must
54    /// immediately return `Poll::Ready(Ok(0))`.
55    ///
56    /// Before returning [`Poll::Pending`], the implementation must arrange for
57    /// `cx`'s waker to be notified when progress may be possible. Neither
58    /// `Poll::Pending` nor `Poll::Ready(Err(_))` may accept items.
59    /// `WouldBlock` and `Interrupted` must not cross this asynchronous
60    /// boundary; implementations must respectively register readiness or retry
61    /// internally.
62    ///
63    /// # Errors
64    ///
65    /// Returns the output error reported by the implementation.
66    ///
67    /// # Safety
68    ///
69    /// The caller must guarantee that `index..index + count` is a valid range
70    /// inside `input` and that the addition does not overflow.
71    unsafe fn poll_write_unchecked(
72        self: Pin<&mut Self>,
73        cx: &mut Context<'_>,
74        input: &[Self::Item],
75        index: usize,
76        count: usize,
77    ) -> Poll<Result<usize>>;
78
79    /// Polls one write from the full source slice.
80    ///
81    /// # Parameters
82    ///
83    /// * `cx` - Task context used to register interest when output is pending.
84    /// * `input` - Source storage.
85    ///
86    /// # Returns
87    ///
88    /// [`Poll::Pending`] or a ready result containing the accepted item count.
89    ///
90    /// # Errors
91    ///
92    /// Returns the implementation's output error. Returns
93    /// [`std::io::ErrorKind::InvalidData`] if the implementation reports more
94    /// items than requested.
95    #[inline]
96    fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, input: &[Self::Item]) -> Poll<Result<usize>> {
97        if input.is_empty() {
98            return Poll::Ready(Ok(0));
99        }
100        let requested = input.len();
101        // SAFETY: The full input slice is a valid source range.
102        match unsafe { self.poll_write_unchecked(cx, input, 0, requested) } {
103            Poll::Ready(Ok(written)) => Poll::Ready(validate_write_count(written, requested).map(|()| written)),
104            Poll::Ready(Err(error)) => Poll::Ready(Err(normalize_async_error(error))),
105            Poll::Pending => Poll::Pending,
106        }
107    }
108
109    /// Polls the flushing of internally buffered items.
110    ///
111    /// # Parameters
112    ///
113    /// * `cx` - Task context used to register interest when flushing is
114    ///   pending.
115    ///
116    /// # Returns
117    ///
118    /// [`Poll::Pending`] or the ready flush result.
119    ///
120    /// Before returning [`Poll::Pending`], the implementation must arrange for
121    /// `cx`'s waker to be notified when flushing may progress. `WouldBlock` and
122    /// `Interrupted` must not cross this asynchronous boundary.
123    ///
124    /// # Errors
125    ///
126    /// Returns the flush error reported by the implementation.
127    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>>;
128
129    /// Creates a future that performs one asynchronous write operation.
130    ///
131    /// # Type Parameters
132    ///
133    /// * `'a` - Shared lifetime of the output borrow and source slice.
134    ///
135    /// # Parameters
136    ///
137    /// * `input` - Source storage.
138    ///
139    /// # Returns
140    ///
141    /// A future that resolves with the number of accepted items.
142    #[inline(always)]
143    fn write_async<'a>(&'a mut self, input: &'a [Self::Item]) -> WriteFuture<'a, Self>
144    where
145        Self: Sized + Unpin,
146    {
147        WriteFuture::new(Pin::new(self), input)
148    }
149
150    /// Creates a future that writes the entire source slice.
151    ///
152    /// The returned future reports [`std::io::ErrorKind::WriteZero`] when
153    /// output makes no progress.
154    ///
155    /// # Type Parameters
156    ///
157    /// * `'a` - Shared lifetime of the output borrow and source slice.
158    ///
159    /// # Parameters
160    ///
161    /// * `input` - Source storage.
162    ///
163    /// # Returns
164    ///
165    /// A future that resolves when every item has been accepted.
166    #[inline(always)]
167    fn write_fully_async<'a>(&'a mut self, input: &'a [Self::Item]) -> WriteFullyFuture<'a, Self>
168    where
169        Self: Sized + Unpin,
170    {
171        WriteFullyFuture::new(Pin::new(self), input)
172    }
173
174    /// Creates a future that flushes internally buffered items.
175    ///
176    /// # Returns
177    ///
178    /// A future that resolves with the flush result.
179    #[inline(always)]
180    fn flush_async(&mut self) -> FlushFuture<'_, Self>
181    where
182        Self: Sized + Unpin,
183    {
184        FlushFuture::new(Pin::new(self))
185    }
186}