1
//! A builder type for constructing and configuring [`Engine`] instances.
2

            
3
use crate::cache::{NoCache, RewriteCache};
4
use crate::engine::Engine;
5
use crate::events::EventHandlers;
6
use crate::helpers::{SelectorFn, select_first};
7
use crate::prelude::Rule;
8
use crate::rule::RuleGroups;
9

            
10
use paste::paste;
11
use uniplate::Uniplate;
12

            
13
/// A builder type for constructing and configuring [`Engine`] instances.
14
pub struct EngineBuilder<T, M, R, C>
15
where
16
    T: Uniplate + Send + Sync,
17
    R: Rule<T, M> + Clone,
18
    C: RewriteCache<T>,
19
{
20
    event_handlers: EventHandlers<T, M, R>,
21

            
22
    /// Groups of rules, each with a selector function.
23
    rule_groups: Vec<Vec<R>>,
24

            
25
    selector: SelectorFn<T, M, R>,
26

            
27
    cache: C,
28

            
29
    discriminant_fn: Option<fn(&T) -> usize>,
30

            
31
    parallel: bool,
32

            
33
    fixedpoint: bool,
34

            
35
    down_predicate: fn(&T) -> bool,
36
}
37

            
38
macro_rules! add_handler_fns {
39
    (
40
        directions: [$($dir:ident),*]
41
    ) => {
42
        paste! {$(
43
            /// Register an event handler to be called before moving $dir in the tree.
44
            pub fn [<add_before_ $dir>](mut self, handler: fn(&T, &mut M)) -> Self {
45
                self.event_handlers.[<add_before_ $dir>](handler);
46
                self
47
            }
48

            
49
            /// Register an event handler to be called after moving $dir one node in the tree.
50
            pub fn [<add_after_ $dir>](mut self, handler: fn(&T, &mut M)) -> Self {
51
                self.event_handlers.[<add_after_ $dir>](handler);
52
                self
53
            }
54
        )*}
55
    };
56
}
57

            
58
impl<T, M, R> EngineBuilder<T, M, R, NoCache>
59
where
60
    T: Uniplate + Send + Sync,
61
    R: Rule<T, M> + Clone,
62
{
63
    /// Creates a new builder instance with the default [`select_first`] selector.
64
40275
    pub fn new() -> Self {
65
        EngineBuilder {
66
40275
            event_handlers: EventHandlers::new(),
67
40275
            rule_groups: Vec::new(),
68
40275
            selector: select_first,
69
40275
            cache: NoCache,
70
40275
            discriminant_fn: None,
71
            parallel: false,
72
            fixedpoint: false,
73
            down_predicate: |_| true,
74
        }
75
40275
    }
76
}
77

            
78
impl<T, M, R, C> EngineBuilder<T, M, R, C>
79
where
80
    T: Uniplate + Send + Sync,
81
    R: Rule<T, M> + Clone,
82
    C: RewriteCache<T>,
