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
39255
    pub fn new() -> Self {
65
        EngineBuilder {
66
39255
            event_handlers: EventHandlers::new(),
67
39255
            rule_groups: Vec::new(),
68
39255
            selector: select_first,
69
39255
            cache: NoCache,
70
39255
            discriminant_fn: None,
71
            parallel: false,
72
            fixedpoint: false,
73
            down_predicate: |_| true,
74
        }
75
39255
    }
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
39255
    pub fn build(self) -> Engine<T, M, R, C> {
86
39255
        Engine {
87
39255
            event_handlers: self.event_handlers,
88
39255
            rule_groups: RuleGroups::new(self.rule_groups, self.discriminant_fn),
89
39255
            selector: self.selector,
90
39255
            cache: self.cache,
91
39255
            parallel: self.parallel,
92
39255
            fixedpoint: self.fixedpoint,
93
39255
            down_predicate: self.down_predicate,
94
39255
        }
95
39255
    }
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
39230
    pub fn append_rule_groups(mut self, groups: Vec<Vec<R>>) -> Self {
116
39230
        self.rule_groups.extend(groups);
117
39230
        self
118
39230
    }
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
39231
    pub fn set_selector(mut self, selector: SelectorFn<T, M, R>) -> Self {
159
39231
        self.selector = selector;
160
39231
        self
161
39231
    }
162

            
163
    /// Adds caching support to tree-morph.
164
    ///
165
    /// Recommended to use [`HashMapCache`] as it has a concrete
166
    /// implementation
167
39233
    pub fn add_cacher<Cache: RewriteCache<T>>(
168
39233
        self,
169
39233
        cacher: Cache,
170
39233
    ) -> EngineBuilder<T, M, R, Cache> {
171
39233
        EngineBuilder {
172
39233
            event_handlers: self.event_handlers,
173
39233
            rule_groups: self.rule_groups,
174
39233
            selector: self.selector,
175
39233
            cache: cacher,
176
39233
            discriminant_fn: self.discriminant_fn,
177
39233
            parallel: self.parallel,
178
39233
            fixedpoint: self.fixedpoint,
179
39233
            down_predicate: self.down_predicate,
180
39233
        }
181
39233
    }
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
39230
    pub fn set_parallel(mut self, parallel: bool) -> Self {
188
39230
        self.parallel = parallel;
189
39230
        self
190
39230
    }
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
39230
    pub fn set_discriminant_fn(mut self, discriminant_fn: Option<fn(&T) -> usize>) -> Self {
198
39230
        self.discriminant_fn = discriminant_fn;
199
39230
        self
200
39230
    }
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
39230
    pub fn set_fixedpoint(mut self, fixedpoint: bool) -> Self {
205
39230
        self.fixedpoint = fixedpoint;
206
39230
        self
207
39230
    }
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
}