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}