link_cli/changes_simplifier.rs
1//! Simplifies the raw changes of one query to the net change of every link.
2//!
3//! Corresponds to `ChangesSimplifier.cs` in C#.
4
5use crate::link::Link;
6use std::collections::HashMap;
7
8/// Reduces the raw `(before, after)` steps a store reports to one change per
9/// link address: from its state before the query to its state after it.
10///
11/// The store reports every step it takes — a link is emptied to `(i: 0 0)`
12/// before it is deleted and created as `(i: 0 0)` before it is set — and the
13/// null link `(0: 0 0)` stands for "no link", before a creation and after a
14/// deletion. The steps of one address form a chain, so its net change is the
15/// `before` of its first step and the `after` of its last:
16///
17/// - a link created and deleted again within the query is not reported;
18/// - a link that ends where it started is reported unchanged, like a link the
19/// query only matched.
20///
21/// The changes are ordered by their `after` state, so deletions come first;
22/// changes with equal `after` states keep the order of their first step.
23pub fn simplify_changes(changes: Vec<(Link, Link)>) -> Vec<(Link, Link)> {
24 let mut net_changes: Vec<(Link, Link)> = Vec::new();
25 let mut position_of_address: HashMap<u32, usize> = HashMap::new();
26 for (before, after) in changes {
27 let address = if before.index != 0 {
28 before.index
29 } else {
30 after.index
31 };
32 if address == 0 {
33 continue;
34 }
35 match position_of_address.get(&address) {
36 Some(&position) => net_changes[position].1 = after,
37 None => {
38 position_of_address.insert(address, net_changes.len());
39 net_changes.push((before, after));
40 }
41 }
42 }
43 net_changes.retain(|(before, after)| !(before.is_null() && after.is_null()));
44 net_changes.sort_by_key(|(_, after)| (after.index, after.source, after.target));
45 net_changes
46}