1
use std::collections::VecDeque;
2
use uniplate::Uniplate;
3

            
4
enum Command<T: Uniplate, M> {
5
    Transform(fn(T) -> T),
6
    MutMeta(fn(&mut M)),
7
}
8

            
9
/// A queue of commands (side-effects) to be applied after a successful rule application.
10
///
11
/// A rule is given a mutable reference to a [`Commands`] and can use it to register side-effects.
12
/// These side-effects are applied in order of registration **after** the rule itself updates
13
/// a part of the tree.
14
///
15
/// # Application
16
///
17
/// A rule may not be applied due to different reasons, for example:
18
/// - It does not return a new subtree (i.e. it returns `None`).
19
/// - It returns a new subtree but the resulting [`Update`](crate::update::Update) is not chosen
20
/// by the user-defined selector function. The function may select a different rule's update or
21
/// no update at all.
22
/// - It is part of a lower-priority rule group and a higher-priority rule is applied first.
23
///
24
/// In these cases, any side-effects which are registered by the rule are not applied and are
25
/// dropped by the engine.
26
///
27
/// # Example
28
/// ```rust
29
1
/// use tree_morph::prelude::*;
30
/// use uniplate::derive::Uniplate;
31
///
32
/// #[derive(Debug, Clone, PartialEq, Eq, Uniplate)]
33
/// #[uniplate()]
34
/// enum Expr {
35
///     A,
36
///     B,
37
///     C,
38
/// }
39
///
40
/// fn rule(cmds: &mut Commands<Expr, bool>, subtree: &Expr, meta: &bool) -> Option<Expr> {
41
2
///     cmds.transform(|t| match t { // A pure transformation (no other side-effects)
42
2
///         Expr::B => Expr::C,
43
1
///         _ => t,
44
///     });
45
2
///     cmds.mut_meta(|m| *m = true); // Set the metadata to 'true'
46
2
///
47
2
///     match subtree {
48
2
///         Expr::A => Some(Expr::B),
49
1
///         _ => None,
50
1
///     }
51
/// }
52
2
///
53
/// // Start with the expression 'A' and a metadata value of 'false'
54
/// let (result, meta) = morph(vec![rule_fns![rule]], select_first, Expr::A, false);
55
1
///
56
1
/// // After applying the rule itself, the commands are applied in order
57
1
/// assert_eq!(result, Expr::C);
58
1
/// assert_eq!(meta, true);
59
1
/// ```
60
1

            
61
pub struct Commands<T: Uniplate, M> {
62
    commands: VecDeque<Command<T, M>>,
63
}
64

            
65
impl<T: Uniplate, M> Commands<T, M> {
66
114
    pub(crate) fn new() -> Self {
67
114
        Self {
68
114
            commands: VecDeque::new(),
69
114
        }
70
114
    }
71

            
72
    /// Registers a pure transformation of the whole tree.
73
    ///
74
    /// In this case, "pure" means that the transformation cannot register additional side-effects.
75
    /// The transformation function is given ownership of the tree and should return the updated
76
    /// tree.
77
    ///
78
    /// Side-effects are applied in order of registration after the rule is applied.
79
2
    pub fn transform(&mut self, f: fn(T) -> T) {
80
2
        self.commands.push_back(Command::Transform(f));
81
2
    }
82

            
83
    /// Updates the global metadata in-place via a mutable reference.
84
    ///
85
    /// Side-effects are applied in order of registration after the rule is applied.
86
56
    pub fn mut_meta(&mut self, f: fn(&mut M)) {
87
56
        self.commands.push_back(Command::MutMeta(f));
88
56
    }
89

            
90
    /// Removes all side-effects previously registered by the rule.
91
    pub fn clear(&mut self) {
92
        self.commands.clear();
93
    }
94

            
95
    /// Consumes and apply the side-effects currently in the queue.
96
32
    pub(crate) fn apply(&mut self, mut tree: T, mut meta: M) -> (T, M) {
97
54
        while let Some(cmd) = self.commands.pop_front() {
98
22
            match cmd {
99
1
                Command::Transform(f) => tree = f(tree),
100
21
                Command::MutMeta(f) => f(&mut meta),
101
            }
102
        }
103
32
        (tree, meta)
104
32
    }
105
}