Skip to main content

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}