Lines
81.63 %
Functions
26.9 %
//! A builder type for constructing and configuring [`Engine`] instances.
use crate::cache::{NoCache, RewriteCache};
use crate::engine::Engine;
use crate::events::EventHandlers;
use crate::helpers::{SelectorFn, select_first};
use crate::prelude::Rule;
use crate::rule::RuleGroups;
use paste::paste;
use uniplate::Uniplate;
/// A builder type for constructing and configuring [`Engine`] instances.
pub struct EngineBuilder<T, M, R, C>
where
T: Uniplate + Send + Sync,
R: Rule<T, M> + Clone,
C: RewriteCache<T>,
{
event_handlers: EventHandlers<T, M, R>,
/// Groups of rules, each with a selector function.
rule_groups: Vec<Vec<R>>,
selector: SelectorFn<T, M, R>,
cache: C,
discriminant_fn: Option<fn(&T) -> usize>,
parallel: bool,
fixedpoint: bool,
down_predicate: fn(&T) -> bool,
}
macro_rules! add_handler_fns {
(
directions: [$($dir:ident),*]
) => {
paste! {$(
/// Register an event handler to be called before moving $dir in the tree.
pub fn [<add_before_ $dir>](mut self, handler: fn(&T, &mut M)) -> Self {
self.event_handlers.[<add_before_ $dir>](handler);
self
/// Register an event handler to be called after moving $dir one node in the tree.
pub fn [<add_after_ $dir>](mut self, handler: fn(&T, &mut M)) -> Self {
self.event_handlers.[<add_after_ $dir>](handler);
)*}
};
impl<T, M, R> EngineBuilder<T, M, R, NoCache>
/// Creates a new builder instance with the default [`select_first`] selector.
pub fn new() -> Self {
EngineBuilder {
event_handlers: EventHandlers::new(),
rule_groups: Vec::new(),
selector: select_first,
cache: NoCache,
discriminant_fn: None,
parallel: false,
fixedpoint: false,
down_predicate: |_| true,
impl<T, M, R, C> EngineBuilder<T, M, R, C>
/// Consumes the builder and returns the constructed [`Engine`] instance.
pub fn build(self) -> Engine<T, M, R, C> {
Engine {
event_handlers: self.event_handlers,
rule_groups: RuleGroups::new(self.rule_groups, self.discriminant_fn),
selector: self.selector,
cache: self.cache,
parallel: self.parallel,
fixedpoint: self.fixedpoint,
down_predicate: self.down_predicate,
/// Adds a collection of rules with the same priority.
///
/// These rules will have a lower priority than previously added groups.
pub fn add_rule_group(mut self, rules: Vec<R>) -> Self {
self.rule_groups.push(rules);
/// Adds a single rule in a group by itself.
/// This is a special case of [`add_rule_group`](EngineBuilder::add_rule_group).
pub fn add_rule(self, rule: R) -> Self {
self.add_rule_group(vec![rule])
/// Adds a collection of rule groups to the existing one.
/// Rule groups maintain the same order and will be lower priority than existing groups.
pub fn append_rule_groups(mut self, groups: Vec<Vec<R>>) -> Self {
self.rule_groups.extend(groups);
add_handler_fns! {
directions: [up, down, right]
/// Register an event handler to be called before attempting a rule
pub fn add_before_rule(mut self, handler: fn(&T, &mut M, &R)) -> Self {
self.event_handlers.add_before_rule(handler);
/// Register an event handler to be called after attempting a rule
/// The boolean signifies whether the rule is applicable
pub fn add_after_rule(mut self, handler: fn(&T, &mut M, &R, bool)) -> Self {
self.event_handlers.add_after_rule(handler);
/// Register an event handler to be called after applying a rule
pub fn add_after_apply(mut self, handler: fn(&T, &mut M, &R)) -> Self {
self.event_handlers.add_after_apply(handler);
/// Register an event handler to be called on a cache hit
pub fn add_on_cache_hit(mut self, handler: fn(&T, &mut M)) -> Self {
self.event_handlers.add_on_cache_hit(handler);
/// Register an event handler to be called on a cache miss
pub fn add_on_cache_miss(mut self, handler: fn(&T, &mut M)) -> Self {
self.event_handlers.add_on_cache_miss(handler);
/// Sets the selector function to be used when multiple rules are applicable to the same node.
/// See the [`morph`](Engine::morph) method of the Engine type for more information.
pub fn set_selector(mut self, selector: SelectorFn<T, M, R>) -> Self {
self.selector = selector;
/// Adds caching support to tree-morph.
/// Recommended to use [`HashMapCache`] as it has a concrete
/// implementation
pub fn add_cacher<Cache: RewriteCache<T>>(
self,
cacher: Cache,
) -> EngineBuilder<T, M, R, Cache> {
rule_groups: self.rule_groups,
cache: cacher,
discriminant_fn: self.discriminant_fn,
/// Enables or disables parallel rule checking via Rayon.
/// When enabled, all rules are tested in parallel using `par_iter`.
/// Defaults to `false`.
pub fn set_parallel(mut self, parallel: bool) -> Self {
self.parallel = parallel;
/// Sets the discriminant function used for rule prefiltering.
/// When `Some(f)` is provided, `f` is called on each node to compute a unique `usize` id,
/// which is used to skip rules that do not apply to that node type.
/// When `None`, prefiltering is disabled and all rules are tried on every node.
pub fn set_discriminant_fn(mut self, discriminant_fn: Option<fn(&T) -> usize>) -> Self {
self.discriminant_fn = discriminant_fn;
/// Fixed-point application: after a rule fires, re-apply rules to the
/// transformed node until no more rules match, before continuing traversal.
pub fn set_fixedpoint(mut self, fixedpoint: bool) -> Self {
self.fixedpoint = fixedpoint;
/// Adds a predicate that controls whether the engine descends into a node's children.
pub fn add_down_predicate(mut self, predicate: fn(&T) -> bool) -> Self {
self.down_predicate = predicate;
impl<T, M, R> Default for EngineBuilder<T, M, R, NoCache>
fn default() -> Self {
Self::new()
impl<T, M, R, C> From<EngineBuilder<T, M, R, C>> for Engine<T, M, R, C>
fn from(val: EngineBuilder<T, M, R, C>) -> Self {
val.build()