meta_language/
snapshots.rs1use std::collections::BTreeSet;
2use std::sync::Arc;
3
4use crate::access::ReadOnlyNetwork;
5use crate::link_network::{LinkId, LinkNetwork};
6
7#[derive(Clone, Debug, Default, PartialEq, Eq)]
9pub struct StructuralDiff {
10 changed: BTreeSet<LinkId>,
11 added: BTreeSet<LinkId>,
12 removed: BTreeSet<LinkId>,
13}
14
15impl StructuralDiff {
16 #[must_use]
18 pub const fn new(
19 changed: BTreeSet<LinkId>,
20 added: BTreeSet<LinkId>,
21 removed: BTreeSet<LinkId>,
22 ) -> Self {
23 Self {
24 changed,
25 added,
26 removed,
27 }
28 }
29
30 #[must_use]
32 pub const fn changed(&self) -> &BTreeSet<LinkId> {
33 &self.changed
34 }
35
36 #[must_use]
38 pub const fn added(&self) -> &BTreeSet<LinkId> {
39 &self.added
40 }
41
42 #[must_use]
44 pub const fn removed(&self) -> &BTreeSet<LinkId> {
45 &self.removed
46 }
47
48 #[must_use]
50 pub fn is_empty(&self) -> bool {
51 self.changed.is_empty() && self.added.is_empty() && self.removed.is_empty()
52 }
53}
54
55#[derive(Clone, Debug, PartialEq, Eq)]
57pub struct NetworkSnapshot {
58 version: u64,
59 parent_version: Option<u64>,
60 provenance: String,
61 network: Arc<LinkNetwork>,
62}
63
64impl NetworkSnapshot {
65 #[must_use]
67 pub fn new(version: u64, network: LinkNetwork, provenance: impl Into<String>) -> Self {
68 Self {
69 version,
70 parent_version: None,
71 provenance: provenance.into(),
72 network: Arc::new(network),
73 }
74 }
75
76 #[must_use]
78 pub const fn version(&self) -> u64 {
79 self.version
80 }
81
82 #[must_use]
84 pub const fn parent_version(&self) -> Option<u64> {
85 self.parent_version
86 }
87
88 #[must_use]
90 pub fn provenance(&self) -> &str {
91 &self.provenance
92 }
93
94 #[must_use]
96 pub fn network(&self) -> &LinkNetwork {
97 self.network.as_ref()
98 }
99
100 #[must_use]
102 pub fn shared_snapshot_count(&self) -> usize {
103 Arc::strong_count(&self.network)
104 }
105
106 #[must_use]
111 pub fn from_read_only(
112 version: u64,
113 view: &ReadOnlyNetwork,
114 provenance: impl Into<String>,
115 ) -> Self {
116 Self {
117 version,
118 parent_version: None,
119 provenance: provenance.into(),
120 network: view.shared().clone(),
121 }
122 }
123
124 #[must_use]
126 pub fn as_read_only(&self) -> ReadOnlyNetwork {
127 ReadOnlyNetwork::from_shared(self.network.clone())
128 }
129
130 #[must_use]
132 pub fn structural_diff(&self, other: &Self) -> StructuralDiff {
133 self.network().structural_diff(other.network())
134 }
135
136 #[must_use]
138 pub fn to_mutable(&self, provenance: impl Into<String>) -> MutableNetworkSnapshot {
139 MutableNetworkSnapshot {
140 base_version: self.version,
141 network: self.network().clone(),
142 provenance: provenance.into(),
143 }
144 }
145
146 fn committed(
147 version: u64,
148 parent_version: u64,
149 network: LinkNetwork,
150 provenance: String,
151 ) -> Self {
152 Self {
153 version,
154 parent_version: Some(parent_version),
155 provenance,
156 network: Arc::new(network),
157 }
158 }
159}
160
161#[derive(Clone, Debug, PartialEq, Eq)]
163pub struct MutableNetworkSnapshot {
164 base_version: u64,
165 network: LinkNetwork,
166 provenance: String,
167}
168
169impl MutableNetworkSnapshot {
170 #[must_use]
172 pub const fn base_version(&self) -> u64 {
173 self.base_version
174 }
175
176 #[must_use]
178 pub fn provenance(&self) -> &str {
179 &self.provenance
180 }
181
182 #[must_use]
184 pub const fn network(&self) -> &LinkNetwork {
185 &self.network
186 }
187
188 pub fn network_mut(&mut self) -> &mut LinkNetwork {
190 &mut self.network
191 }
192
193 #[must_use]
195 pub fn commit(self) -> NetworkSnapshot {
196 let next_version = self
197 .base_version
198 .checked_add(1)
199 .expect("snapshot version overflow");
200 self.commit_as(next_version)
201 }
202
203 #[must_use]
205 pub fn commit_as(self, version: u64) -> NetworkSnapshot {
206 assert!(
207 version > self.base_version,
208 "snapshot version must move forward"
209 );
210 NetworkSnapshot::committed(version, self.base_version, self.network, self.provenance)
211 }
212}
213
214impl LinkNetwork {
215 #[must_use]
217 pub fn snapshot(&self, version: u64, provenance: impl Into<String>) -> NetworkSnapshot {
218 NetworkSnapshot::new(version, self.clone(), provenance)
219 }
220
221 #[must_use]
223 pub fn structural_diff(&self, other: &Self) -> StructuralDiff {
224 let old_ids = self
225 .links()
226 .map(crate::link_network::Link::id)
227 .collect::<BTreeSet<_>>();
228 let new_ids = other
229 .links()
230 .map(crate::link_network::Link::id)
231 .collect::<BTreeSet<_>>();
232
233 let removed = old_ids.difference(&new_ids).copied().collect();
234 let added = new_ids.difference(&old_ids).copied().collect();
235 let changed = old_ids
236 .intersection(&new_ids)
237 .copied()
238 .filter(|id| self.link(*id) != other.link(*id))
239 .collect();
240
241 StructuralDiff::new(changed, added, removed)
242 }
243}