qubit_io/adapters/box_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::pin::Pin;
10use std::task::Context;
11use std::task::Poll;
12
13use crate::AsyncClose;
14use crate::AsyncOutput;
15
16/// Adapts an owned pinned boxed asynchronous output, including a trait object.
17///
18/// # Type Parameters
19///
20/// - `O`: The boxed asynchronous output type, which may be unsized and
21/// non-`Unpin`.
22#[must_use]
23#[repr(transparent)]
24pub struct BoxAsyncOutput<O>
25where
26 O: AsyncOutput + ?Sized,
27{
28 /// Pinned asynchronous output owned by this adapter.
29 inner: Pin<Box<O>>,
30}
31
32impl<O> BoxAsyncOutput<O>
33where
34 O: AsyncOutput + ?Sized,
35{
36 /// Creates an asynchronous output adapter around `inner`.
37 ///
38 /// # Parameters
39 ///
40 /// - `inner`: Boxed asynchronous output to pin and adapt.
41 ///
42 /// # Returns
43 ///
44 /// Returns an adapter that owns and pins `inner`.
45 #[inline(always)]
46 pub fn new(inner: Box<O>) -> Self {
47 Self {
48 inner: Box::into_pin(inner),
49 }
50 }
51
52 /// Returns a shared reference to the boxed asynchronous output.
53 ///
54 /// # Returns
55 ///
56 /// Returns the wrapped asynchronous output.
57 #[inline(always)]
58 #[must_use]
59 pub fn get_ref(&self) -> &O {
60 self.inner.as_ref().get_ref()
61 }
62
63 /// Projects a pinned adapter to its pinned boxed asynchronous output.
64 ///
65 /// # Returns
66 ///
67 /// Returns a pinned mutable reference to the wrapped output without moving
68 /// it.
69 #[inline(always)]
70 #[must_use]
71 pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut O> {
72 // SAFETY: Accessing the field through a pinned mutable reference does
73 // not move `inner`, whose allocation remains pinned.
74 let this = unsafe { self.get_unchecked_mut() };
75 this.inner.as_mut()
76 }
77
78 /// Consumes this adapter and returns its pinned boxed asynchronous output.
79 ///
80 /// # Returns
81 ///
82 /// Returns the pinned boxed output so a potentially non-`Unpin` value stays
83 /// pinned after extraction.
84 #[inline(always)]
85 #[must_use]
86 pub fn into_inner(self) -> Pin<Box<O>> {
87 self.inner
88 }
89}
90
91impl<O> AsyncOutput for BoxAsyncOutput<O>
92where
93 O: AsyncOutput + ?Sized,
94{
95 /// Item type written to the wrapped asynchronous output.
96 type Item = O::Item;
97
98 /// Returns the wrapped output's buffering capability.
99 ///
100 /// # Returns
101 ///
102 /// Returns `true` when the wrapped output is buffered.
103 #[inline(always)]
104 fn is_buffered(&self) -> bool {
105 self.get_ref().is_buffered()
106 }
107
108 /// Forwards an unchecked asynchronous write to the pinned boxed output.
109 ///
110 /// # Parameters
111 ///
112 /// - `cx`: Task context used to register a wake-up.
113 /// - `input`: Source item slice.
114 /// - `index`: Starting source index.
115 /// - `count`: Maximum number of items to write.
116 ///
117 /// # Returns
118 ///
119 /// Returns a pending state or the wrapped output's ready write result.
120 ///
121 /// # Errors
122 ///
123 /// Returns an error reported by the wrapped output.
124 ///
125 /// # Safety
126 ///
127 /// The range `index..index + count` must be valid for `input`.
128 #[inline(always)]
129 unsafe fn poll_write_unchecked(
130 self: Pin<&mut Self>,
131 cx: &mut Context<'_>,
132 input: &[Self::Item],
133 index: usize,
134 count: usize,
135 ) -> Poll<std::io::Result<usize>> {
136 // SAFETY: The caller's valid-range guarantee is forwarded unchanged.
137 unsafe { self.get_pin_mut().poll_write_unchecked(cx, input, index, count) }
138 }
139
140 /// Forwards an asynchronous flush to the pinned boxed output.
141 ///
142 /// # Parameters
143 ///
144 /// - `cx`: Task context used to register a wake-up.
145 ///
146 /// # Returns
147 ///
148 /// Returns a pending state or the wrapped output's ready flush result.
149 ///
150 /// # Errors
151 ///
152 /// Returns an error reported while flushing the wrapped output.
153 #[inline(always)]
154 fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
155 self.get_pin_mut().poll_flush(cx)
156 }
157}
158
159impl<O> AsyncClose for BoxAsyncOutput<O>
160where
161 O: AsyncClose + ?Sized,
162{
163 /// Forwards an asynchronous close to the pinned boxed output.
164 ///
165 /// # Parameters
166 ///
167 /// - `cx`: Task context used to register a wake-up.
168 ///
169 /// # Returns
170 ///
171 /// Returns a pending state or the wrapped output's ready close result.
172 ///
173 /// # Errors
174 ///
175 /// Returns an error reported while closing the wrapped output.
176 #[inline(always)]
177 fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
178 self.get_pin_mut().poll_close(cx)
179 }
180}