Skip to main content

bwq_lint/validation/
engine.rs

1use super::rules::*;
2use super::{ValidationContext, ValidationRule};
3use crate::ast::*;
4use crate::error::{LintError, LintReport, LintWarning};
5
6pub struct ValidationEngine {
7    rules: Vec<Box<dyn ValidationRule>>,
8}
9
10impl ValidationEngine {
11    pub fn new() -> Self {
12        Self {
13            rules: vec![
14                // Field validation rules
15                Box::new(RatingFieldRule),
16                Box::new(CoordinateFieldRule),
17                Box::new(LanguageFieldRule),
18                Box::new(AuthorGenderFieldRule),
19                Box::new(BooleanFieldRule),
20                Box::new(EngagementTypeFieldRule),
21                Box::new(VerifiedTypeFieldRule),
22                Box::new(MinuteOfDayFieldRule),
23                Box::new(RangeFieldRule),
24                // Operator validation rules
25                Box::new(MixedAndOrRule),
26                Box::new(MixedNearRule),
27                Box::new(PureNegativeRule),
28                Box::new(BinaryOperatorRule),
29                // Performance validation rules
30                Box::new(WildcardPerformanceRule),
31                Box::new(ShortTermRule),
32                Box::new(RangePerformanceRule),
33            ],
34        }
35    }
36
37    pub fn validate(&self, query: &Query) -> LintReport {
38        let mut all_errors = Vec::new();
39        let mut all_warnings = Vec::new();
40
41        let ctx = ValidationContext::default();
42        self.walk_expression(&query.expression, &ctx, &mut all_errors, &mut all_warnings);
43
44        LintReport {
45            errors: all_errors,
46            warnings: all_warnings,
47        }
48    }
49
50    fn walk_expression(
51        &self,
52        expr: &Expression,
53        ctx: &ValidationContext,
54        errors: &mut Vec<LintError>,
55        warnings: &mut Vec<LintWarning>,
56    ) {
57        // Apply all relevant rules to this expression
58        for rule in &self.rules {
59            if rule.can_validate(expr) {
60                let result = rule.validate(expr, ctx);
61                errors.extend(result.errors);
62                warnings.extend(result.warnings);
63            }
64        }
65
66        // Recursively validate child expressions with updated context
67        match expr {
68            Expression::BooleanOp {
69                operator,
70                left,
71                right,
72                ..
73            } => {
74                let mut child_ctx = ctx.clone();
75                child_ctx.parent_operator = Some(operator.clone());
76
77                self.walk_expression(left, &child_ctx, errors, warnings);
78                if let Some(right_expr) = right {
79                    self.walk_expression(right_expr, &child_ctx, errors, warnings);
80                }
81            }
82            Expression::Group { expression, .. } => {
83                let mut group_ctx = ctx.clone();
84                group_ctx.inside_group = true;
85                self.walk_expression(expression, &group_ctx, errors, warnings);
86            }
87            Expression::Proximity { terms, .. } => {
88                for term in terms {
89                    self.walk_expression(term, ctx, errors, warnings);
90                }
91            }
92            Expression::Field { field, value, .. } => {
93                let mut field_ctx = ctx.clone();
94                field_ctx.field_context = Some(field.clone());
95                self.walk_expression(value, &field_ctx, errors, warnings);
96            }
97            Expression::Range { .. } | Expression::Term { .. } | Expression::Comment { .. } => {
98                // Terminal nodes - no recursion needed
99            }
100        }
101    }
102}
103
104impl Default for ValidationEngine {
105    fn default() -> Self {
106        Self::new()
107    }
108}