use std::fmt;
use uni_common::Properties;
use uni_common::Value;
use uni_common::core::id::{UniId, Vid};
#[derive(Debug, Clone, Default)]
pub struct ForkDiff {
pub vertices: VertexDiff,
pub edges: EdgeDiff,
}
impl ForkDiff {
pub fn is_empty(&self) -> bool {
self.vertices.is_empty() && self.edges.is_empty()
}
pub fn total_rows(&self) -> usize {
self.vertices.total_rows() + self.edges.total_rows()
}
pub fn invert(mut self) -> Self {
self.vertices = self.vertices.invert();
self.edges = self.edges.invert();
self
}
}
#[derive(Debug, Clone, Default)]
pub struct VertexDiff {
pub added: Vec<DiffVertex>,
pub deleted: Vec<DiffVertex>,
pub changed: Vec<VertexPropertyChange>,
}
impl VertexDiff {
pub fn is_empty(&self) -> bool {
self.added.is_empty() && self.deleted.is_empty() && self.changed.is_empty()
}
pub fn total_rows(&self) -> usize {
self.added.len() + self.deleted.len() + self.changed.len()
}
fn invert(self) -> Self {
Self {
added: self.deleted,
deleted: self.added,
changed: self
.changed
.into_iter()
.map(VertexPropertyChange::invert)
.collect(),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct EdgeDiff {
pub added: Vec<DiffEdge>,
pub deleted: Vec<DiffEdge>,
pub changed: Vec<EdgePropertyChange>,
}
impl EdgeDiff {
pub fn is_empty(&self) -> bool {
self.added.is_empty() && self.deleted.is_empty() && self.changed.is_empty()
}
pub fn total_rows(&self) -> usize {
self.added.len() + self.deleted.len() + self.changed.len()
}
fn invert(self) -> Self {
Self {
added: self.deleted,
deleted: self.added,
changed: self
.changed
.into_iter()
.map(EdgePropertyChange::invert)
.collect(),
}
}
}
#[derive(Debug, Clone)]
pub struct DiffVertex {
pub label: String,
pub uid: UniId,
pub vid: Option<Vid>,
pub properties: Properties,
}
#[derive(Debug, Clone)]
pub struct VertexPropertyChange {
pub label: String,
pub uid: UniId,
pub changes: Vec<PropertyChange>,
}
impl VertexPropertyChange {
fn invert(self) -> Self {
Self {
label: self.label,
uid: self.uid,
changes: self
.changes
.into_iter()
.map(PropertyChange::invert)
.collect(),
}
}
}
#[derive(Debug, Clone)]
pub struct DiffEdge {
pub edge_type: String,
pub edge_uid: UniId,
pub src_uid: UniId,
pub dst_uid: UniId,
pub properties: Properties,
}
#[derive(Debug, Clone)]
pub struct EdgePropertyChange {
pub edge_type: String,
pub src_uid: UniId,
pub dst_uid: UniId,
pub changes: Vec<PropertyChange>,
}
impl EdgePropertyChange {
fn invert(self) -> Self {
Self {
edge_type: self.edge_type,
src_uid: self.src_uid,
dst_uid: self.dst_uid,
changes: self
.changes
.into_iter()
.map(PropertyChange::invert)
.collect(),
}
}
}
#[derive(Debug, Clone)]
pub struct PropertyChange {
pub key: String,
pub before: Option<Value>,
pub after: Option<Value>,
}
impl PropertyChange {
fn invert(self) -> Self {
Self {
key: self.key,
before: self.after,
after: self.before,
}
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum PromotePattern {
Vertex {
label: String,
where_clause: Option<String>,
},
Edge {
edge_type: String,
where_clause: Option<String>,
},
}
impl PromotePattern {
pub fn label(label: impl Into<String>) -> Self {
Self::Vertex {
label: label.into(),
where_clause: None,
}
}
pub fn edge_type(edge_type: impl Into<String>) -> Self {
Self::Edge {
edge_type: edge_type.into(),
where_clause: None,
}
}
pub fn where_clause(mut self, expr: impl Into<String>) -> Self {
let expr = expr.into();
match &mut self {
Self::Vertex { where_clause, .. } | Self::Edge { where_clause, .. } => {
*where_clause = Some(expr)
}
}
self
}
pub fn label_name(&self) -> &str {
match self {
Self::Vertex { label, .. } => label,
Self::Edge { .. } => "",
}
}
pub fn edge_type_name(&self) -> &str {
match self {
Self::Edge { edge_type, .. } => edge_type,
Self::Vertex { .. } => "",
}
}
pub fn where_expr(&self) -> Option<&str> {
match self {
Self::Vertex { where_clause, .. } | Self::Edge { where_clause, .. } => {
where_clause.as_deref()
}
}
}
pub fn is_edge(&self) -> bool {
matches!(self, Self::Edge { .. })
}
}
impl fmt::Display for PromotePattern {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Vertex {
label,
where_clause: Some(w),
} => write!(f, "(:{} WHERE {})", label, w),
Self::Vertex {
label,
where_clause: None,
} => write!(f, "(:{})", label),
Self::Edge {
edge_type,
where_clause: Some(w),
} => write!(f, "[:{} WHERE {}]", edge_type, w),
Self::Edge {
edge_type,
where_clause: None,
} => write!(f, "[:{}]", edge_type),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct PromoteReport {
pub vertices_inserted: usize,
pub vertices_skipped_uid_conflict: usize,
pub edges_inserted: usize,
pub edges_skipped_duplicate: usize,
pub edges_skipped_no_endpoint: usize,
pub edges_skipped: usize,
pub per_pattern_inserted: Vec<usize>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn property_change_inverts_before_after() {
let pc = PropertyChange {
key: "age".into(),
before: Some(Value::Int(30)),
after: Some(Value::Int(31)),
};
let inv = pc.clone().invert();
assert_eq!(inv.before, pc.after);
assert_eq!(inv.after, pc.before);
}
#[test]
fn vertex_diff_invert_swaps_added_deleted() {
let v_a = DiffVertex {
label: "Person".into(),
uid: UniId::from_bytes([1; 32]),
vid: Some(Vid::new(1)),
properties: Default::default(),
};
let v_b = DiffVertex {
label: "Person".into(),
uid: UniId::from_bytes([2; 32]),
vid: Some(Vid::new(2)),
properties: Default::default(),
};
let d = VertexDiff {
added: vec![v_a.clone()],
deleted: vec![v_b.clone()],
changed: vec![],
};
let inv = d.invert();
assert_eq!(inv.added.len(), 1);
assert_eq!(inv.deleted.len(), 1);
}
#[test]
fn fork_diff_default_is_empty() {
let d = ForkDiff::default();
assert!(d.is_empty());
assert_eq!(d.total_rows(), 0);
}
#[test]
fn promote_pattern_display() {
let p = PromotePattern::label("Person");
assert_eq!(format!("{}", p), "(:Person)");
let p2 = PromotePattern::label("Person").where_clause("n.age > 30");
assert_eq!(format!("{}", p2), "(:Person WHERE n.age > 30)");
}
}