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

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

            
12
/// A queue of commands (side effects) to be applied after every successful rule application.
13
pub struct Commands<T, M>
14
where
15
    T: Uniplate,
16
{
17
    commands: VecDeque<Command<T, M>>,
18
}
19

            
20
impl<T, M> Commands<T, M>
21
where
22
    T: Uniplate,
23
{
24
222
    pub fn new() -> Self {
25
222
        Self {
26
222
            commands: VecDeque::new(),
27
222
        }
28
222
    }
29

            
30
    /// Apply the given transformation to the root node.
31
    /// Commands are applied in order after the rule is applied.
32
    pub fn transform(&mut self, f: fn(&T) -> T) {
33
        self.commands.push_back(Command::Transform(f));
34
    }
35

            
36
    /// Update the associated metadata.
37
    /// Commands are applied in order after the rule is applied.
38
13
    pub fn mut_meta(&mut self, f: fn(&mut M)) {
39
13
        self.commands.push_back(Command::MutMeta(f));
40
13
    }
41

            
42
    /// Remove all commands in the queue.
43
    pub fn clear(&mut self) {
44
        self.commands.clear();
45
    }
46

            
47
    /// Consume and apply the commands currently in the queue.
48
22
    pub(crate) fn apply(&mut self, mut tree: T, mut meta: M) -> (T, M) {
49
35
        while let Some(cmd) = self.commands.pop_front() {
50
13
            match cmd {
51
                Command::Transform(f) => tree = f(&tree),
52
13
                Command::MutMeta(f) => f(&mut meta),
53
            }
54
        }
55
22
        (tree, meta)
56
22
    }
57
}