use serde::{Deserialize, Serialize};
use crate::memory::{ConstraintRequest, IndexRequest};
use crate::{NodeId, Properties, PropertyValue, RelationshipId};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum MutationEvent {
CreateNode {
id: NodeId,
labels: Vec<String>,
properties: Properties,
},
CreateRelationship {
id: RelationshipId,
src: NodeId,
dst: NodeId,
rel_type: String,
properties: Properties,
},
SetNodeProperty {
node_id: NodeId,
key: String,
value: PropertyValue,
},
RemoveNodeProperty {
node_id: NodeId,
key: String,
},
AddNodeLabel {
node_id: NodeId,
label: String,
},
RemoveNodeLabel {
node_id: NodeId,
label: String,
},
SetRelationshipProperty {
rel_id: RelationshipId,
key: String,
value: PropertyValue,
},
RemoveRelationshipProperty {
rel_id: RelationshipId,
key: String,
},
DeleteRelationship {
rel_id: RelationshipId,
},
DeleteNode {
node_id: NodeId,
},
DetachDeleteNode {
node_id: NodeId,
},
Clear,
CreateIndex {
request: IndexRequest,
if_not_exists: bool,
},
DropIndex {
name: String,
if_exists: bool,
},
CreateConstraint {
request: ConstraintRequest,
if_not_exists: bool,
},
DropConstraint {
name: String,
if_exists: bool,
},
}
pub trait MutationRecorder: Send + Sync + 'static {
fn record(&self, event: MutationEvent);
fn poisoned(&self) -> Option<String> {
None
}
}
pub struct ClosureRecorder<F>(pub F)
where
F: Fn(MutationEvent) + Send + Sync + 'static;
impl<F> MutationRecorder for ClosureRecorder<F>
where
F: Fn(MutationEvent) + Send + Sync + 'static,
{
fn record(&self, event: MutationEvent) {
(self.0)(event)
}
}
#[derive(Debug, Default, Clone)]
pub struct MutationWriteSet {
pub nodes: std::collections::BTreeSet<NodeId>,
pub rels: std::collections::BTreeSet<RelationshipId>,
pub cleared: bool,
}
impl MutationWriteSet {
pub fn new() -> Self {
Self::default()
}
pub fn extend_from_events<'a>(&mut self, events: impl IntoIterator<Item = &'a MutationEvent>) {
for event in events {
match event {
MutationEvent::CreateNode { id, .. } => {
self.nodes.insert(*id);
}
MutationEvent::CreateRelationship { id, src, dst, .. } => {
self.rels.insert(*id);
self.nodes.insert(*src);
self.nodes.insert(*dst);
}
MutationEvent::SetNodeProperty { node_id, .. }
| MutationEvent::RemoveNodeProperty { node_id, .. }
| MutationEvent::AddNodeLabel { node_id, .. }
| MutationEvent::RemoveNodeLabel { node_id, .. } => {
self.nodes.insert(*node_id);
}
MutationEvent::SetRelationshipProperty { rel_id, .. }
| MutationEvent::RemoveRelationshipProperty { rel_id, .. } => {
self.rels.insert(*rel_id);
}
MutationEvent::DeleteRelationship { rel_id } => {
self.rels.insert(*rel_id);
}
MutationEvent::DeleteNode { node_id } => {
self.nodes.insert(*node_id);
}
MutationEvent::DetachDeleteNode { node_id } => {
self.nodes.insert(*node_id);
}
MutationEvent::Clear => {
self.cleared = true;
}
MutationEvent::CreateIndex { .. }
| MutationEvent::DropIndex { .. }
| MutationEvent::CreateConstraint { .. }
| MutationEvent::DropConstraint { .. } => {
}
}
}
}
pub fn is_empty(&self) -> bool {
!self.cleared && self.nodes.is_empty() && self.rels.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn write_set_extracts_ids_from_events() {
let events = [
MutationEvent::CreateNode {
id: 1,
labels: vec!["A".into()],
properties: Default::default(),
},
MutationEvent::CreateRelationship {
id: 10,
src: 1,
dst: 2,
rel_type: "R".into(),
properties: Default::default(),
},
MutationEvent::SetNodeProperty {
node_id: 3,
key: "x".into(),
value: PropertyValue::Int(5),
},
MutationEvent::DeleteRelationship { rel_id: 11 },
];
let mut ws = MutationWriteSet::new();
ws.extend_from_events(events.iter());
assert_eq!(ws.nodes.iter().copied().collect::<Vec<_>>(), vec![1, 2, 3]);
assert_eq!(ws.rels.iter().copied().collect::<Vec<_>>(), vec![10, 11]);
assert!(!ws.cleared);
}
#[test]
fn write_set_clear_event_is_sticky() {
let mut ws = MutationWriteSet::new();
ws.extend_from_events([&MutationEvent::Clear]);
assert!(ws.cleared);
assert!(!ws.is_empty()); }
}