Skip to main content

qubit_fs/directory/
directory_stream.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//! Concrete synchronous directory stream handle.
9
10use std::fmt::Debug;
11use std::fmt::Formatter;
12use std::fmt::Result as FmtResult;
13use std::time::Instant;
14
15use crate::directory::DirectoryStreamState;
16use crate::directory::ListOptions;
17use crate::directory::ListScope;
18use crate::directory::internal::ListStreamPolicy;
19use crate::error::FsResult;
20use crate::metadata::DirEntry;
21use crate::metadata::FileSystemLimits;
22use crate::spi::DirectoryStreamSpi;
23
24/// Type-erased synchronous directory enumeration handle.
25///
26/// The stream validates every provider entry against the requested root and
27/// listing options before returning it to the caller.
28///
29/// # Examples
30///
31/// This helper demonstrates the normal bounded-listing workflow without
32/// requiring a concrete provider in the documentation build.
33///
34/// ```
35/// # use qubit_fs::{FileSystem, FsResult, Path};
36/// # use qubit_fs::directory::ListOptions;
37/// # fn visit(filesystem: &FileSystem, root: &Path) -> FsResult<()> {
38/// let mut stream = filesystem.list(&qubit_fs::directory::ListScope::Path(root.clone()), ListOptions::default())?;
39/// while let Some(entry) = stream.next_entry()? {
40///     println!("{}", entry.path);
41/// }
42/// # Ok(())
43/// # }
44/// ```
45pub struct DirectoryStream {
46    /// Provider enumeration session.
47    session: Box<dyn DirectoryStreamSpi>,
48    /// Shared validation, deadline, and terminal-state policy.
49    policy: ListStreamPolicy,
50}
51
52impl DirectoryStream {
53    /// Wraps an already-open provider enumeration session.
54    ///
55    /// # Parameters
56    /// - `session`: Provider directory enumeration session.
57    ///
58    /// # Returns
59    /// A concrete type-erased directory stream.
60    #[inline]
61    pub(crate) fn new(
62        scope: ListScope,
63        session: Box<dyn DirectoryStreamSpi>,
64        options: ListOptions,
65        provider: &str,
66        path_semantics: crate::path::PathSemantics,
67        limits: FileSystemLimits,
68    ) -> FsResult<Self> {
69        let policy = ListStreamPolicy::new(scope, options, provider, path_semantics, limits, Instant::now())?;
70        Ok(Self { session, policy })
71    }
72
73    /// Returns the current lifecycle state of this stream.
74    #[inline]
75    #[must_use = "inspect the stream lifecycle state"]
76    pub const fn state(&self) -> DirectoryStreamState {
77        self.policy.state()
78    }
79
80    /// Reads the next directory entry.
81    ///
82    /// # Returns
83    /// `Some` for one entry or `None` at end of enumeration.
84    ///
85    /// # Errors
86    /// Returns a filesystem error when enumeration cannot continue.
87    #[inline]
88    pub fn next_entry(&mut self) -> FsResult<Option<DirEntry>> {
89        self.policy.before_next(Instant::now())?;
90        let result = self.session.next_entry();
91        self.policy.finish_next(result, Instant::now())
92    }
93}
94
95impl Debug for DirectoryStream {
96    #[inline]
97    fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
98        formatter.debug_struct("DirectoryStream").finish_non_exhaustive()
99    }
100}