ibid
This commit is contained in:
@@ -52,12 +52,8 @@ pub struct SearchResult {
|
|||||||
///
|
///
|
||||||
/// This is a naive implementation that reads entire files into memory.
|
/// This is a naive implementation that reads entire files into memory.
|
||||||
/// For production use, consider using streaming/buffered reads or the `grep-searcher` crate.
|
/// For production use, consider using streaming/buffered reads or the `grep-searcher` crate.
|
||||||
#[tauri::command]
|
/// Search files implementation (pure function for testing)
|
||||||
pub async fn search_files(
|
pub async fn search_files_impl(query: String, root: PathBuf) -> Result<Vec<SearchResult>, String> {
|
||||||
query: String,
|
|
||||||
state: State<'_, SessionState>,
|
|
||||||
) -> Result<Vec<SearchResult>, String> {
|
|
||||||
let root = get_project_root(&state)?;
|
|
||||||
let root_clone = root.clone();
|
let root_clone = root.clone();
|
||||||
|
|
||||||
// Run computationally expensive search on a blocking thread
|
// Run computationally expensive search on a blocking thread
|
||||||
@@ -105,6 +101,15 @@ pub async fn search_files(
|
|||||||
Ok(results)
|
Ok(results)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn search_files(
|
||||||
|
query: String,
|
||||||
|
state: State<'_, SessionState>,
|
||||||
|
) -> Result<Vec<SearchResult>, String> {
|
||||||
|
let root = get_project_root(&state)?;
|
||||||
|
search_files_impl(query, root).await
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Tests
|
// Tests
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -126,58 +131,11 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper to call search_files logic directly for testing
|
|
||||||
async fn call_search_files(
|
|
||||||
query: String,
|
|
||||||
state: &SessionState,
|
|
||||||
) -> Result<Vec<SearchResult>, String> {
|
|
||||||
let root = state.get_project_root()?;
|
|
||||||
let root_clone = root.clone();
|
|
||||||
|
|
||||||
let results = tauri::async_runtime::spawn_blocking(move || {
|
|
||||||
let mut matches = Vec::new();
|
|
||||||
let walker = WalkBuilder::new(&root_clone).git_ignore(true).build();
|
|
||||||
|
|
||||||
for result in walker {
|
|
||||||
match result {
|
|
||||||
Ok(entry) => {
|
|
||||||
if !entry.file_type().map(|ft| ft.is_file()).unwrap_or(false) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let path = entry.path();
|
|
||||||
if let Ok(content) = fs::read_to_string(path) {
|
|
||||||
if !content.contains(&query) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let relative = path
|
|
||||||
.strip_prefix(&root_clone)
|
|
||||||
.unwrap_or(path)
|
|
||||||
.to_string_lossy()
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
matches.push(SearchResult {
|
|
||||||
path: relative,
|
|
||||||
matches: 1,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => eprintln!("Error walking dir: {}", err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
matches
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.map_err(|e| format!("Search task failed: {}", e))?;
|
|
||||||
|
|
||||||
Ok(results)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_search_files_no_project_open() {
|
async fn test_search_files_no_project_open() {
|
||||||
let state = create_test_state(None);
|
let state = create_test_state(None);
|
||||||
|
|
||||||
let result = call_search_files("test".to_string(), &state).await;
|
let result = state.get_project_root();
|
||||||
|
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert_eq!(result.unwrap_err(), "No project is currently open.");
|
assert_eq!(result.unwrap_err(), "No project is currently open.");
|
||||||
@@ -191,7 +149,8 @@ mod tests {
|
|||||||
|
|
||||||
let state = create_test_state(Some(temp_dir.path().to_path_buf()));
|
let state = create_test_state(Some(temp_dir.path().to_path_buf()));
|
||||||
|
|
||||||
let results = call_search_files("test".to_string(), &state).await.unwrap();
|
let root = state.get_project_root().unwrap();
|
||||||
|
let results = search_files_impl("test".to_string(), root).await.unwrap();
|
||||||
|
|
||||||
assert_eq!(results.len(), 1);
|
assert_eq!(results.len(), 1);
|
||||||
assert_eq!(results[0].path, "test.txt");
|
assert_eq!(results[0].path, "test.txt");
|
||||||
@@ -209,9 +168,8 @@ mod tests {
|
|||||||
|
|
||||||
let state = create_test_state(Some(temp_dir.path().to_path_buf()));
|
let state = create_test_state(Some(temp_dir.path().to_path_buf()));
|
||||||
|
|
||||||
let results = call_search_files("hello".to_string(), &state)
|
let root = state.get_project_root().unwrap();
|
||||||
.await
|
let results = search_files_impl("hello".to_string(), root).await.unwrap();
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(results.len(), 2);
|
assert_eq!(results.len(), 2);
|
||||||
let paths: Vec<&str> = results.iter().map(|r| r.path.as_str()).collect();
|
let paths: Vec<&str> = results.iter().map(|r| r.path.as_str()).collect();
|
||||||
@@ -226,7 +184,8 @@ mod tests {
|
|||||||
|
|
||||||
let state = create_test_state(Some(temp_dir.path().to_path_buf()));
|
let state = create_test_state(Some(temp_dir.path().to_path_buf()));
|
||||||
|
|
||||||
let results = call_search_files("nonexistent".to_string(), &state)
|
let root = state.get_project_root().unwrap();
|
||||||
|
let results = search_files_impl("nonexistent".to_string(), root)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -241,15 +200,14 @@ mod tests {
|
|||||||
let state = create_test_state(Some(temp_dir.path().to_path_buf()));
|
let state = create_test_state(Some(temp_dir.path().to_path_buf()));
|
||||||
|
|
||||||
// Search for lowercase - should not match
|
// Search for lowercase - should not match
|
||||||
let results = call_search_files("hello".to_string(), &state)
|
let root = state.get_project_root().unwrap();
|
||||||
|
let results = search_files_impl("hello".to_string(), root.clone())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(results.len(), 0);
|
assert_eq!(results.len(), 0);
|
||||||
|
|
||||||
// Search for correct case - should match
|
// Search for correct case - should match
|
||||||
let results = call_search_files("Hello".to_string(), &state)
|
let results = search_files_impl("Hello".to_string(), root).await.unwrap();
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(results.len(), 1);
|
assert_eq!(results.len(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,9 +224,8 @@ mod tests {
|
|||||||
|
|
||||||
let state = create_test_state(Some(temp_dir.path().to_path_buf()));
|
let state = create_test_state(Some(temp_dir.path().to_path_buf()));
|
||||||
|
|
||||||
let results = call_search_files("match".to_string(), &state)
|
let root = state.get_project_root().unwrap();
|
||||||
.await
|
let results = search_files_impl("match".to_string(), root).await.unwrap();
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(results.len(), 2);
|
assert_eq!(results.len(), 2);
|
||||||
let paths: Vec<&str> = results.iter().map(|r| r.path.as_str()).collect();
|
let paths: Vec<&str> = results.iter().map(|r| r.path.as_str()).collect();
|
||||||
@@ -296,7 +253,8 @@ mod tests {
|
|||||||
|
|
||||||
let state = create_test_state(Some(temp_dir.path().to_path_buf()));
|
let state = create_test_state(Some(temp_dir.path().to_path_buf()));
|
||||||
|
|
||||||
let results = call_search_files("searchterm".to_string(), &state)
|
let root = state.get_project_root().unwrap();
|
||||||
|
let results = search_files_impl("searchterm".to_string(), root)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -324,7 +282,8 @@ mod tests {
|
|||||||
|
|
||||||
let state = create_test_state(Some(temp_dir.path().to_path_buf()));
|
let state = create_test_state(Some(temp_dir.path().to_path_buf()));
|
||||||
|
|
||||||
let results = call_search_files("searchable".to_string(), &state)
|
let root = state.get_project_root().unwrap();
|
||||||
|
let results = search_files_impl("searchable".to_string(), root)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -340,26 +299,10 @@ mod tests {
|
|||||||
|
|
||||||
let state = create_test_state(Some(temp_dir.path().to_path_buf()));
|
let state = create_test_state(Some(temp_dir.path().to_path_buf()));
|
||||||
|
|
||||||
let results = call_search_files("".to_string(), &state).await.unwrap();
|
let root = state.get_project_root().unwrap();
|
||||||
|
let results = search_files_impl("".to_string(), root).await.unwrap();
|
||||||
|
|
||||||
// Empty string is contained in all strings, so should match
|
// Empty string is contained in all strings, so should match
|
||||||
assert_eq!(results.len(), 1);
|
assert_eq!(results.len(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_get_project_root_helper() {
|
|
||||||
// Test with no root set
|
|
||||||
let state = create_test_state(None);
|
|
||||||
let result = state.get_project_root();
|
|
||||||
assert!(result.is_err());
|
|
||||||
assert_eq!(result.unwrap_err(), "No project is currently open.");
|
|
||||||
|
|
||||||
// Test with root set
|
|
||||||
let temp_dir = TempDir::new().unwrap();
|
|
||||||
let path = temp_dir.path().to_path_buf();
|
|
||||||
let state = create_test_state(Some(path.clone()));
|
|
||||||
let result = state.get_project_root();
|
|
||||||
assert!(result.is_ok());
|
|
||||||
assert_eq!(result.unwrap(), path);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user