1use std::sync::Arc;
4
5use super::{BuildError, ConditionBuilder, RegexBuilder};
6use crate::query::registry::FieldRegistry;
7use crate::query::types::{
8 Expr, FieldType, Operator, Query as QueryAST, RegexFlags, RegexValue, Span, Value,
9};
10
11#[derive(Clone, Debug)]
24#[must_use = "QueryBuilder does nothing until .build() is called"]
25pub struct QueryBuilder {
26 expr: BuilderExpr,
28 errors: Vec<BuildError>,
30}
31
32#[derive(Clone, Debug)]
34enum BuilderExpr {
35 Condition(ConditionBuilder),
37 And(Vec<QueryBuilder>),
39 Or(Vec<QueryBuilder>),
41 Not(Box<QueryBuilder>),
43 Empty,
45}
46
47impl QueryBuilder {
52 pub fn new() -> Self {
54 Self {
55 expr: BuilderExpr::Empty,
56 errors: Vec::new(),
57 }
58 }
59
60 pub fn kind(value: impl Into<String>) -> Self {
66 Self::condition("kind", Operator::Equal, Value::String(value.into()))
67 }
68
69 pub fn kind_any(values: &[&str]) -> Self {
71 Self::any(values.iter().map(|v| Self::kind(*v)).collect())
72 }
73
74 pub fn name(value: impl Into<String>) -> Self {
76 Self::condition("name", Operator::Equal, Value::String(value.into()))
77 }
78
79 pub fn name_matches(pattern: impl Into<String>) -> Self {
81 let regex = RegexValue {
82 pattern: pattern.into(),
83 flags: RegexFlags::default(),
84 };
85 Self::condition("name", Operator::Regex, Value::Regex(regex))
86 }
87
88 pub fn name_matches_with<F>(pattern: impl Into<String>, configure: F) -> Self
97 where
98 F: FnOnce(RegexBuilder) -> RegexBuilder,
99 {
100 let builder = RegexBuilder::new(pattern);
101 let configured = configure(builder);
102 Self::condition(
103 "name",
104 Operator::Regex,
105 Value::Regex(configured.into_regex_value()),
106 )
107 }
108
109 pub fn lang(value: impl Into<String>) -> Self {
111 Self::condition("lang", Operator::Equal, Value::String(value.into()))
112 }
113
114 pub fn language(value: impl Into<String>) -> Self {
116 Self::lang(value)
117 }
118
119 pub fn path(value: impl Into<String>) -> Self {
125 Self::condition("path", Operator::Equal, Value::String(value.into()))
126 }
127
128 pub fn file(value: impl Into<String>) -> Self {
130 Self::path(value)
131 }
132
133 pub fn path_matches(pattern: impl Into<String>) -> Self {
135 let regex = RegexValue {
136 pattern: pattern.into(),
137 flags: RegexFlags::default(),
138 };
139 Self::condition("path", Operator::Regex, Value::Regex(regex))
140 }
141
142 pub fn path_matches_with<F>(pattern: impl Into<String>, configure: F) -> Self
151 where
152 F: FnOnce(RegexBuilder) -> RegexBuilder,
153 {
154 let builder = RegexBuilder::new(pattern);
155 let configured = configure(builder);
156 Self::condition(
157 "path",
158 Operator::Regex,
159 Value::Regex(configured.into_regex_value()),
160 )
161 }
162
163 pub fn repo(value: impl Into<String>) -> Self {
165 Self::condition("repo", Operator::Equal, Value::String(value.into()))
166 }
167
168 pub fn parent(value: impl Into<String>) -> Self {
174 Self::condition("parent", Operator::Equal, Value::String(value.into()))
175 }
176
177 pub fn text_matches(pattern: impl Into<String>) -> Self {
183 let regex = RegexValue {
184 pattern: pattern.into(),
185 flags: RegexFlags::default(),
186 };
187 Self::condition("text", Operator::Regex, Value::Regex(regex))
188 }
189
190 pub fn text_matches_with<F>(pattern: impl Into<String>, configure: F) -> Self
199 where
200 F: FnOnce(RegexBuilder) -> RegexBuilder,
201 {
202 let builder = RegexBuilder::new(pattern);
203 let configured = configure(builder);
204 Self::condition(
205 "text",
206 Operator::Regex,
207 Value::Regex(configured.into_regex_value()),
208 )
209 }
210
211 pub fn callers(symbol: impl Into<String>) -> Self {
217 Self::condition("callers", Operator::Equal, Value::String(symbol.into()))
218 }
219
220 pub fn callees(symbol: impl Into<String>) -> Self {
222 Self::condition("callees", Operator::Equal, Value::String(symbol.into()))
223 }
224
225 pub fn imports(module: impl Into<String>) -> Self {
227 Self::condition("imports", Operator::Equal, Value::String(module.into()))
228 }
229
230 pub fn exports(value: impl Into<String>) -> Self {
232 Self::condition("exports", Operator::Equal, Value::String(value.into()))
233 }
234
235 pub fn returns(type_name: impl Into<String>) -> Self {
237 Self::condition("returns", Operator::Equal, Value::String(type_name.into()))
238 }
239
240 pub fn references(symbol: impl Into<String>) -> Self {
242 Self::condition("references", Operator::Equal, Value::String(symbol.into()))
243 }
244
245 pub fn scope(value: impl Into<String>) -> Self {
253 Self::condition("scope", Operator::Equal, Value::String(value.into()))
254 }
255
256 pub fn scope_type(value: impl Into<String>) -> Self {
260 Self::condition("scope.type", Operator::Equal, Value::String(value.into()))
261 }
262
263 pub fn scope_name(value: impl Into<String>) -> Self {
265 Self::condition("scope.name", Operator::Equal, Value::String(value.into()))
266 }
267
268 pub fn scope_parent(value: impl Into<String>) -> Self {
270 Self::condition("scope.parent", Operator::Equal, Value::String(value.into()))
271 }
272
273 pub fn scope_ancestor(value: impl Into<String>) -> Self {
275 Self::condition(
276 "scope.ancestor",
277 Operator::Equal,
278 Value::String(value.into()),
279 )
280 }
281
282 pub fn field(name: impl Into<String>, value: impl Into<Value>) -> Self {
288 Self::condition_value(name.into(), Operator::Equal, value.into())
289 }
290
291 pub fn field_matches(name: impl Into<String>, pattern: impl Into<String>) -> Self {
293 let regex = RegexValue {
294 pattern: pattern.into(),
295 flags: RegexFlags::default(),
296 };
297 Self::condition_value(name.into(), Operator::Regex, Value::Regex(regex))
298 }
299
300 pub fn field_matches_with<F>(
309 name: impl Into<String>,
310 pattern: impl Into<String>,
311 configure: F,
312 ) -> Self
313 where
314 F: FnOnce(RegexBuilder) -> RegexBuilder,
315 {
316 let builder = RegexBuilder::new(pattern);
317 let configured = configure(builder);
318 Self::condition_value(
319 name.into(),
320 Operator::Regex,
321 Value::Regex(configured.into_regex_value()),
322 )
323 }
324
325 pub fn field_gt(name: impl Into<String>, value: i64) -> Self {
327 Self::condition_value(name.into(), Operator::Greater, Value::Number(value))
328 }
329
330 pub fn field_gte(name: impl Into<String>, value: i64) -> Self {
332 Self::condition_value(name.into(), Operator::GreaterEq, Value::Number(value))
333 }
334
335 pub fn field_lt(name: impl Into<String>, value: i64) -> Self {
337 Self::condition_value(name.into(), Operator::Less, Value::Number(value))
338 }
339
340 pub fn field_lte(name: impl Into<String>, value: i64) -> Self {
342 Self::condition_value(name.into(), Operator::LessEq, Value::Number(value))
343 }
344
345 fn condition(field: &'static str, operator: Operator, value: Value) -> Self {
351 Self {
352 expr: BuilderExpr::Condition(ConditionBuilder::new_static(field, operator, value)),
353 errors: Vec::new(),
354 }
355 }
356
357 fn condition_value(field: String, operator: Operator, value: Value) -> Self {
359 Self {
360 expr: BuilderExpr::Condition(ConditionBuilder::new(field, operator, value)),
361 errors: Vec::new(),
362 }
363 }
364}
365
366impl QueryBuilder {
371 pub fn all(conditions: Vec<QueryBuilder>) -> Self {
373 let errors = conditions.iter().flat_map(|c| c.errors.clone()).collect();
374 Self {
375 expr: BuilderExpr::And(conditions),
376 errors,
377 }
378 }
379
380 pub fn any(conditions: Vec<QueryBuilder>) -> Self {
382 let errors = conditions.iter().flat_map(|c| c.errors.clone()).collect();
383 Self {
384 expr: BuilderExpr::Or(conditions),
385 errors,
386 }
387 }
388
389 pub fn and(self, other: QueryBuilder) -> Self {
391 let mut errors = self.errors;
393 errors.extend(other.errors.clone());
394
395 match self.expr {
396 BuilderExpr::Empty => Self {
397 expr: other.expr,
398 errors,
399 },
400 BuilderExpr::And(mut exprs) => {
401 exprs.push(other);
402 Self {
403 expr: BuilderExpr::And(exprs),
404 errors,
405 }
406 }
407 _ => Self {
408 expr: BuilderExpr::And(vec![
409 Self {
410 expr: self.expr,
411 errors: Vec::new(),
412 },
413 other,
414 ]),
415 errors,
416 },
417 }
418 }
419
420 pub fn or(self, other: QueryBuilder) -> Self {
422 let mut errors = self.errors;
424 errors.extend(other.errors.clone());
425
426 match self.expr {
427 BuilderExpr::Empty => Self {
428 expr: other.expr,
429 errors,
430 },
431 BuilderExpr::Or(mut exprs) => {
432 exprs.push(other);
433 Self {
434 expr: BuilderExpr::Or(exprs),
435 errors,
436 }
437 }
438 _ => Self {
439 expr: BuilderExpr::Or(vec![
440 Self {
441 expr: self.expr,
442 errors: Vec::new(),
443 },
444 other,
445 ]),
446 errors,
447 },
448 }
449 }
450
451 pub fn and_not(self, other: QueryBuilder) -> Self {
453 self.and(Self::negate(other))
454 }
455
456 pub fn negate(builder: QueryBuilder) -> Self {
461 let errors = builder.errors.clone();
462 Self {
463 expr: BuilderExpr::Not(Box::new(builder)),
464 errors,
465 }
466 }
467}
468
469impl QueryBuilder {
474 pub fn build(self) -> Result<Arc<QueryAST>, BuildError> {
486 let registry = FieldRegistry::with_core_fields();
487 self.build_with_registry(®istry)
488 }
489
490 pub fn build_with_registry(
499 self,
500 registry: &FieldRegistry,
501 ) -> Result<Arc<QueryAST>, BuildError> {
502 if !self.errors.is_empty() {
504 return Err(BuildError::Multiple(self.errors));
505 }
506
507 let expr = self.into_expr(registry)?;
509
510 Ok(Arc::new(QueryAST {
511 root: expr,
512 span: Span::synthetic(),
513 }))
514 }
515
516 fn into_expr(self, registry: &FieldRegistry) -> Result<Expr, BuildError> {
517 match self.expr {
518 BuilderExpr::Empty => Err(BuildError::EmptyQuery),
519 BuilderExpr::Condition(ref cond) => {
520 Self::validate_condition(cond, registry)?;
522 Ok(Expr::Condition(cond.clone().into_condition(registry)))
524 }
525 BuilderExpr::And(exprs) => {
526 let children: Result<Vec<_>, _> =
527 exprs.into_iter().map(|e| e.into_expr(registry)).collect();
528 Ok(Expr::And(children?))
529 }
530 BuilderExpr::Or(exprs) => {
531 let children: Result<Vec<_>, _> =
532 exprs.into_iter().map(|e| e.into_expr(registry)).collect();
533 Ok(Expr::Or(children?))
534 }
535 BuilderExpr::Not(inner) => Ok(Expr::Not(Box::new(inner.into_expr(registry)?))),
536 }
537 }
538
539 fn validate_condition(
540 cond: &ConditionBuilder,
541 registry: &FieldRegistry,
542 ) -> Result<(), BuildError> {
543 let descriptor = registry
545 .get(cond.field())
546 .ok_or_else(|| BuildError::UnknownField {
547 field: cond.field().to_string(),
548 available: registry.field_names().join(", "),
549 })?;
550
551 if !descriptor.supports_operator(cond.operator()) {
553 return Err(BuildError::InvalidOperator {
554 field: cond.field().to_string(),
555 operator: cond.operator().clone(),
556 field_type: format!("{:?}", descriptor.field_type),
557 });
558 }
559
560 Self::validate_value_type(cond.field(), &descriptor.field_type, cond.value())?;
562
563 Self::validate_regex_pattern(cond.value())?;
566
567 Self::validate_enum_value(cond.field(), cond.value(), &descriptor.field_type)?;
569
570 Ok(())
571 }
572
573 fn validate_regex_pattern(value: &Value) -> Result<(), BuildError> {
574 if let Value::Regex(regex_value) = value {
575 let has_lookaround = regex_value.pattern.contains("(?=")
578 || regex_value.pattern.contains("(?!")
579 || regex_value.pattern.contains("(?<=")
580 || regex_value.pattern.contains("(?<!");
581
582 if has_lookaround {
583 fancy_regex::Regex::new(®ex_value.pattern).map_err(|e| {
585 BuildError::InvalidFancyRegex {
586 pattern: regex_value.pattern.clone(),
587 error: e.to_string(),
588 }
589 })?;
590 } else {
591 let mut builder = regex::RegexBuilder::new(®ex_value.pattern);
593 builder.case_insensitive(regex_value.flags.case_insensitive);
594 builder.multi_line(regex_value.flags.multiline);
595 builder.dot_matches_new_line(regex_value.flags.dot_all);
596 builder.build()?;
597 }
598 }
599 Ok(())
600 }
601
602 fn validate_enum_value(
603 field: &str,
604 value: &Value,
605 field_type: &FieldType,
606 ) -> Result<(), BuildError> {
607 if let (FieldType::Enum(valid), Value::String(s)) = (field_type, value)
610 && !valid.contains(&s.as_str())
611 {
612 return Err(BuildError::InvalidEnumValue {
613 field: field.to_string(),
614 value: s.clone(),
615 valid: valid.join(", "),
616 });
617 }
618
619 Ok(())
620 }
621
622 fn validate_value_type(
623 field: &str,
624 field_type: &FieldType,
625 value: &Value,
626 ) -> Result<(), BuildError> {
627 let is_valid = matches!(
630 (field_type, value),
631 (
632 FieldType::String | FieldType::Path | FieldType::Enum(_),
633 Value::String(_) | Value::Regex(_)
634 ) | (FieldType::Number, Value::Number(_))
635 | (FieldType::Bool, Value::Boolean(_)) );
637
638 if !is_valid {
639 return Err(BuildError::ValueTypeMismatch {
640 field: field.to_string(),
641 expected: format!("{field_type:?}"),
642 actual: value.type_name().to_string(),
643 });
644 }
645
646 Ok(())
647 }
648}
649
650impl Default for QueryBuilder {
651 fn default() -> Self {
652 Self::new()
653 }
654}
655
656impl From<&str> for Value {
661 fn from(s: &str) -> Self {
662 Value::String(s.to_string())
663 }
664}
665
666impl From<String> for Value {
667 fn from(s: String) -> Self {
668 Value::String(s)
669 }
670}
671
672impl From<i64> for Value {
673 fn from(n: i64) -> Self {
674 Value::Number(n)
675 }
676}
677
678impl From<bool> for Value {
679 fn from(b: bool) -> Self {
680 Value::Boolean(b)
681 }
682}