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}
51
52impl MemoryEdge {
53 pub fn is_valid_at(&self, at: Timestamp) -> bool {
56 let from = self.valid_from.unwrap_or(0);
57 match self.valid_until {
58 Some(until) => at >= from && at < until,
59 None => at >= from,
60 }
61 }
62
63 pub fn invalidate(&mut self, at: Timestamp) {
65 self.valid_until = Some(at);
66 }
67
68 pub fn is_invalidated(&self) -> bool {
70 self.valid_until.is_some()
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 fn make_edge() -> MemoryEdge {
79 MemoryEdge {
80 source: MemoryId::new(),
81 target: MemoryId::new(),
82 edge_type: EdgeType::Related,
83 weight: 0.8,
84 created_at: 1000,
85 valid_from: None,
86 valid_until: None,
87 }
88 }
89
90 #[test]
91 fn unbounded_edge_always_valid() {
92 let edge = make_edge();
93 assert!(edge.is_valid_at(0));
94 assert!(edge.is_valid_at(u64::MAX));
95 assert!(!edge.is_invalidated());
96 }
97
98 #[test]
99 fn edge_with_valid_from() {
100 let mut edge = make_edge();
101 edge.valid_from = Some(5000);
102 assert!(!edge.is_valid_at(4999));
103 assert!(edge.is_valid_at(5000));
104 assert!(edge.is_valid_at(99999));
105 }
106
107 #[test]
108 fn edge_with_valid_until() {
109 let mut edge = make_edge();
110 edge.valid_until = Some(8000);
111 assert!(edge.is_valid_at(0));
112 assert!(edge.is_valid_at(7999));
113 assert!(!edge.is_valid_at(8000));
114 assert!(edge.is_invalidated());
115 }
116
117 #[test]
118 fn edge_with_both_bounds() {
119 let mut edge = make_edge();
120 edge.valid_from = Some(1000);
121 edge.valid_until = Some(5000);
122 assert!(!edge.is_valid_at(999));
123 assert!(edge.is_valid_at(1000));
124 assert!(edge.is_valid_at(3000));
125 assert!(!edge.is_valid_at(5000));
126 }
127
128 #[test]
129 fn invalidate_sets_valid_until() {
130 let mut edge = make_edge();
131 assert!(!edge.is_invalidated());
132 edge.invalidate(9000);
133 assert!(edge.is_invalidated());
134 assert_eq!(edge.valid_until, Some(9000));
135 assert!(edge.is_valid_at(8999));
136 assert!(!edge.is_valid_at(9000));
137 }
138
139 #[test]
140 fn serde_roundtrip_with_no_bounds() {
141 let edge = make_edge();
142 let json = serde_json::to_string(&edge).unwrap();
143 assert!(!json.contains("valid_from"));
144 assert!(!json.contains("valid_until"));
145 let deserialized: MemoryEdge = serde_json::from_str(&json).unwrap();
146 assert_eq!(deserialized.valid_from, None);
147 assert_eq!(deserialized.valid_until, None);
148 }
149
150 #[test]
151 fn serde_roundtrip_with_bounds() {
152 let mut edge = make_edge();
153 edge.valid_from = Some(1000);
154 edge.valid_until = Some(5000);
155 let json = serde_json::to_string(&edge).unwrap();
156 assert!(json.contains("valid_from"));
157 assert!(json.contains("valid_until"));
158 let deserialized: MemoryEdge = serde_json::from_str(&json).unwrap();
159 assert_eq!(deserialized.valid_from, Some(1000));
160 assert_eq!(deserialized.valid_until, Some(5000));
161 }
162
163 #[test]
164 fn deserialize_old_format_without_temporal_fields() {
165 let json = r#"{"source":"00000000-0000-0000-0000-000000000001","target":"00000000-0000-0000-0000-000000000002","edge_type":"Related","weight":0.8,"created_at":1000}"#;
167 let edge: MemoryEdge = serde_json::from_str(json).unwrap();
168 assert_eq!(edge.valid_from, None);
169 assert_eq!(edge.valid_until, None);
170 assert!(edge.is_valid_at(5000));
171 }
172}