1use crate::{Event, EventId, ForkId, Graph, StateError, replay};
2use serde::{Deserialize, Serialize};
3use std::collections::BTreeSet;
4
5#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
6pub struct ForkSnapshot {
7 pub id: ForkId,
8 pub parent_event: Option<EventId>,
9 pub events: Vec<Event>,
10 pub graph: Graph,
11}
12
13#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14pub struct GraphDiff {
15 pub added_nodes: Vec<String>,
16 pub removed_nodes: Vec<String>,
17 pub changed_nodes: Vec<String>,
18 pub added_relations: Vec<String>,
19 pub removed_relations: Vec<String>,
20}
21
22pub fn fork_events_at(
23 events: &[Event],
24 id: ForkId,
25 parent_event: Option<EventId>,
26) -> Result<ForkSnapshot, StateError> {
27 let cutoff = match &parent_event {
28 Some(parent_event) => {
29 events
30 .iter()
31 .position(|event| &event.id == parent_event)
32 .ok_or_else(|| StateError::EventNotFound(parent_event.clone()))?
33 + 1
34 }
35 None => events.len(),
36 };
37 let forked_events = events.iter().take(cutoff).cloned().collect::<Vec<_>>();
38 let graph = replay(&forked_events)?;
39 Ok(ForkSnapshot {
40 id,
41 parent_event,
42 events: forked_events,
43 graph,
44 })
45}
46
47pub fn diff_graphs(left: &Graph, right: &Graph) -> GraphDiff {
48 let left_nodes = left
49 .nodes
50 .keys()
51 .map(|id| id.0.clone())
52 .collect::<BTreeSet<_>>();
53 let right_nodes = right
54 .nodes
55 .keys()
56 .map(|id| id.0.clone())
57 .collect::<BTreeSet<_>>();
58
59 let changed_nodes = left
60 .nodes
61 .iter()
62 .filter_map(|(id, node)| {
63 right
64 .nodes
65 .get(id)
66 .filter(|right_node| *right_node != node)
67 .map(|_| id.0.clone())
68 })
69 .collect();
70
71 let left_relations = left
72 .relations
73 .iter()
74 .map(|rel| format!("{}:{}:{}", rel.from, rel.rel, rel.to))
75 .collect::<BTreeSet<_>>();
76 let right_relations = right
77 .relations
78 .iter()
79 .map(|rel| format!("{}:{}:{}", rel.from, rel.rel, rel.to))
80 .collect::<BTreeSet<_>>();
81
82 GraphDiff {
83 added_nodes: right_nodes.difference(&left_nodes).cloned().collect(),
84 removed_nodes: left_nodes.difference(&right_nodes).cloned().collect(),
85 changed_nodes,
86 added_relations: right_relations
87 .difference(&left_relations)
88 .cloned()
89 .collect(),
90 removed_relations: left_relations
91 .difference(&right_relations)
92 .cloned()
93 .collect(),
94 }
95}