grit_pattern_matcher/pattern/
includes.rs

1use super::{
2    patterns::{Matcher, Pattern, PatternName},
3    resolved_pattern::ResolvedPattern,
4    State,
5};
6use crate::context::{ExecContext, QueryContext};
7use core::fmt::Debug;
8use grit_util::{
9    error::{GritPatternError, GritResult},
10    AnalysisLogs,
11};
12
13#[derive(Debug, Clone)]
14pub struct Includes<Q: QueryContext> {
15    pub includes: Pattern<Q>,
16}
17
18impl<Q: QueryContext> Includes<Q> {
19    pub fn new(includes: Pattern<Q>) -> Self {
20        Self { includes }
21    }
22}
23
24impl<Q: QueryContext> PatternName for Includes<Q> {
25    fn name(&self) -> &'static str {
26        "INCLUDES"
27    }
28}
29
30fn execute<'a, Q: QueryContext>(
31    pattern: &'a Pattern<Q>,
32    binding: &Q::ResolvedPattern<'a>,
33    state: &mut State<'a, Q>,
34    context: &'a Q::ExecContext<'a>,
35    logs: &mut AnalysisLogs,
36) -> GritResult<bool> {
37    match &pattern {
38        Pattern::Regex(pattern) => pattern.execute_matching(binding, state, context, logs, false),
39        Pattern::Or(pattern) => {
40            for p in pattern.patterns.iter() {
41                if execute(p, binding, state, context, logs)? {
42                    return Ok(true);
43                }
44            }
45            Ok(false)
46        }
47        Pattern::Any(pattern) => {
48            // Any is subtly different from or in that it will not short-circuit so we *must* execute all patterns
49            let mut any_matched = false;
50            for p in pattern.patterns.iter() {
51                if execute(p, binding, state, context, logs)? {
52                    any_matched = true;
53                }
54            }
55            Ok(any_matched)
56        }
57        Pattern::And(pattern) => {
58            for p in pattern.patterns.iter() {
59                if !execute(p, binding, state, context, logs)? {
60                    return Ok(false);
61                }
62            }
63            Ok(true)
64        }
65        Pattern::AstNode(_)
66        | Pattern::List(_)
67        | Pattern::ListIndex(_)
68        | Pattern::Map(_)
69        | Pattern::Accessor(_)
70        | Pattern::Call(_)
71        | Pattern::File(_)
72        | Pattern::Files(_)
73        | Pattern::Bubble(_)
74        | Pattern::Limit(_)
75        | Pattern::CallBuiltIn(_)
76        | Pattern::CallFunction(_)
77        | Pattern::CallForeignFunction(_)
78        | Pattern::Assignment(_)
79        | Pattern::Accumulate(_)
80        | Pattern::Maybe(_)
81        | Pattern::Not(_)
82        | Pattern::If(_)
83        | Pattern::Undefined
84        | Pattern::Top
85        | Pattern::Bottom
86        | Pattern::Underscore
87        | Pattern::StringConstant(_)
88        | Pattern::AstLeafNode(_)
89        | Pattern::IntConstant(_)
90        | Pattern::FloatConstant(_)
91        | Pattern::BooleanConstant(_)
92        | Pattern::Dynamic(_)
93        | Pattern::CodeSnippet(_)
94        | Pattern::Variable(_)
95        | Pattern::Rewrite(_)
96        | Pattern::Range(_)
97        | Pattern::Contains(_)
98        | Pattern::Includes(_)
99        | Pattern::Within(_)
100        | Pattern::After(_)
101        | Pattern::Before(_)
102        | Pattern::Where(_)
103        | Pattern::Some(_)
104        | Pattern::Every(_)
105        | Pattern::Add(_)
106        | Pattern::Subtract(_)
107        | Pattern::Multiply(_)
108        | Pattern::Divide(_)
109        | Pattern::Modulo(_)
110        | Pattern::Dots
111        | Pattern::Sequential(_)
112        | Pattern::Like(_)
113        | Pattern::CallbackPattern(_) => {
114            let resolved = Q::ResolvedPattern::from_pattern(pattern, state, context, logs)
115                .map_err(|err| {
116                    GritPatternError::new(format!(
117                        "{err} \n includes can only be used with patterns that can be resolved"
118                    ))
119                })?;
120            let substring = resolved.text(&state.files, context.language()).map_err(|err| {
121                GritPatternError::new(
122                    format!("{err} \n includes can only be used with patterns that can be resolved to a string")
123                )
124            })?;
125            let string = binding.text(&state.files, context.language()).map_err(|err| {
126                GritPatternError::new(
127                    format!("{err} \n includes can only be used with patterns that can be resolved to a string")
128                )
129            })?;
130            if string.contains(&*substring) {
131                Ok(true)
132            } else {
133                Ok(false)
134            }
135        }
136    }
137}
138
139// Includes and within should call the same function taking an iterator as an argument
140// even better two arguments an accumulator and an iterator.
141impl<Q: QueryContext> Matcher<Q> for Includes<Q> {
142    fn execute<'a>(
143        &'a self,
144        binding: &Q::ResolvedPattern<'a>,
145        state: &mut State<'a, Q>,
146        context: &'a Q::ExecContext<'a>,
147        logs: &mut AnalysisLogs,
148    ) -> GritResult<bool> {
149        execute(&self.includes, binding, state, context, logs)
150    }
151}