Skip to main content

qubit_io/traits/
async_char_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 crate::AsyncInput;
10
11/// An asynchronous input whose items are Unicode scalar values.
12///
13/// This trait adds no methods. Every [`AsyncInput<Item = char>`](AsyncInput),
14/// including unsized streams, implements it automatically. Implement
15/// [`AsyncInput`] to provide the operations; no separate implementation of
16/// `AsyncCharInput` is needed.
17///
18/// Character counts measure Unicode scalar values, not encoded bytes or
19/// grapheme clusters. This trait does not encode or decode text.
20///
21/// The inherited polling methods retain their pinning and readiness contracts;
22/// implementors do not need to be `Unpin`.
23///
24/// # Examples
25///
26/// `CharSlice` is always ready, so it does not need to register a waker:
27/// ```
28/// use std::io;
29/// use std::pin::Pin;
30/// use std::task::Context;
31/// use std::task::Poll;
32/// use std::task::Waker;
33///
34/// use qubit_io::AsyncCharInput;
35/// use qubit_io::AsyncInput;
36///
37/// struct CharSlice<'a> {
38///     items: &'a [char],
39///     position: usize,
40/// }
41///
42/// impl AsyncInput for CharSlice<'_> {
43///     type Item = char;
44///
45///     unsafe fn poll_read_unchecked(
46///         self: Pin<&mut Self>,
47///         _cx: &mut Context<'_>,
48///         output: &mut [char],
49///         index: usize,
50///         count: usize,
51///     ) -> Poll<io::Result<usize>> {
52///         let this = self.get_mut();
53///         let read = count.min(this.items.len() - this.position);
54///         output[index..index + read]
55///             .copy_from_slice(&this.items[this.position..this.position + read]);
56///         this.position += read;
57///         Poll::Ready(Ok(read))
58///     }
59/// }
60///
61/// fn read<S: AsyncCharInput + ?Sized>(
62///     stream: Pin<&mut S>,
63///     cx: &mut Context<'_>,
64///     output: &mut [char],
65/// ) -> Poll<io::Result<usize>> {
66///     stream.poll_read(cx, output)
67/// }
68///
69/// let mut stream = CharSlice { items: &['你', '🦀'], position: 0 };
70/// let mut cx = Context::from_waker(Waker::noop());
71/// let mut output = ['\0'; 2];
72/// assert!(matches!(read(Pin::new(&mut stream), &mut cx, &mut output), Poll::Ready(Ok(2))));
73/// assert_eq!(output, ['你', '🦀']);
74/// assert!(matches!(read(Pin::new(&mut stream), &mut cx, &mut output), Poll::Ready(Ok(0))));
75/// ```
76///
77/// Streams with a different item type do not satisfy this bound:
78///
79/// ```compile_fail
80/// use qubit_io::AsyncCharInput;
81/// use qubit_io::AsyncInput;
82///
83/// fn accept(stream: impl AsyncCharInput) {}
84///
85/// fn reject(stream: impl AsyncInput<Item = u8>) {
86///     accept(stream);
87/// }
88/// ```
89pub trait AsyncCharInput: AsyncInput<Item = char> {
90    // empty
91}
92
93impl<T: AsyncInput<Item = char> + ?Sized> AsyncCharInput for T {}