1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Serialize, Deserialize)]
4pub struct SearchResult {
5 pub path: String,
6 pub content: String,
7 #[serde(skip_serializing_if = "Option::is_none")]
8 pub symbol_name: Option<String>,
9 #[serde(skip_serializing_if = "Option::is_none")]
10 pub symbol_type: Option<String>,
11 pub start_line: usize,
12 pub end_line: usize,
13 pub language: String,
14 pub score: f32,
15}
16
17impl SearchResult {
18 fn overlaps(&self, other: &SearchResult) -> bool {
20 self.path == other.path
21 && self.start_line <= other.end_line
22 && other.start_line <= self.end_line
23 }
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct SearchResponse {
28 pub results: Vec<SearchResult>,
29 pub query: String,
30 pub elapsed_ms: f64,
31 pub project: String,
32}
33
34impl SearchResponse {
35 pub fn deduplicate(&mut self) {
40 if self.results.len() <= 1 {
41 return;
42 }
43
44 self.results.sort_by(|a, b| {
46 b.score
47 .partial_cmp(&a.score)
48 .unwrap_or(std::cmp::Ordering::Equal)
49 });
50
51 let mut deduped = Vec::with_capacity(self.results.len());
52
53 for result in std::mem::take(&mut self.results) {
54 let dominated = deduped
56 .iter()
57 .any(|kept: &SearchResult| kept.overlaps(&result));
58
59 if !dominated {
60 deduped.push(result);
61 }
62 }
63
64 self.results = deduped;
65 }
66}