Skip to main content

dioxus_swdir_tree_core/
scan.rs

1//! The async-boundary types: side effects **as data**, and the one
2//! blocking function that executes them.
3//!
4//! Transitions on [`crate::DirectoryTree`] never spawn tasks. When disk
5//! access is needed they return a [`ScanRequest`]; the embedding layer
6//! (a Dioxus coroutine, a thread pool, or a test) runs [`run`] on a
7//! worker and feeds the produced [`LoadPayload`] back through
8//! [`crate::DirectoryTree::on_loaded`].
9
10use std::path::PathBuf;
11
12use swdir::{ScanOptions, scan_dir_with_options};
13
14use crate::entry::LoadedEntry;
15use crate::error::ScanIssue;
16
17/// A scan the embedding layer must execute off the UI thread.
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct ScanRequest {
20    /// Directory to list (one level, non-recursive).
21    pub path: PathBuf,
22    /// Generation the result must carry to be accepted.
23    pub generation: u32,
24    /// Depth of `path` below the root, in components.
25    pub depth: u32,
26}
27
28/// A completed scan, ready to merge.
29#[derive(Debug, Clone, PartialEq)]
30pub struct LoadPayload {
31    /// Directory that was listed.
32    pub path: PathBuf,
33    /// Generation copied from the originating [`ScanRequest`].
34    pub generation: u32,
35    /// Depth copied from the originating [`ScanRequest`].
36    pub depth: u32,
37    /// The entries, or the failure.
38    pub result: Result<Vec<LoadedEntry>, ScanIssue>,
39}
40
41/// What [`crate::DirectoryTree::on_loaded`] did with a payload.
42#[derive(Debug, Clone, Default, PartialEq, Eq)]
43pub struct LoadedOutcome {
44    /// `false` means the payload was stale (or its node vanished) and
45    /// the tree state is bit-identical to before the call.
46    pub accepted: bool,
47    /// Follow-up scans to execute. Always empty until prefetch
48    /// (RFC 009) lands; the field exists now so the signature never
49    /// breaks.
50    pub prefetch_requests: Vec<ScanRequest>,
51}
52
53impl LoadedOutcome {
54    /// Outcome for a silently discarded payload.
55    pub(crate) fn discarded() -> Self {
56        Self::default()
57    }
58
59    /// Outcome for an accepted merge with no follow-up work.
60    pub(crate) fn accepted() -> Self {
61        Self {
62            accepted: true,
63            prefetch_requests: Vec::new(),
64        }
65    }
66}
67
68/// Execute a [`ScanRequest`]: list the directory (sorted
69/// directories-first, name-ascending — the layout tree widgets expect)
70/// and package the result.
71///
72/// **Blocking.** Call this on a worker thread; never on the UI thread.
73pub fn run(request: &ScanRequest) -> LoadPayload {
74    let result = scan_dir_with_options(&request.path, &ScanOptions::default())
75        .map(|entries| entries.iter().map(LoadedEntry::from).collect())
76        .map_err(ScanIssue::from);
77    LoadPayload {
78        path: request.path.clone(),
79        generation: request.generation,
80        depth: request.depth,
81        result,
82    }
83}