1use serde::{Deserialize, Serialize};
4
5use crate::types::MemoryId;
6use crate::types::*;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub enum EdgeType {
11 Caused,
13 Before,
15 Related,
17 Contradicts,
19 Supports,
21 Supersedes,
23 Derived,
25 PartOf,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct MemoryEdge {
32 pub source: MemoryId,
34 pub target: MemoryId,
36 pub edge_type: EdgeType,
38 pub weight: f32,
40 pub created_at: Timestamp,
42 #[serde(default, skip_serializing_if = "Option::is_none")]
45 pub valid_from: Option<Timestamp>,
46 #[serde(default, skip_serializing_if = "Option::is_none")]
49 pub valid_until: Option<Timestamp>,
50 #[serde(default, skip_serializing_if = "Option::is_none")]
53 pub label: Option<String>,
54}
55
56impl MemoryEdge {
57 pub fn is_valid_at(&self, at: Timestamp) -> bool {
60 let from = self.valid_from.unwrap_or(0);
61 match self.valid_until {
62 Some(until) => at >= from && at < until,
63 None => at >= from,
64 }
65 }
66
67 pub fn invalidate(&mut self, at: Timestamp) {
69 self.valid_until = Some(at);
70 }
71
72 pub fn is_invalidated(&self) -> bool {
74 self.valid_until.is_some()
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81
82 fn make_edge() -> MemoryEdge {
83 MemoryEdge {
84 source: MemoryId::new(),
85 target: MemoryId::new(),
86 edge_type: EdgeType::Related,
87 weight: 0.8,
88 created_at: 1000,
89 valid_from: None,
90 valid_until: None,
91 label: None,
92 }
93 }
94
95 #[test]
96 fn unbounded_edge_always_valid() {
97 let edge = make_edge();
98 assert!(edge.is_valid_at(0));
99 assert!(edge.is_valid_at(u64::MAX));
100 assert!(!edge.is_invalidated());
101 }
102
103 #[test]
104 fn edge_with_valid_from() {
105 let mut edge = make_edge();
106 edge.valid_from = Some(5000);
107 assert!(!edge.is_valid_at(4999));
108 assert!(edge.is_valid_at(5000));
109 assert!(edge.is_valid_at(99999));
110 }
111
112 #[test]
113 fn edge_with_valid_until() {
114 let mut edge = make_edge();
115 edge.valid_until = Some(8000);
116 assert!(edge.is_valid_at(0));
117 assert!(edge.is_valid_at(7999));
118 assert!(!edge.is_valid_at(8000));
119 assert!(edge.is_invalidated());
120 }
121
122 #[test]
123 fn edge_with_both_bounds() {
124 let mut edge = make_edge();
125 edge.valid_from = Some(1000);
126 edge.valid_until = Some(5000);
127 assert!(!edge.is_valid_at(999));
128 assert!(edge.is_valid_at(1000));
129 assert!(edge.is_valid_at(3000));
130 assert!(!edge.is_valid_at(5000));
131 }
132
133 #[test]
134 fn invalidate_sets_valid_until() {
135 let mut edge = make_edge();
136 assert!(!edge.is_invalidated());
137 edge.invalidate(9000);
138 assert!(edge.is_invalidated());
139 assert_eq!(edge.valid_until, Some(9000));
140 assert!(edge.is_valid_at(8999));
141 assert!(!edge.is_valid_at(9000));
142 }
143
144 #[test]
145 fn serde_roundtrip_with_no_bounds() {
146 let edge = make_edge();
147 let json = serde_json::to_string(&edge).unwrap();
148 assert!(!json.contains("valid_from"));
149 assert!(!json.contains("valid_until"));
150 let deserialized: MemoryEdge = serde_json::from_str(&json).unwrap();
151 assert_eq!(deserialized.valid_from, None);
152 assert_eq!(deserialized.valid_until, None);
153 }
154
155 #[test]
156 fn serde_roundtrip_with_bounds() {
157 let mut edge = make_edge();
158 edge.valid_from = Some(1000);
159 edge.valid_until = Some(5000);
160 let json = serde_json::to_string(&edge).unwrap();
161 assert!(json.contains("valid_from"));
162 assert!(json.contains("valid_until"));
163 let deserialized: MemoryEdge = serde_json::from_str(&json).unwrap();
164 assert_eq!(deserialized.valid_from, Some(1000));
165 assert_eq!(deserialized.valid_until, Some(5000));
166 }
167
168 #[test]
169 fn deserialize_old_format_without_temporal_fields() {
170 let json = r#"{"source":"00000000-0000-0000-0000-000000000001","target":"00000000-0000-0000-0000-000000000002","edge_type":"Related","weight":0.8,"created_at":1000}"#;
172 let edge: MemoryEdge = serde_json::from_str(json).unwrap();
173 assert_eq!(edge.valid_from, None);
174 assert_eq!(edge.valid_until, None);
175 assert_eq!(edge.label, None);
176 assert!(edge.is_valid_at(5000));
177 }
178
179 #[test]
180 fn edge_with_label() {
181 let mut edge = make_edge();
182 edge.label = Some("owns".to_string());
183 let json = serde_json::to_string(&edge).unwrap();
184 assert!(json.contains("\"label\":\"owns\""));
185 let deserialized: MemoryEdge = serde_json::from_str(&json).unwrap();
186 assert_eq!(deserialized.label, Some("owns".to_string()));
187 }
188}