Skip to main content

qubit_fs/read/
async_file_reader.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// facade tests.
9//! Concrete asynchronous file reader handle.
10
11use std::fmt::Debug;
12use std::fmt::Formatter;
13use std::fmt::Result as FmtResult;
14use std::io::Result as IoResult;
15use std::pin::Pin;
16use std::task::Context;
17use std::task::Poll;
18
19use qubit_io::AsyncInput;
20use qubit_io::BoxAsyncInput;
21
22use crate::metadata::OpenedFileInfo;
23
24/// Type-erased asynchronous byte input associated with an opened file.
25///
26/// # Examples
27///
28/// This example uses an isolated in-memory provider fixture.
29///
30/// ```rust
31/// # mod support { include!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/common/rustdoc_support.rs")); }
32/// # use support::*;
33/// # let (filesystem, _) = async_recording_spi::async_recording_file_system(Default::default());
34/// # poll_support::ready(async {
35/// use qubit_fs::Path;
36/// use qubit_fs::read::ReadOptions;
37/// use qubit_io::AsyncInput;
38///
39/// let mut reader = filesystem.open_reader(&Path::parse("/report")?, ReadOptions::default()).await?;
40/// let mut prefix = [0; 3];
41/// assert_eq!(3, reader.read_fully_async(&mut prefix).await?);
42/// assert_eq!(*b"byt", prefix);
43/// # Ok::<(), Box<dyn std::error::Error>>(())
44/// # }).unwrap();
45/// ```
46pub struct AsyncFileReader {
47    /// Pinned provider byte input.
48    inner: BoxAsyncInput<dyn AsyncInput<Item = u8> + Send>,
49    /// Stable identity and metadata captured at open time.
50    info: OpenedFileInfo,
51}
52
53impl AsyncFileReader {
54    /// Wraps an already-open asynchronous provider byte input.
55    ///
56    /// # Parameters
57    /// - `inner`: Runtime-neutral asynchronous byte input.
58    /// - `info`: File identity and optional open-time metadata snapshot.
59    ///
60    /// # Returns
61    /// A pinned, type-erased asynchronous file reader.
62    #[inline]
63    #[must_use]
64    pub(crate) fn new(info: OpenedFileInfo, inner: Box<dyn AsyncInput<Item = u8> + Send>) -> Self {
65        Self {
66            inner: BoxAsyncInput::new(inner),
67            info,
68        }
69    }
70
71    /// Returns the fixed identity and open-time metadata snapshot.
72    ///
73    /// # Returns
74    /// Information captured when the reader was opened.
75    #[inline]
76    #[must_use]
77    pub fn info(&self) -> &OpenedFileInfo {
78        &self.info
79    }
80}
81
82impl AsyncInput for AsyncFileReader {
83    type Item = u8;
84
85    #[inline]
86    fn is_buffered(&self) -> bool {
87        self.inner.is_buffered()
88    }
89
90    #[inline]
91    unsafe fn poll_read_unchecked(
92        self: Pin<&mut Self>,
93        cx: &mut Context<'_>,
94        output: &mut [u8],
95        index: usize,
96        count: usize,
97    ) -> Poll<IoResult<usize>> {
98        let this = self.get_mut();
99        // SAFETY: The caller guarantees the same range contract required by
100        // the wrapped asynchronous input.
101        unsafe {
102            Pin::new(&mut this.inner)
103                .get_pin_mut()
104                .poll_read_unchecked(cx, output, index, count)
105        }
106    }
107}
108
109impl Debug for AsyncFileReader {
110    #[inline]
111    fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
112        formatter
113            .debug_struct("AsyncFileReader")
114            .field("info", &self.info)
115            .finish_non_exhaustive()
116    }
117}