Actions

Software without side-effects. Redo and Undo. Macro's.

No unsafe blocks!

Beware, it is very beta right now. See it as a proof of concept in this stage.

Wanna help?

There are so many ways you can help: - Implement this library in practice and report what about the API and the documention is unclear or can be improved - If you feel this library misses a feature, please open an issue! - Fix bugs which will probably appear in the library :) - If you still not sure how to help, open an issue

Questions?

Open an issue!

The idea

Some structures describe the state of an application. Those structures should not be freely modifiable. Instead, they should be given Actions.

An action is a piece of data that describes how the state of the program should change.

An action can be applied to a datastructure. An action can also be merged with another action.

Merging actions

Imagine for example this scenario.

You have got a very simple program. A counter. The user can enter an operation (increment or decrement) an 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.

rust enum CounterAction { IncrementBy(i32), DecrementBy(i32) }

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.

rust let actions = vec![ CounterAction::IncrementBy(5), CounterAction::DecrementBy(4), CounterAction::IncrementBy(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:

```rust impl Merge for Action { fn merge(&self, previous: &CounterAction) -> MergeResult { match self { // If the current action is incrementing by val CounterAction::IncrementBy(val) => match previous { // If the previous action was decrementing by val2 CounterAction::DecrementBy(val2) => // Then return a new 'merged' action which // increments by (val - val2) MergeResult::Merged( CounterAction::IncrementBy(val - val2) ), // Etc.. CounterAction::IncrementBy(val2) => MergeResult::Merged( CounterAction::IncrementBy(val + val2) ), },

        CounterAction::DecrementBy(val) => match previous {
            CounterAction::IncrementBy(val2) =>
                MergeResult::Merged(
                    CounterAction::IncrementBy(val2 - val)
                ),

            CounterAction::DecrementBy(val2) =>
                MergeResult::Merged(
                    CounterAction::DecrementBy(val + val2)
                )
        },
    }
}

} ```

Please note that this merge is not the optimal solution., because it is possible to end up with either CounterAction::IncrementBy(0) or CounterAction::DecrementBy(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.