use std::collections::BTreeSet;
use std::sync::Arc;
use crate::access::ReadOnlyNetwork;
use crate::link_network::{LinkId, LinkNetwork};
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct StructuralDiff {
changed: BTreeSet<LinkId>,
added: BTreeSet<LinkId>,
removed: BTreeSet<LinkId>,
}
impl StructuralDiff {
#[must_use]
pub const fn new(
changed: BTreeSet<LinkId>,
added: BTreeSet<LinkId>,
removed: BTreeSet<LinkId>,
) -> Self {
Self {
changed,
added,
removed,
}
}
#[must_use]
pub const fn changed(&self) -> &BTreeSet<LinkId> {
&self.changed
}
#[must_use]
pub const fn added(&self) -> &BTreeSet<LinkId> {
&self.added
}
#[must_use]
pub const fn removed(&self) -> &BTreeSet<LinkId> {
&self.removed
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.changed.is_empty() && self.added.is_empty() && self.removed.is_empty()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NetworkSnapshot {
version: u64,
parent_version: Option<u64>,
provenance: String,
network: Arc<LinkNetwork>,
}
impl NetworkSnapshot {
#[must_use]
pub fn new(version: u64, network: LinkNetwork, provenance: impl Into<String>) -> Self {
Self {
version,
parent_version: None,
provenance: provenance.into(),
network: Arc::new(network),
}
}
#[must_use]
pub const fn version(&self) -> u64 {
self.version
}
#[must_use]
pub const fn parent_version(&self) -> Option<u64> {
self.parent_version
}
#[must_use]
pub fn provenance(&self) -> &str {
&self.provenance
}
#[must_use]
pub fn network(&self) -> &LinkNetwork {
self.network.as_ref()
}
#[must_use]
pub fn shared_snapshot_count(&self) -> usize {
Arc::strong_count(&self.network)
}
#[must_use]
pub fn from_read_only(
version: u64,
view: &ReadOnlyNetwork,
provenance: impl Into<String>,
) -> Self {
Self {
version,
parent_version: None,
provenance: provenance.into(),
network: view.shared().clone(),
}
}
#[must_use]
pub fn as_read_only(&self) -> ReadOnlyNetwork {
ReadOnlyNetwork::from_shared(self.network.clone())
}
#[must_use]
pub fn structural_diff(&self, other: &Self) -> StructuralDiff {
self.network().structural_diff(other.network())
}
#[must_use]
pub fn to_mutable(&self, provenance: impl Into<String>) -> MutableNetworkSnapshot {
MutableNetworkSnapshot {
base_version: self.version,
network: self.network().clone(),
provenance: provenance.into(),
}
}
fn committed(
version: u64,
parent_version: u64,
network: LinkNetwork,
provenance: String,
) -> Self {
Self {
version,
parent_version: Some(parent_version),
provenance,
network: Arc::new(network),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MutableNetworkSnapshot {
base_version: u64,
network: LinkNetwork,
provenance: String,
}
impl MutableNetworkSnapshot {
#[must_use]
pub const fn base_version(&self) -> u64 {
self.base_version
}
#[must_use]
pub fn provenance(&self) -> &str {
&self.provenance
}
#[must_use]
pub const fn network(&self) -> &LinkNetwork {
&self.network
}
pub fn network_mut(&mut self) -> &mut LinkNetwork {
&mut self.network
}
#[must_use]
pub fn commit(self) -> NetworkSnapshot {
let next_version = self
.base_version
.checked_add(1)
.expect("snapshot version overflow");
self.commit_as(next_version)
}
#[must_use]
pub fn commit_as(self, version: u64) -> NetworkSnapshot {
assert!(
version > self.base_version,
"snapshot version must move forward"
);
NetworkSnapshot::committed(version, self.base_version, self.network, self.provenance)
}
}
impl LinkNetwork {
#[must_use]
pub fn snapshot(&self, version: u64, provenance: impl Into<String>) -> NetworkSnapshot {
NetworkSnapshot::new(version, self.clone(), provenance)
}
#[must_use]
pub fn structural_diff(&self, other: &Self) -> StructuralDiff {
let old_ids = self
.links()
.map(crate::link_network::Link::id)
.collect::<BTreeSet<_>>();
let new_ids = other
.links()
.map(crate::link_network::Link::id)
.collect::<BTreeSet<_>>();
let removed = old_ids.difference(&new_ids).copied().collect();
let added = new_ids.difference(&old_ids).copied().collect();
let changed = old_ids
.intersection(&new_ids)
.copied()
.filter(|id| self.link(*id) != other.link(*id))
.collect();
StructuralDiff::new(changed, added, removed)
}
}