1use std::{
16 fmt::{Display, Formatter},
17 sync::Arc,
18};
19
20use serde::{Deserialize, Serialize};
21
22use crate::evaluation::variable_value::VariableValue;
23
24use super::{ElementPropertyMap, ElementValue};
25
26#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
27pub struct ElementReference {
28 pub source_id: Arc<str>,
29 pub element_id: Arc<str>,
30}
31
32impl ElementReference {
33 pub fn new(source_id: &str, element_id: &str) -> Self {
34 ElementReference {
35 source_id: Arc::from(source_id),
36 element_id: Arc::from(element_id),
37 }
38 }
39}
40
41impl Display for ElementReference {
42 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
43 write!(f, "{}:{}", self.source_id, self.element_id)
44 }
45}
46
47pub type ElementTimestamp = u64;
49
50pub const MAX_REASONABLE_MILLIS_TIMESTAMP: u64 = 10_000_000_000_000;
60
61pub fn validate_effective_from(value: ElementTimestamp) -> Result<(), String> {
83 if value == 0 {
84 return Ok(());
85 }
86 if value > MAX_REASONABLE_MILLIS_TIMESTAMP {
87 return Err(format!(
88 "effective_from value {} ({:.2e}) appears to be in nanoseconds or microseconds, \
89 not milliseconds. Expected a value < {} (~year 2286). \
90 Use timestamp_millis() instead of timestamp_nanos_opt().",
91 value, value as f64, MAX_REASONABLE_MILLIS_TIMESTAMP
92 ));
93 }
94 Ok(())
95}
96
97#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
98pub struct ElementMetadata {
99 pub reference: ElementReference,
100 pub labels: Arc<[Arc<str>]>,
101
102 pub effective_from: ElementTimestamp,
104}
105
106impl Display for ElementMetadata {
107 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
108 write!(
109 f,
110 "({}, [{}], {})",
111 self.reference,
112 self.labels.join(","),
113 self.effective_from
114 )
115 }
116}
117
118#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
119pub enum Element {
120 Node {
122 metadata: ElementMetadata,
123 properties: ElementPropertyMap,
124 },
125 Relation {
126 metadata: ElementMetadata,
127 in_node: ElementReference,
128 out_node: ElementReference,
129 properties: ElementPropertyMap,
130 },
131}
132
133impl Element {
134 pub fn get_reference(&self) -> &ElementReference {
135 match self {
136 Element::Node { metadata, .. } => &metadata.reference,
137 Element::Relation { metadata, .. } => &metadata.reference,
138 }
139 }
140
141 pub fn get_effective_from(&self) -> ElementTimestamp {
142 match self {
143 Element::Node { metadata, .. } => metadata.effective_from,
144 Element::Relation { metadata, .. } => metadata.effective_from,
145 }
146 }
147
148 pub fn get_metadata(&self) -> &ElementMetadata {
149 match self {
150 Element::Node { metadata, .. } => metadata,
151 Element::Relation { metadata, .. } => metadata,
152 }
153 }
154
155 pub fn get_property(&self, name: &str) -> &ElementValue {
156 let props = match self {
157 Element::Node { properties, .. } => properties,
158 Element::Relation { properties, .. } => properties,
159 };
160 &props[name]
161 }
162
163 pub fn get_properties(&self) -> &ElementPropertyMap {
164 match self {
165 Element::Node { properties, .. } => properties,
166 Element::Relation { properties, .. } => properties,
167 }
168 }
169
170 pub fn merge_missing_properties(&mut self, other: &Element) {
171 match (self, other) {
172 (
173 Element::Node {
174 properties,
175 metadata,
176 },
177 Element::Node {
178 properties: other_properties,
179 metadata: other_metadata,
180 },
181 ) => {
182 assert_eq!(metadata.reference, other_metadata.reference);
183 properties.merge(other_properties);
184 }
185 (
186 Element::Relation {
187 in_node: _,
188 out_node: _,
189 properties,
190 metadata,
191 },
192 Element::Relation {
193 in_node: _other_in_node,
194 out_node: _other_out_node,
195 properties: other_properties,
196 metadata: other_metadata,
197 },
198 ) => {
199 assert_eq!(metadata.reference, other_metadata.reference);
200 properties.merge(other_properties);
201 }
202 _ => panic!("Cannot merge different element types"),
203 }
204 }
205
206 pub fn to_expression_variable(&self) -> VariableValue {
207 VariableValue::Element(Arc::new(self.clone()))
208 }
209
210 pub fn update_effective_time(&mut self, timestamp: ElementTimestamp) {
211 match self {
212 Element::Node { metadata, .. } => metadata.effective_from = timestamp,
213 Element::Relation { metadata, .. } => metadata.effective_from = timestamp,
214 }
215 }
216}
217
218impl From<&Element> for serde_json::Value {
219 fn from(val: &Element) -> Self {
220 match val {
221 Element::Node {
222 metadata,
223 properties,
224 } => {
225 let mut properties: serde_json::Map<String, serde_json::Value> = properties.into();
226
227 properties.insert(
228 "$metadata".to_string(),
229 serde_json::Value::String(metadata.to_string()),
230 );
231
232 serde_json::Value::Object(properties)
233 }
234 Element::Relation {
235 metadata,
236 in_node,
237 out_node,
238 properties,
239 } => {
240 let mut properties: serde_json::Map<String, serde_json::Value> = properties.into();
241
242 properties.insert(
243 "$metadata".to_string(),
244 serde_json::Value::String(metadata.to_string()),
245 );
246
247 properties.insert(
248 "$in_node".to_string(),
249 serde_json::Value::String(in_node.to_string()),
250 );
251 properties.insert(
252 "$out_node".to_string(),
253 serde_json::Value::String(out_node.to_string()),
254 );
255
256 serde_json::Value::Object(properties)
257 }
258 }
259 }
260}
261
262#[cfg(test)]
263mod tests {
264 use super::*;
265
266 #[test]
267 fn validate_effective_from_accepts_zero() {
268 assert!(validate_effective_from(0).is_ok());
269 }
270
271 #[test]
272 fn validate_effective_from_accepts_valid_millis() {
273 assert!(validate_effective_from(1_771_000_000_000).is_ok());
275 assert!(validate_effective_from(946_684_800_000).is_ok());
277 assert!(validate_effective_from(4_102_444_800_000).is_ok());
279 }
280
281 #[test]
282 fn validate_effective_from_rejects_nanoseconds() {
283 let nanos = 1_771_000_000_000_000_000u64;
285 let result = validate_effective_from(nanos);
286 assert!(result.is_err());
287 assert!(result.unwrap_err().contains("nanoseconds"));
288 }
289
290 #[test]
291 fn validate_effective_from_rejects_microseconds() {
292 let micros = 1_771_000_000_000_000u64;
294 let result = validate_effective_from(micros);
295 assert!(result.is_err());
296 }
297
298 #[test]
299 fn validate_effective_from_accepts_boundary_value() {
300 assert!(validate_effective_from(MAX_REASONABLE_MILLIS_TIMESTAMP - 1).is_ok());
302 assert!(validate_effective_from(MAX_REASONABLE_MILLIS_TIMESTAMP).is_ok());
304 assert!(validate_effective_from(MAX_REASONABLE_MILLIS_TIMESTAMP + 1).is_err());
306 }
307}