1use anyhow::{Context, Result};
4
5use crate::content_store::ContentReader;
6use crate::trigram::TrigramIndex;
7
8pub fn find_file_id(content_reader: &ContentReader, target_path: &str) -> Option<u32> {
10 for file_id in 0..content_reader.file_count() {
11 if let Some(path) = content_reader.get_file_path(file_id as u32)
12 && path.to_string_lossy() == target_path
13 {
14 return Some(file_id as u32);
15 }
16 }
17 None
18}
19
20pub fn rebuild_trigram_index(content_reader: &ContentReader) -> Result<TrigramIndex> {
22 log::debug!(
23 "Rebuilding trigram index from {} files",
24 content_reader.file_count()
25 );
26 let mut trigram_index = TrigramIndex::new();
27
28 for file_id in 0..content_reader.file_count() {
29 let file_path = content_reader
30 .get_file_path(file_id as u32)
31 .context("Invalid file_id")?
32 .to_path_buf();
33 let content = content_reader.get_file_content(file_id as u32)?;
34
35 let idx = trigram_index.add_file(file_path);
36 trigram_index.index_file(idx, content);
37 }
38
39 trigram_index.finalize();
40 log::debug!(
41 "Trigram index rebuilt with {} trigrams",
42 trigram_index.trigram_count()
43 );
44
45 Ok(trigram_index)
46}
47
48pub fn normalize_glob_pattern(pattern: &str) -> String {
66 if pattern.starts_with('.') || pattern.starts_with('/') || pattern.starts_with('*') {
67 pattern.to_string()
68 } else {
69 format!("**/{}", pattern)
70 }
71}
72
73#[cfg(test)]
74mod normalize_glob_tests {
75 use super::normalize_glob_pattern;
76 use globset::Glob;
77
78 fn matches(pattern: &str, path: &str) -> bool {
79 let normalized = normalize_glob_pattern(pattern);
80 Glob::new(&normalized)
81 .unwrap()
82 .compile_matcher()
83 .is_match(path)
84 }
85
86 #[test]
87 fn relative_patterns_get_recursive_prefix() {
88 assert_eq!(normalize_glob_pattern("src/**/*.rs"), "**/src/**/*.rs");
89 assert_eq!(normalize_glob_pattern("main.rs"), "**/main.rs");
90 assert_eq!(normalize_glob_pattern("src/**"), "**/src/**");
91 }
92
93 #[test]
94 fn anchored_and_prefixed_patterns_are_unchanged() {
95 assert_eq!(
96 normalize_glob_pattern("./services/**/*.php"),
97 "./services/**/*.php"
98 );
99 assert_eq!(normalize_glob_pattern("/abs/path/*.rs"), "/abs/path/*.rs");
100 assert_eq!(normalize_glob_pattern("**/foo"), "**/foo");
101 assert_eq!(normalize_glob_pattern("*.rs"), "*.rs");
102 }
103
104 #[test]
109 fn src_glob_matches_bare_stored_paths() {
110 assert!(matches("src/**", "src/parsers/rust.rs"));
111 assert!(matches("src/**", "src/mcp.rs"));
112 assert!(matches("src/**/*.rs", "src/parsers/rust.rs"));
113 assert!(matches("src/**/*.rs", "src/mcp.rs"));
114 }
115
116 #[test]
117 fn src_glob_does_not_match_unrelated_paths() {
118 assert!(!matches("src/**", "src_helpers/foo.rs"));
120 assert!(!matches("src/**/*.rs", "benches/foo.rs"));
121 }
122
123 #[test]
124 fn bare_filename_matches_at_any_depth() {
125 assert!(matches("main.rs", "src/main.rs"));
126 assert!(matches("main.rs", "main.rs"));
127 }
128}