1use std::collections::{BTreeMap, HashMap};
10
11use chrono::{DateTime, Utc};
12use ocel::{
13 AttrType, AttrValue, AttributeDefinition, Event, EventAttribute, EventType, Object,
14 ObjectAttribute, ObjectType, Ocel, Relationship, Violation,
15};
16
17#[derive(Debug, Clone)]
20pub struct StagingEvent {
21 pub id: String,
22 pub event_type: String,
23 pub time: DateTime<Utc>,
24 pub attributes: Vec<(String, AttrValue)>,
26 pub relations: Vec<(String, String)>,
28}
29
30#[derive(Debug, Clone, Default)]
31struct StagingObject {
32 object_type: String,
33 attributes: Vec<(String, AttrValue, DateTime<Utc>)>,
35 relations: Vec<(String, String)>,
37}
38
39#[derive(Debug, Default)]
42pub struct StagingLog {
43 events: Vec<StagingEvent>,
44 object_index: HashMap<String, usize>,
46 object_ids: Vec<String>,
47 objects: Vec<StagingObject>,
48 event_schema: BTreeMap<String, BTreeMap<String, AttrType>>,
51 object_schema: BTreeMap<String, BTreeMap<String, AttrType>>,
52}
53
54fn observe(schema: &mut BTreeMap<String, AttrType>, name: &str, value: &AttrValue) {
55 let observed = value.attr_type();
56 match schema.get(name) {
57 None => {
58 schema.insert(name.to_owned(), observed);
59 }
60 Some(&declared) if declared != observed => {
61 schema.insert(name.to_owned(), AttrType::String);
62 }
63 Some(_) => {}
64 }
65}
66
67fn conform(value: AttrValue, declared: AttrType) -> AttrValue {
69 if value.attr_type() == declared {
70 value
71 } else {
72 AttrValue::String(value.to_text())
73 }
74}
75
76impl StagingLog {
77 #[must_use]
78 pub fn new() -> Self {
79 Self::default()
80 }
81
82 #[must_use]
89 pub fn from_ocel(ocel: Ocel) -> Self {
90 let mut staging = Self::new();
91 for event_type in &ocel.event_types {
92 staging
93 .event_schema
94 .insert(event_type.name.clone(), seed(&event_type.attributes));
95 }
96 for object_type in &ocel.object_types {
97 staging
98 .object_schema
99 .insert(object_type.name.clone(), seed(&object_type.attributes));
100 }
101 for object in ocel.objects {
102 staging.upsert_object(&object.id, &object.object_type);
103 for attr in object.attributes {
104 staging.add_object_attribute(&object.id, &attr.name, attr.value, attr.time);
105 }
106 for rel in object.relationships {
107 staging.add_o2o(&object.id, &rel.object_id, &rel.qualifier);
108 }
109 }
110 for event in ocel.events {
111 staging.add_event(StagingEvent {
112 id: event.id,
113 event_type: event.event_type,
114 time: event.time,
115 attributes: event
116 .attributes
117 .into_iter()
118 .map(|a| (a.name, a.value))
119 .collect(),
120 relations: event
121 .relationships
122 .into_iter()
123 .map(|r| (r.object_id, r.qualifier))
124 .collect(),
125 });
126 }
127 staging
128 }
129
130 pub fn add_event(&mut self, event: StagingEvent) {
132 let schema = self
133 .event_schema
134 .entry(event.event_type.clone())
135 .or_default();
136 for (name, value) in &event.attributes {
137 observe(schema, name, value);
138 }
139 self.events.push(event);
140 }
141
142 pub fn upsert_object(&mut self, id: &str, object_type: &str) {
145 let index = self.object_slot(id);
146 if self.objects[index].object_type.is_empty() {
147 object_type.clone_into(&mut self.objects[index].object_type);
148 }
149 self.object_schema
150 .entry(object_type.to_owned())
151 .or_default();
152 }
153
154 pub fn add_object_attribute(
157 &mut self,
158 id: &str,
159 name: &str,
160 value: AttrValue,
161 time: DateTime<Utc>,
162 ) {
163 let index = self.object_slot(id);
164 let object_type = self.objects[index].object_type.clone();
165 if !object_type.is_empty() {
166 let schema = self.object_schema.entry(object_type).or_default();
167 observe(schema, name, &value);
168 }
169 self.objects[index]
170 .attributes
171 .push((name.to_owned(), value, time));
172 }
173
174 pub fn add_o2o(&mut self, source_id: &str, target_id: &str, qualifier: &str) {
176 let index = self.object_slot(source_id);
177 self.objects[index]
178 .relations
179 .push((target_id.to_owned(), qualifier.to_owned()));
180 }
181
182 pub fn map_object_ids(&mut self, f: impl Fn(&str) -> String) {
188 let old_ids = std::mem::take(&mut self.object_ids);
189 let old_objects = std::mem::take(&mut self.objects);
190 self.object_index.clear();
191 for (old_id, mut object) in old_ids.into_iter().zip(old_objects) {
192 for (target, _) in &mut object.relations {
193 *target = f(target);
194 }
195 let index = self.object_slot(&f(&old_id));
196 let slot = &mut self.objects[index];
197 if slot.object_type.is_empty() {
198 slot.object_type = object.object_type;
199 }
200 slot.attributes.append(&mut object.attributes);
201 slot.relations.append(&mut object.relations);
202 }
203 for event in &mut self.events {
204 for (target, _) in &mut event.relations {
205 *target = f(target);
206 }
207 }
208 }
209
210 pub fn map_events(&mut self, mut f: impl FnMut(&mut StagingEvent)) {
214 for event in &mut self.events {
215 f(event);
216 }
217 self.rebuild_event_schema();
218 }
219
220 pub fn retain_events(&mut self, mut pred: impl FnMut(&StagingEvent) -> bool) {
223 self.events.retain(|event| pred(event));
224 self.rebuild_event_schema();
225 }
226
227 fn rebuild_event_schema(&mut self) {
228 self.event_schema.clear();
229 for event in &self.events {
230 let schema = self
231 .event_schema
232 .entry(event.event_type.clone())
233 .or_default();
234 for (name, value) in &event.attributes {
235 observe(schema, name, value);
236 }
237 }
238 }
239
240 fn object_slot(&mut self, id: &str) -> usize {
241 if let Some(&index) = self.object_index.get(id) {
242 return index;
243 }
244 let index = self.objects.len();
245 self.object_index.insert(id.to_owned(), index);
246 self.object_ids.push(id.to_owned());
247 self.objects.push(StagingObject::default());
248 index
249 }
250
251 pub fn into_ocel(self) -> Result<Ocel, Vec<Violation>> {
264 let mut builder = Ocel::builder();
265
266 for (name, attrs) in &self.event_schema {
267 builder.add_event_type(EventType {
268 name: name.clone(),
269 attributes: attr_defs(attrs),
270 });
271 }
272 for (name, attrs) in &self.object_schema {
273 builder.add_object_type(ObjectType {
274 name: name.clone(),
275 attributes: attr_defs(attrs),
276 });
277 }
278
279 for event in self.events {
280 let schema = self.event_schema.get(&event.event_type);
281 let attributes = event
282 .attributes
283 .into_iter()
284 .map(|(name, value)| {
285 let declared = schema
286 .and_then(|s| s.get(&name).copied())
287 .unwrap_or_else(|| value.attr_type());
288 EventAttribute {
289 value: conform(value, declared),
290 name,
291 }
292 })
293 .collect();
294 builder.add_event(Event {
295 id: event.id,
296 event_type: event.event_type,
297 time: event.time,
298 attributes,
299 relationships: relationships(event.relations),
300 });
301 }
302
303 for (id, object) in self.object_ids.into_iter().zip(self.objects) {
304 let schema = self.object_schema.get(&object.object_type);
305 let mut attributes: Vec<ObjectAttribute> = object
306 .attributes
307 .into_iter()
308 .map(|(name, value, time)| {
309 let declared = schema
310 .and_then(|s| s.get(&name).copied())
311 .unwrap_or_else(|| value.attr_type());
312 ObjectAttribute {
313 value: conform(value, declared),
314 name,
315 time,
316 }
317 })
318 .collect();
319 let mut seen: std::collections::HashSet<(String, String, DateTime<Utc>)> =
325 std::collections::HashSet::new();
326 attributes.retain(|a| seen.insert((a.name.clone(), a.value.to_text(), a.time)));
327 attributes.sort_by(|a, b| a.time.cmp(&b.time).then_with(|| a.name.cmp(&b.name)));
328 let mut relations = object.relations;
329 relations.sort();
330 relations.dedup();
331 builder.add_object(Object {
332 id,
333 object_type: object.object_type,
334 attributes,
335 relationships: relationships(relations),
336 });
337 }
338
339 builder.build()
340 }
341}
342
343fn seed(attributes: &[AttributeDefinition]) -> BTreeMap<String, AttrType> {
344 attributes
345 .iter()
346 .map(|a| (a.name.clone(), a.value_type))
347 .collect()
348}
349
350fn attr_defs(attrs: &BTreeMap<String, AttrType>) -> Vec<AttributeDefinition> {
351 attrs
352 .iter()
353 .map(|(name, &value_type)| AttributeDefinition {
354 name: name.clone(),
355 value_type,
356 })
357 .collect()
358}
359
360fn relationships(pairs: Vec<(String, String)>) -> Vec<Relationship> {
361 pairs
362 .into_iter()
363 .map(|(object_id, qualifier)| Relationship {
364 object_id,
365 qualifier,
366 })
367 .collect()
368}