1use crate::types::Value;
2use serde::{Deserialize, Serialize};
3use std::collections::{BTreeMap, BTreeSet};
4
5#[derive(Debug, Default, Clone, Serialize, Deserialize)]
12pub struct EdgeProps {
13 map: BTreeMap<(u32, u32, u32), BTreeMap<String, Value>>,
14 #[serde(skip)]
20 tombstones: BTreeSet<(u32, u32, u32)>,
21}
22
23impl EdgeProps {
24 pub fn new() -> Self {
25 Self::default()
26 }
27
28 pub fn set(&mut self, etype: u32, src: u32, dst: u32, field: &str, value: Value) {
29 self.tombstones.remove(&(etype, src, dst));
31 self.map
32 .entry((etype, src, dst))
33 .or_default()
34 .insert(field.to_owned(), value);
35 }
36
37 pub fn get(&self, etype: u32, src: u32, dst: u32, field: &str) -> Option<&Value> {
38 self.map.get(&(etype, src, dst))?.get(field)
39 }
40
41 pub fn remove_edge(&mut self, etype: u32, src: u32, dst: u32) {
44 self.map.remove(&(etype, src, dst));
45 self.tombstones.insert((etype, src, dst));
46 }
47
48 pub fn is_tombstoned(&self, etype: u32, src: u32, dst: u32) -> bool {
50 self.tombstones.contains(&(etype, src, dst))
51 }
52
53 pub fn is_clean(&self) -> bool {
57 self.map.is_empty() && self.tombstones.is_empty()
58 }
59
60 pub fn sorted_entries(
63 &self,
64 ) -> Vec<(
65 u32,
66 u32,
67 u32,
68 &std::collections::BTreeMap<String, crate::types::Value>,
69 )> {
70 self.map
71 .iter()
72 .map(|((et, s, d), props)| (*et, *s, *d, props))
73 .collect()
74 }
75
76 pub fn tombstoned_keys(&self) -> impl Iterator<Item = (u32, u32, u32)> + '_ {
78 self.tombstones.iter().copied()
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85
86 #[test]
87 fn set_get_remove_edge_props() {
88 let mut e = EdgeProps::new();
89 e.set(0, 1, 2, "score", Value::Float(0.5));
90 e.set(0, 1, 2, "score", Value::Float(0.7)); assert_eq!(e.get(0, 1, 2, "score"), Some(&Value::Float(0.7)));
92 assert_eq!(e.get(0, 2, 1, "score"), None); e.remove_edge(0, 1, 2);
94 assert_eq!(e.get(0, 1, 2, "score"), None);
95 }
96
97 #[test]
98 fn tombstone_masks_base_check() {
99 let mut e = EdgeProps::new();
100 assert!(e.is_clean());
102 e.set(0, 1, 2, "score", Value::Float(1.0));
104 assert!(!e.is_clean());
105 e.remove_edge(0, 1, 2);
107 assert!(!e.is_clean());
108 assert!(e.is_tombstoned(0, 1, 2));
109 assert!(!e.is_tombstoned(0, 2, 1));
110 }
111
112 #[test]
113 fn set_clears_tombstone() {
114 let mut e = EdgeProps::new();
115 e.set(0, 1, 2, "score", Value::Float(1.0));
116 e.remove_edge(0, 1, 2);
117 assert!(e.is_tombstoned(0, 1, 2));
118 e.set(0, 1, 2, "score", Value::Float(2.0));
120 assert!(!e.is_tombstoned(0, 1, 2));
121 assert_eq!(e.get(0, 1, 2, "score"), Some(&Value::Float(2.0)));
122 }
123}