Chapter 3 - Merging actions

Imagine this scenario.

You have got a very simple program. A counter. The user can enter an operation (increment or decrement) and a value. The user could, for example say: increment the counter by 5. Then the user decrements the counter by 4. Lastly, the user performs an increment of 3.

We can use an enum to represent those actions.


# #![allow(unused_variables)]
#fn main() {
enum MyAction {
    Add(u8),
    Subtract(u8)
}
#}

But the user is not satisfied. The user wants to automate this time-consuming process. Simple!

We just record the actions and store them. If the user wants to perform all 3 actions, the user can just press one button to perform all 3 actions. They will be executed sequentially.


# #![allow(unused_variables)]
#fn main() {
let actions = vec![
    MyAction::Add(5),
    MyAction::Subtract(4),
    MyAction::Add(3)
];
#}

"That is dumb, it could just be 1 single action", you say? Yes, it is dumb. That is why actions enables you to merge actions by creating a Chain of actions. You provide the merge-logic. This is how that would look like for our example:


# #![allow(unused_variables)]
#fn main() {
impl Merge for Action {
    fn merge(&self, previous: &MyAction) -> MergeResult<Self> {
        match self {
            // If the current action is incrementing by val
            MyAction::Add(val) => match previous {
                // If the previous action was decrementing by val2
                MyAction::Subtract(val2) => 
                    // Then return a new 'merged' action which
                    // increments by (val - val2)
                    MergeResult::Merged(
                        MyAction::Add(val - val2)
                    ),
                // Etc..
                MyAction::IncrementBy(val2) =>
                    MergeResult::Merged(
                        MyAction::Add(val + val2)
                    ),
            },

            MyAction::DecrementBy(val) => match previous {
                MyAction::IncrementBy(val2) =>
                    MergeResult::Merged(
                        MyAction::Add(val2 - val)
                    ),
                
                MyAction::DecrementBy(val2) =>
                    MergeResult::Merged(
                        MyAction::Subtract(val + val2)
                    )
            },
        }
    }
}
#}

Please note that this merge is not the optimal solution, because it is possible to end up with either CounterAction::Add(0) or CounterAction::Subtract(0), which could be entirely removed.

To solve that issue, you can return other MergeResults. You can return MergeResult::CancelsOut, for example. You can read more about the different MergeResults in the documentation.