83
{
84
    /// Consumes the builder and returns the constructed [`Engine`] instance.
85
40275
    pub fn build(self) -> Engine<T, M, R, C> {
86
40275
        Engine {
87
40275
            event_handlers: self.event_handlers,
88
40275
            rule_groups: RuleGroups::new(self.rule_groups, self.discriminant_fn),
89
40275
            selector: self.selector,
90
40275
            cache: self.cache,
91
40275
            parallel: self.parallel,
92
40275
            fixedpoint: self.fixedpoint,
93
40275
            down_predicate: self.down_predicate,
94
40275
        }
95
40275
    }
96

            
97
    /// Adds a collection of rules with the same priority.
98
    ///
99
    /// These rules will have a lower priority than previously added groups.
100
30
    pub fn add_rule_group(mut self, rules: Vec<R>) -> Self {
101
30
        self.rule_groups.push(rules);
102
30
        self
103
30
    }
104

            
105
    /// Adds a single rule in a group by itself.
106
    ///
107
    /// This is a special case of [`add_rule_group`](EngineBuilder::add_rule_group).
108
16
    pub fn add_rule(self, rule: R) -> Self {
109
16
        self.add_rule_group(vec![rule])
110
16
    }
111

            
112
    /// Adds a collection of rule groups to the existing one.
113
    ///
114
    /// Rule groups maintain the same order and will be lower priority than existing groups.
115
40250
    pub fn append_rule_groups(mut self, groups: Vec<Vec<R>>) -> Self {
116
40250
        self.rule_groups.extend(groups);
117
40250
        self
118
40250
    }
119

            
120
    add_handler_fns! {
121
        directions: [up, down, right]
122
    }
123

            
124
    /// Register an event handler to be called before attempting a rule
125
1
    pub fn add_before_rule(mut self, handler: fn(&T, &mut M, &R)) -> Self {
126
1
        self.event_handlers.add_before_rule(handler);
127
1
        self
128
1
    }
129

            
130
    /// Register an event handler to be called after attempting a rule
131
    /// The boolean signifies whether the rule is applicable
132
1
    pub fn add_after_rule(mut self, handler: fn(&T, &mut M, &R, bool)) -> Self {
133
1
        self.event_handlers.add_after_rule(handler);
134
1
        self
135
1
    }
136

            
137
    /// Register an event handler to be called after applying a rule
138
1
    pub fn add_after_apply(mut self, handler: fn(&T, &mut M, &R)) -> Self {
139
1
        self.event_handlers.add_after_apply(handler);
140
1
        self
141
1
    }
142

            
143
    /// Register an event handler to be called on a cache hit
144
1
    pub fn add_on_cache_hit(mut self, handler: fn(&T, &mut M)) -> Self {
145
1
        self.event_handlers.add_on_cache_hit(handler);
146
1
        self
147
1
    }
148

            
149
    /// Register an event handler to be called on a cache miss
150
1
    pub fn add_on_cache_miss(mut self, handler: fn(&T, &mut M)) -> Self {
151
1
        self.event_handlers.add_on_cache_miss(handler);
152
1
        self
153
1
    }
154

            
155
    /// Sets the selector function to be used when multiple rules are applicable to the same node.
156
    ///
157
    /// See the [`morph`](Engine::morph) method of the Engine type for more information.
158
40251
    pub fn set_selector(mut self, selector: SelectorFn<T, M, R>) -> Self {
159
40251
        self.selector = selector;
160
40251
        self
161
40251
    }
162

            
163
    /// Adds caching support to tree-morph.
164
    ///
165
    /// Recommended to use [`HashMapCache`] as it has a concrete
166
    /// implementation
167
40253
    pub fn add_cacher<Cache: RewriteCache<T>>(
168
40253
        self,
169
40253
        cacher: Cache,
170
40253
    ) -> EngineBuilder<T, M, R, Cache> {
171
40253
        EngineBuilder {
172
40253
            event_handlers: self.event_handlers,
173
40253
            rule_groups: self.rule_groups,
174
40253
            selector: self.selector,
175
40253
            cache: cacher,
176
40253
            discriminant_fn: self.discriminant_fn,
177
40253
            parallel: self.parallel,
178
40253
            fixedpoint: self.fixedpoint,
179
40253
            down_predicate: self.down_predicate,
180
40253
        }
181
40253
    }
182

            
183
    /// Enables or disables parallel rule checking via Rayon.
184
    ///
185
    /// When enabled, all rules are tested in parallel using `par_iter`.
186
    /// Defaults to `false`.
187
40250
    pub fn set_parallel(mut self, parallel: bool) -> Self {
188
40250
        self.parallel = parallel;
189
40250
        self
190
40250
    }
191

            
192
    /// Sets the discriminant function used for rule prefiltering.
193
    ///
194
    /// When `Some(f)` is provided, `f` is called on each node to compute a unique `usize` id,
195
    /// which is used to skip rules that do not apply to that node type.
196
    /// When `None`, prefiltering is disabled and all rules are tried on every node.
197
40250
    pub fn set_discriminant_fn(mut self, discriminant_fn: Option<fn(&T) -> usize>) -> Self {
198
40250
        self.discriminant_fn = discriminant_fn;
199
40250
        self
200
40250
    }
201

            
202
    /// Fixed-point application: after a rule fires, re-apply rules to the
203
    /// transformed node until no more rules match, before continuing traversal.
204
40250
    pub fn set_fixedpoint(mut self, fixedpoint: bool) -> Self {
205
40250
        self.fixedpoint = fixedpoint;
206
40250
        self
207
40250
    }
208

            
209
    /// Adds a predicate that controls whether the engine descends into a node's children.
210
    pub fn add_down_predicate(mut self, predicate: fn(&T) -> bool) -> Self {
211
        self.down_predicate = predicate;
212
        self
213
    }
214
}
215

            
216
impl<T, M, R> Default for EngineBuilder<T, M, R, NoCache>
217
where
218
    T: Uniplate + Send + Sync,
219
    R: Rule<T, M> + Clone,
220
{
221
    fn default() -> Self {
222
        Self::new()
223
    }
224
}
225

            
226
impl<T, M, R, C> From<EngineBuilder<T, M, R, C>> for Engine<T, M, R, C>
227
where
228
    T: Uniplate + Send + Sync,
229
    R: Rule<T, M> + Clone,
230
    C: RewriteCache<T>,
231
{
232
    fn from(val: EngineBuilder<T, M, R, C>) -> Self {
233
        val.build()
234
    }
235
}