(&self, request: WorkspaceGrepRequest)
| 810 | } |
| 811 | |
| 812 | async fn grep(&self, request: WorkspaceGrepRequest) -> Result<WorkspaceGrepResult> { |
| 813 | use futures::stream::StreamExt; |
| 814 | |
| 815 | if let Some(ref g) = request.glob { |
| 816 | validate_relative_pattern(g, "grep glob filter")?; |
| 817 | } |
| 818 | |
| 819 | let regex_pattern = if request.case_insensitive { |
| 820 | format!("(?i){}", request.pattern) |
| 821 | } else { |
| 822 | request.pattern.clone() |
| 823 | }; |
| 824 | let regex = std::sync::Arc::new( |
| 825 | regex::Regex::new(®ex_pattern) |
| 826 | .map_err(|e| anyhow!("Invalid regex pattern '{}': {}", request.pattern, e))?, |
| 827 | ); |
| 828 | |
| 829 | let glob_filter = match request.glob.as_deref() { |
| 830 | Some(g) => Some(( |
| 831 | glob::Pattern::new(g) |
| 832 | .map_err(|e| anyhow!("Invalid grep glob filter '{}': {}", g, e))?, |
| 833 | g.contains('/'), |
| 834 | )), |
| 835 | None => None, |
| 836 | }; |
| 837 | |
| 838 | let (entries, scan_truncated) = self |
| 839 | .list_recursive_under(&request.base, self.max_objects_scanned) |
| 840 | .await?; |
| 841 | |
| 842 | // Phase 1 — sequentially filter the listing (cheap; no I/O). We |
| 843 | // produce a list of objects that pass the glob filter and the |
| 844 | // per-object size cap. Oversized objects are skipped here, not |
| 845 | // downloaded. |
| 846 | let listing_prefix = self.list_prefix_for(&request.base); |
| 847 | let candidates: Vec<(WorkspacePath, String)> = entries |
| 848 | .into_iter() |
| 849 | .filter_map(|(rel, size)| { |
| 850 | if let Some((ref pat, has_sep)) = glob_filter { |
| 851 | let target = if has_sep { |
| 852 | rel.as_str() |
| 853 | } else { |
| 854 | basename(&rel) |
| 855 | }; |
| 856 | if !pat.matches(target) { |
| 857 | return None; |
| 858 | } |
| 859 | } |
| 860 | if size > self.max_grep_bytes_per_object { |
| 861 | tracing::debug!( |
| 862 | "Skipping S3 object {}{} ({} bytes > grep cap {})", |
| 863 | listing_prefix, |
| 864 | rel, |
| 865 | size, |
| 866 | self.max_grep_bytes_per_object |
| 867 | ); |
| 868 | return None; |
| 869 | } |
nothing calls this directly
no test coverage detected