(
State(state): State<AppState>,
headers: HeaderMap,
Query(params): Query<GrepQuery>,
)
| 589 | } |
| 590 | |
| 591 | async fn grep_handler( |
| 592 | State(state): State<AppState>, |
| 593 | headers: HeaderMap, |
| 594 | Query(params): Query<GrepQuery>, |
| 595 | ) -> Result<Json<Value>, AppError> { |
| 596 | let project = require_project(&state, &headers)?; |
| 597 | let max_matches = params.max_matches.unwrap_or(50); |
| 598 | let context_lines = params.context_lines.unwrap_or(2); |
| 599 | let scope = params |
| 600 | .scope |
| 601 | .as_deref() |
| 602 | .map(|s| content::GrepScope::from_str(s)) |
| 603 | .flatten() |
| 604 | .unwrap_or(content::GrepScope::All); |
| 605 | |
| 606 | // Run grep on a blocking thread since it reads many files |
| 607 | let root = project.root.clone(); |
| 608 | let file_tree = project.file_tree.clone(); |
| 609 | let pattern = params.pattern.clone(); |
| 610 | let file_filter = params.file.clone(); |
| 611 | |
| 612 | let result = tokio::task::spawn_blocking(move || { |
| 613 | content::grep_with_scope(&root, &file_tree, &pattern, max_matches, context_lines, scope, file_filter.as_deref()) |
| 614 | }) |
| 615 | .await |
| 616 | .map_err(|e| AppError::Internal(e.to_string()))? |
| 617 | .map_err(AppError::BadRequest)?; |
| 618 | |
| 619 | let preview = format!("{} matches for '{}'", result.total_matches, params.pattern); |
| 620 | record_history(&state, session_id(&headers).as_deref(), "GET", "/grep", &preview); |
| 621 | Ok(Json(serde_json::to_value(result).unwrap())) |
| 622 | } |
| 623 | |
| 624 | #[derive(Deserialize)] |
| 625 | struct ChunkQuery { |
nothing calls this directly
no test coverage detected