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 {}