anshdadhich commited on
Commit
edcdb12
Β·
verified Β·
1 Parent(s): ad60b1c

Upload src/index/search.rs

Browse files
Files changed (1) hide show
  1. src/index/search.rs +129 -0
src/index/search.rs ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ use rayon::prelude::*;
2
+ use crate::index::store::IndexStore;
3
+
4
+ const APP_EXTENSIONS: &[&str] = &["exe", "lnk", "msi", "appx", "msix"];
5
+ const APP_PATH_MARKERS: &[&str] = &[
6
+ "\\program files\\", "\\program files (x86)\\",
7
+ "\\start menu\\", "\\desktop\\", "\\appdata\\",
8
+ ];
9
+
10
+ #[derive(Debug, Clone)]
11
+ pub struct SearchResult {
12
+ pub full_path: std::path::PathBuf,
13
+ pub name: String,
14
+ pub rank: u8,
15
+ pub is_dir: bool,
16
+ }
17
+
18
+ pub fn search(
19
+ store: &IndexStore,
20
+ query: &str,
21
+ limit: usize,
22
+ case_sensitive: bool,
23
+ excluded_dirs: &[String],
24
+ ) -> Vec<SearchResult> {
25
+ if query.is_empty() {
26
+ return Vec::new();
27
+ }
28
+
29
+ let q = if case_sensitive { query.to_string() } else { query.to_lowercase() };
30
+
31
+ // ── Phase 1: lightweight name-only matching ──────────────────────────
32
+ let entries = &store.entries;
33
+ let name_lower_arena = &store.name_lower_arena;
34
+ let name_arena = &store.name_arena;
35
+
36
+ let mut candidates: Vec<(u32, u8)> = entries
37
+ .par_iter()
38
+ .enumerate()
39
+ .filter_map(|(idx, entry)| {
40
+ let name_cmp = if case_sensitive {
41
+ unsafe { std::str::from_utf8_unchecked(&name_arena[entry.name_off as usize..(entry.name_off as usize + entry.name_len as usize)]) }
42
+ } else {
43
+ unsafe { std::str::from_utf8_unchecked(&name_lower_arena[entry.name_lower_off as usize..(entry.name_lower_off as usize + entry.name_lower_len as usize)]) }
44
+ };
45
+
46
+ let rank = if name_cmp == q { 1u8 }
47
+ else if name_cmp.starts_with(&q) { 2 }
48
+ else if name_cmp.contains(q.as_str()) { 3 }
49
+ else { return None; };
50
+
51
+ Some((idx as u32, rank))
52
+ })
53
+ .collect();
54
+
55
+ // ── Phase 2: sort by rank, keep overshoot buffer ─────────────────
56
+ candidates.sort_unstable_by_key(|&(_, rank)| rank);
57
+ let overshoot = (limit * 5).max(1000);
58
+ candidates.truncate(overshoot);
59
+
60
+ // ── Phase 3: build paths + exclusions + app promotion ────────────
61
+ let mut results: Vec<SearchResult> = Vec::with_capacity(limit);
62
+
63
+ for &(idx, base_rank) in &candidates {
64
+ let entry = &entries[idx as usize];
65
+ let full_path = build_path(entry.file_ref, store);
66
+
67
+ if !excluded_dirs.is_empty() {
68
+ let path_lower = full_path.to_string_lossy().to_lowercase();
69
+ if excluded_dirs.iter().any(|ex| path_lower.starts_with(ex.as_str())) {
70
+ continue;
71
+ }
72
+ }
73
+
74
+ let name_lower = store.name_lower(entry);
75
+ let rank = if base_rank <= 2 {
76
+ let ext_is_app = name_lower
77
+ .rsplit('.')
78
+ .next()
79
+ .map(|e| APP_EXTENSIONS.contains(&e))
80
+ .unwrap_or(false);
81
+ if ext_is_app {
82
+ let path_lower = full_path.to_string_lossy().to_lowercase();
83
+ if APP_PATH_MARKERS.iter().any(|m| path_lower.contains(m)) { 0 } else { base_rank }
84
+ } else {
85
+ base_rank
86
+ }
87
+ } else {
88
+ base_rank
89
+ };
90
+
91
+ results.push(SearchResult {
92
+ full_path,
93
+ name: store.name(entry).to_string(),
94
+ rank,
95
+ is_dir: entry.is_dir(),
96
+ });
97
+ }
98
+
99
+ results.sort_unstable_by_key(|r| r.rank);
100
+ results.truncate(limit);
101
+ results
102
+ }
103
+
104
+ /// Iterative path builder β€” walks parent chain via sorted ref_lookup.
105
+ pub fn build_path(file_ref: u64, store: &IndexStore) -> std::path::PathBuf {
106
+ let mut components: Vec<&str> = Vec::with_capacity(16);
107
+ let mut current = file_ref;
108
+
109
+ for _ in 0..64 {
110
+ match store.lookup_idx(current) {
111
+ Some(idx) => {
112
+ let entry = &store.entries[idx as usize];
113
+ components.push(store.name(entry));
114
+ if entry.parent_ref == current {
115
+ break;
116
+ }
117
+ current = entry.parent_ref;
118
+ }
119
+ None => break,
120
+ }
121
+ }
122
+
123
+ components.reverse();
124
+ let mut path = std::path::PathBuf::from(&store.drive_root);
125
+ for comp in components {
126
+ path.push(comp);
127
+ }
128
+ path
129
+ }