1use crate::pattern::CaseOptions;
4use crate::query::UsageContext;
5use crate::Pattern;
6use crate::SymbolKind;
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
11pub enum SortOrder {
12 #[default]
14 Refs,
15 Alpha,
17 Impls,
19}
20
21#[derive(Debug, Clone, Default, Serialize, Deserialize)]
29pub struct TypeFilter {
30 pub return_type: Option<Pattern>,
33
34 pub param_type: Option<Pattern>,
37
38 pub field_type: Option<Pattern>,
41
42 pub uses_type: Option<Pattern>,
45
46 pub has_bound: Option<Pattern>,
49}
50
51impl TypeFilter {
52 pub fn returns(pattern: impl Into<String>) -> Self {
54 Self {
55 return_type: Some(Pattern::glob(pattern.into())),
56 ..Default::default()
57 }
58 }
59
60 pub fn has_param(pattern: impl Into<String>) -> Self {
62 Self {
63 param_type: Some(Pattern::glob(pattern.into())),
64 ..Default::default()
65 }
66 }
67
68 pub fn has_field(pattern: impl Into<String>) -> Self {
70 Self {
71 field_type: Some(Pattern::glob(pattern.into())),
72 ..Default::default()
73 }
74 }
75
76 pub fn uses(pattern: impl Into<String>) -> Self {
78 Self {
79 uses_type: Some(Pattern::glob(pattern.into())),
80 ..Default::default()
81 }
82 }
83
84 pub fn with_bound(mut self, pattern: impl Into<String>) -> Self {
86 self.has_bound = Some(Pattern::glob(pattern.into()));
87 self
88 }
89
90 pub fn is_empty(&self) -> bool {
92 self.return_type.is_none()
93 && self.param_type.is_none()
94 && self.field_type.is_none()
95 && self.uses_type.is_none()
96 && self.has_bound.is_none()
97 }
98
99 pub fn expected_contexts(&self) -> Vec<UsageContext> {
101 let mut contexts = Vec::new();
102 if self.return_type.is_some() {
103 contexts.push(UsageContext::ReturnType);
104 }
105 if self.param_type.is_some() {
106 contexts.push(UsageContext::ParamType);
107 }
108 if self.field_type.is_some() {
109 contexts.push(UsageContext::FieldType);
110 contexts.push(UsageContext::VariantField);
111 }
112 contexts
113 }
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct DiscoveryQuery {
119 pub pattern: Pattern,
121 #[serde(default, skip_serializing_if = "is_default_case_options")]
123 pub case_options: CaseOptions,
124 pub kinds: Option<Vec<SymbolKind>>,
126 pub in_crate: Option<String>,
128 pub in_module: Option<String>,
130 pub include_relations: bool,
132 pub relation_depth: usize,
134 pub limit: Option<usize>,
136 pub sort: SortOrder,
138 pub type_filter: Option<TypeFilter>,
140}
141
142fn is_default_case_options(opts: &CaseOptions) -> bool {
143 opts.is_default()
144}
145
146impl DiscoveryQuery {
147 pub fn symbol(pattern: impl Into<String>) -> Self {
149 Self {
150 pattern: Pattern::glob(pattern.into()),
151 case_options: CaseOptions::default(),
152 kinds: None,
153 in_crate: None,
154 in_module: None,
155 include_relations: false,
156 relation_depth: 1,
157 limit: None,
158 sort: SortOrder::default(),
159 type_filter: None,
160 }
161 }
162
163 pub fn symbol_with_options(pattern: impl Into<String>, case_options: CaseOptions) -> Self {
165 Self {
166 pattern: Pattern::glob_with_options(pattern.into(), case_options),
167 case_options,
168 kinds: None,
169 in_crate: None,
170 in_module: None,
171 include_relations: false,
172 relation_depth: 1,
173 limit: None,
174 sort: SortOrder::default(),
175 type_filter: None,
176 }
177 }
178
179 pub fn exact(name: impl Into<String>) -> Self {
181 Self {
182 pattern: Pattern::exact(name.into()),
183 case_options: CaseOptions::default(),
184 kinds: None,
185 in_crate: None,
186 in_module: None,
187 include_relations: false,
188 relation_depth: 1,
189 limit: None,
190 sort: SortOrder::default(),
191 type_filter: None,
192 }
193 }
194
195 pub fn regex(pattern: impl Into<String>) -> Result<Self, crate::pattern::PatternError> {
197 Ok(Self {
198 pattern: Pattern::regex(pattern.into())?,
199 case_options: CaseOptions::default(),
200 kinds: None,
201 in_crate: None,
202 in_module: None,
203 include_relations: false,
204 relation_depth: 1,
205 limit: None,
206 sort: SortOrder::default(),
207 type_filter: None,
208 })
209 }
210
211 pub fn ignore_case(mut self) -> Self {
219 self.case_options.ignore_case = true;
220 let pattern_str = self.pattern.as_str().to_string();
222 self.pattern = if self.pattern.is_glob() {
223 Pattern::glob_with_options(pattern_str, self.case_options)
224 } else if self.pattern.is_regex() {
225 Pattern::regex_with_options(pattern_str, self.case_options)
226 .unwrap_or_else(|_| self.pattern.clone())
227 } else {
228 Pattern::exact_with_options(pattern_str, self.case_options)
229 };
230 self
231 }
232
233 pub fn ignore_word_separate(mut self) -> Self {
243 self.case_options.ignore_word_separate = true;
244 let pattern_str = self.pattern.as_str().to_string();
246 self.pattern = if self.pattern.is_glob() {
247 Pattern::glob_with_options(pattern_str, self.case_options)
248 } else if self.pattern.is_regex() {
249 self.pattern.clone()
251 } else {
252 Pattern::exact_with_options(pattern_str, self.case_options)
253 };
254 self
255 }
256
257 pub fn kinds(mut self, kinds: Vec<SymbolKind>) -> Self {
259 self.kinds = Some(kinds);
260 self
261 }
262
263 pub fn kind(mut self, kind: SymbolKind) -> Self {
265 self.kinds = Some(vec![kind]);
266 self
267 }
268
269 pub fn in_crate(mut self, crate_name: impl Into<String>) -> Self {
271 self.in_crate = Some(crate_name.into());
272 self
273 }
274
275 pub fn in_module(mut self, module: impl Into<String>) -> Self {
277 self.in_module = Some(module.into());
278 self
279 }
280
281 pub fn with_relations(mut self) -> Self {
283 self.include_relations = true;
284 self
285 }
286
287 pub fn relation_depth(mut self, depth: usize) -> Self {
289 self.relation_depth = depth;
290 self
291 }
292
293 pub fn limit(mut self, limit: usize) -> Self {
295 self.limit = Some(limit);
296 self
297 }
298
299 pub fn sort(mut self, sort: SortOrder) -> Self {
301 self.sort = sort;
302 self
303 }
304
305 pub fn with_type_filter(mut self, filter: TypeFilter) -> Self {
307 self.type_filter = Some(filter);
308 self
309 }
310
311 pub fn returns(self, pattern: impl Into<String>) -> Self {
313 self.with_type_filter(TypeFilter::returns(pattern))
314 }
315
316 pub fn has_param_type(self, pattern: impl Into<String>) -> Self {
318 self.with_type_filter(TypeFilter::has_param(pattern))
319 }
320
321 pub fn has_field_type(self, pattern: impl Into<String>) -> Self {
323 self.with_type_filter(TypeFilter::has_field(pattern))
324 }
325}
326
327impl Default for DiscoveryQuery {
328 fn default() -> Self {
329 Self::symbol("*")
330 }
331}
332
333#[cfg(test)]
334mod tests {
335 use super::*;
336
337 #[test]
338 fn test_symbol_query() {
339 let query = DiscoveryQuery::symbol("*Config");
340 assert!(query.pattern.matches("AppConfig"));
341 assert!(query.kinds.is_none());
342 }
343
344 #[test]
345 fn test_exact_query() {
346 let query = DiscoveryQuery::exact("Config");
347 assert!(query.pattern.matches("Config"));
348 assert!(!query.pattern.matches("AppConfig"));
349 }
350
351 #[test]
352 fn test_query_with_kinds() {
353 let query = DiscoveryQuery::symbol("*").kinds(vec![SymbolKind::Struct, SymbolKind::Enum]);
354 assert_eq!(query.kinds.as_ref().unwrap().len(), 2);
355 }
356
357 #[test]
358 fn test_query_chaining() {
359 let query = DiscoveryQuery::symbol("*Handler")
360 .kind(SymbolKind::Struct)
361 .in_crate("mylib")
362 .in_module("handlers")
363 .with_relations()
364 .limit(10);
365
366 assert!(query.kinds.is_some());
367 assert_eq!(query.in_crate, Some("mylib".to_string()));
368 assert_eq!(query.in_module, Some("handlers".to_string()));
369 assert!(query.include_relations);
370 assert_eq!(query.limit, Some(10));
371 }
372}