brainwires_rag/rag/types/
search.rs1use serde::{Deserialize, Serialize};
2
3use super::index::PROJECT_NAME_MAX_LENGTH;
4use super::query::{QueryRequest, default_limit, default_min_score};
5
6#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
8pub struct AdvancedSearchRequest {
9 pub query: String,
11 #[serde(default)]
13 pub path: Option<String>,
14 #[serde(default)]
16 pub project: Option<String>,
17 #[serde(default = "default_limit")]
19 pub limit: usize,
20 #[serde(default = "default_min_score")]
22 pub min_score: f32,
23 #[serde(default)]
25 pub file_extensions: Vec<String>,
26 #[serde(default)]
28 pub languages: Vec<String>,
29 #[serde(default)]
31 pub path_patterns: Vec<String>,
32}
33
34impl AdvancedSearchRequest {
35 pub fn validate(&self) -> Result<(), String> {
37 let query_req = QueryRequest {
39 query: self.query.clone(),
40 path: None,
41 project: self.project.clone(),
42 limit: self.limit,
43 min_score: self.min_score,
44 hybrid: true,
45 };
46 query_req.validate()?;
47
48 for ext in &self.file_extensions {
50 if ext.is_empty() {
51 return Err("file extension cannot be empty".to_string());
52 }
53 if ext.len() > 20 {
54 return Err(format!(
55 "file extension too long: {} (max 20 characters)",
56 ext
57 ));
58 }
59 }
60
61 for lang in &self.languages {
63 if lang.is_empty() {
64 return Err("language name cannot be empty".to_string());
65 }
66 if lang.len() > 50 {
67 return Err(format!(
68 "language name too long: {} (max 50 characters)",
69 lang
70 ));
71 }
72 }
73
74 Ok(())
75 }
76}
77
78pub fn default_git_path() -> String {
80 ".".to_string()
81}
82
83pub fn default_max_commits() -> usize {
85 10
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
90pub struct SearchGitHistoryRequest {
91 pub query: String,
93 #[serde(default = "default_git_path")]
95 pub path: String,
96 #[serde(default)]
98 pub project: Option<String>,
99 #[serde(default)]
101 pub branch: Option<String>,
102 #[serde(default = "default_max_commits")]
104 pub max_commits: usize,
105 #[serde(default = "default_limit")]
107 pub limit: usize,
108 #[serde(default = "default_min_score")]
110 pub min_score: f32,
111 #[serde(default)]
113 pub author: Option<String>,
114 #[serde(default)]
116 pub since: Option<String>,
117 #[serde(default)]
119 pub until: Option<String>,
120 #[serde(default)]
122 pub file_pattern: Option<String>,
123}
124
125impl SearchGitHistoryRequest {
126 pub fn validate(&self) -> Result<(), String> {
128 if self.query.trim().is_empty() {
130 return Err("query cannot be empty".to_string());
131 }
132
133 const MAX_QUERY_LENGTH: usize = 10_240; if self.query.len() > MAX_QUERY_LENGTH {
135 return Err(format!(
136 "query too long: {} bytes (max: {} bytes)",
137 self.query.len(),
138 MAX_QUERY_LENGTH
139 ));
140 }
141
142 let path = std::path::Path::new(&self.path);
144 if !path.exists() {
145 return Err(format!("Path does not exist: {}", self.path));
146 }
147
148 if !(0.0..=1.0).contains(&self.min_score) {
150 return Err(format!(
151 "min_score must be between 0.0 and 1.0, got: {}",
152 self.min_score
153 ));
154 }
155
156 const MAX_LIMIT: usize = 1000;
158 if self.limit > MAX_LIMIT {
159 return Err(format!(
160 "limit too large: {} (max: {})",
161 self.limit, MAX_LIMIT
162 ));
163 }
164
165 const MAX_COMMITS_LIMIT: usize = 10000;
167 if self.max_commits > MAX_COMMITS_LIMIT {
168 return Err(format!(
169 "max_commits too large: {} (max: {})",
170 self.max_commits, MAX_COMMITS_LIMIT
171 ));
172 }
173
174 if let Some(ref project) = self.project {
176 if project.is_empty() {
177 return Err("project name cannot be empty".to_string());
178 }
179 if project.len() > PROJECT_NAME_MAX_LENGTH {
180 return Err(format!(
181 "project name too long (max {} characters)",
182 PROJECT_NAME_MAX_LENGTH
183 ));
184 }
185 }
186
187 Ok(())
188 }
189}
190
191#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
193pub struct GitSearchResult {
194 pub commit_hash: String,
196 pub commit_message: String,
198 pub author: String,
200 pub author_email: String,
202 pub commit_date: i64,
204 pub score: f32,
206 pub vector_score: f32,
208 pub keyword_score: Option<f32>,
210 pub files_changed: Vec<String>,
212 pub diff_snippet: String,
214}
215
216#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
218pub struct SearchGitHistoryResponse {
219 pub results: Vec<GitSearchResult>,
221 pub commits_indexed: usize,
223 pub total_cached_commits: usize,
225 pub duration_ms: u64,
227}