Skip to main content

dioxus_swdir_tree_core/
search.rs

1//! Incremental search over the already-loaded node graph.
2//!
3//! Search never triggers I/O (S9.9): it filters the **currently loaded**
4//! tree. [`walk_for_search`] is called every time the query, the filter,
5//! or the node graph changes.
6
7use std::collections::HashSet;
8use std::path::PathBuf;
9
10use crate::node::TreeNode;
11
12/// Active search session held on [`crate::DirectoryTree`].
13///
14/// `None` when no search is active.
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct SearchState {
17    /// Query as provided by the application (original casing).
18    pub query: String,
19    /// `query.to_ascii_lowercase()` — used for comparisons (S9.1).
20    pub query_lower: String,
21    /// Paths that are visible in search mode: direct matches ∪ ancestors
22    /// of matches (S9.2). Used by [`crate::DirectoryTree::visible_rows`]
23    /// to gate which rows are drawn.
24    pub visible_paths: HashSet<PathBuf>,
25    /// Count of **direct** matches only; ancestors shown for context are
26    /// not included (S9.8). Applications should use this for "N results"
27    /// displays.
28    pub match_count: usize,
29}
30
31/// Walk `node` and its already-loaded descendants, populating
32/// `visible` with every matching path and every ancestor of a match
33/// (S9.2, S9.3). Returns `true` iff the subtree rooted at `node`
34/// contains at least one match (so the caller knows to include `node`
35/// in the visible set).
36///
37/// The walk ignores `is_expanded` — it descends into any loaded
38/// directory regardless of its expansion state (S9.3).
39pub fn walk_for_search(
40    node: &TreeNode,
41    query_lower: &str,
42    visible: &mut HashSet<PathBuf>,
43    match_count: &mut usize,
44) -> bool {
45    let basename_lower = node.file_name().to_string_lossy().to_ascii_lowercase();
46    let self_matches = basename_lower.contains(query_lower);
47    if self_matches {
48        *match_count += 1;
49    }
50
51    // Recurse into loaded children.
52    let mut descendant_matches = false;
53    for child in &node.children {
54        if walk_for_search(child, query_lower, visible, match_count) {
55            descendant_matches = true;
56        }
57    }
58
59    let has_visible = self_matches || descendant_matches;
60    if has_visible {
61        visible.insert(node.path.clone());
62    }
63    has_visible
64}