1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
use std::collections::HashMap;

use chrono::{serde::ts_milliseconds, DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;

use super::constants::ACTIVITY_NAME;

///
/// Possible attribute values according to the XES Standard
///
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "type", content = "content")]
pub enum AttributeValue {
    /// String values
    String(String),
    #[serde(with = "ts_milliseconds")]
    /// DateTime values
    Date(DateTime<Utc>),
    /// Integer values
    Int(i64),
    /// Float values
    Float(f64),
    /// Boolean values
    Boolean(bool),
    /// IDs (UUIDs)
    ID(Uuid),
    /// List of other Attributes (where order matters; might contain multiple child attributes with the same key)
    List(Vec<Attribute>),
    /// List of other Attributes (where order does not matter)
    Container(Attributes),
    /// Used to represent invalid values (e.g., DateTime which could not be parsed)
    None(),
}

impl AttributeValue {
    ///
    /// Try to get attribute value as String
    ///
    /// Returns inner value if self is of variant [`AttributeValue::String`]
    ///
    /// Otherwise, returns None

    pub fn try_get_string(&self) -> Option<&String> {
        match self {
            AttributeValue::String(s) => Some(s),
            _ => None,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
///
/// Attribute made up of the key and value
///
/// Depending on usage, the key field might be redundant but useful for some implementations
///
pub struct Attribute {
    /// Attribute key
    pub key: String,
    /// Attribute value
    pub value: AttributeValue,
    /// Child attributes (nested)
    pub own_attributes: Option<Attributes>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
/// Version of [`Attribute`] which represents underlying nested attributes as a [`HashMap`]
///
/// Only used for easier JSON-interop with `ProM`
pub struct HashMapAttribute {
    /// Attribute key
    pub key: String,
    /// Attribute value
    pub value: AttributeValue,
    /// Child attributes (nested)
    pub own_attributes: Option<HashMap<String, HashMapAttribute>>,
}

impl Attribute {
    ///
    /// Helper to create a new attribute
    ///
    pub fn new(key: String, attribute_val: AttributeValue) -> Self {
        Self {
            key,
            value: attribute_val,
            own_attributes: None,
        }
    }
    ///
    /// Helper to create a new attribute, while returning the key String additionally
    ///
    /// This is useful for directly inserting the attribute in a [`HashMap`] afterwards
    ///
    #[deprecated(
        since = "0.2.0",
        note = "This function will be removed soon as Attributes are now backed by Vec instead of HashMap"
    )]
    pub fn new_with_key(key: String, attribute_val: AttributeValue) -> (String, Self) {
        (
            key.clone(),
            Self {
                key,
                value: attribute_val,
                own_attributes: None,
            },
        )
    }
}

///
/// Attributes are [`Vec`] of [`Attribute`]s
///
pub type Attributes = Vec<Attribute>;

///
/// Trait to easily add and update attributes
///
pub trait AttributeAddable {
    ///
    /// Add a new attribute (with key and value)
    ///
    /// Note: Does _not_ check if attribute was already present and does _not_ sort attributes wrt. key.
    ///
    fn add_to_attributes(&mut self, key: String, value: AttributeValue);
    ///
    /// Add a new attribute
    ///
    fn add_attribute(&mut self, attr: Attribute);
    ///
    /// Get an attribute by key
    ///
    /// _Complexity_: Does linear lookup (i.e., in O(n)). If you need faster lookup, consider manually sorting the attributes by key and utilizing binary search.
    fn get_by_key(&self, key: &str) -> Option<&Attribute>;
    ///
    /// Get an attribute as mutable by key
    ///
    /// _Complexity_: Does linear lookup (i.e., in O(n)). If you need faster lookup, consider manually sorting the attributes by key and utilizing binary search.
    fn get_by_key_mut(&mut self, key: &str) -> Option<&mut Attribute>;
    ///
    /// Get an attribute by key or the default value (e.g., provided by global event or trace attributes)
    ///
    /// _Complexity_: Does linear lookup (i.e., in O(n)). If you need faster lookup, consider manually sorting the attributes by key and utilizing binary search.
    fn get_by_key_or_global<'a>(
        &'a self,
        key: &str,
        global_attrs: &'a Option<&'a Attributes>,
    ) -> Option<&'a Attribute>;
    ///
    /// Remove attribute with given key
    ///
    /// Returns `true` if the attribute was present and `false` otherwise
    ///
    /// _Complexity_: Does linear lookup (i.e., in O(n)). If you need faster lookup, consider manually sorting the attributes by key and utilizing binary search.
    fn remove_with_key(&mut self, key: &str) -> bool;

    ///
    /// Convert Attributes to [`HashMap`]-backed version
    ///
    /// Used for creating attribute structures that are more easily compatible with other JSON representations of [`Attributes`].
    ///
    /// __Usage is generally discouraged__
    ///
    /// _Warning_: Currently, nested attributes are stripped.
    ///
    ///
    fn as_hash_map(&self) -> HashMap<String, HashMapAttribute>;
}
impl AttributeAddable for Attributes {
    fn add_to_attributes(&mut self, key: String, value: AttributeValue) {
        let a = Attribute::new(key, value);
        self.push(a);
    }

    fn add_attribute(&mut self, a: Attribute) {
        self.push(a);
    }

    fn get_by_key(&self, key: &str) -> Option<&Attribute> {
        self.iter().find(|attr| attr.key == key)
    }

    fn get_by_key_mut(&mut self, key: &str) -> Option<&mut Attribute> {
        self.iter_mut().find(|attr| attr.key == key)
    }

    fn get_by_key_or_global<'a>(
        &'a self,
        key: &str,
        global_attrs: &'a Option<&'a Attributes>,
    ) -> Option<&'a Attribute> {
        // TODO
        if let Some(attr) = self.iter().find(|attr| attr.key == key) {
            return Some(attr);
        }
        if let Some(global_attrs) = global_attrs {
            if let Some(attr) = global_attrs.get_by_key(key) {
                return Some(attr);
            }
        }
        None
    }

    fn remove_with_key(&mut self, key: &str) -> bool {
        let index_opt = self.iter().position(|a| a.key == key);
        if let Some(index) = index_opt {
            self.remove(index);
            return true;
        }
        false
    }
    fn as_hash_map(&self) -> HashMap<String, HashMapAttribute> {
        self.iter()
            .map(|a| {
                let a_clone = HashMapAttribute {
                    key: a.key.clone(),
                    value: a.value.clone(),
                    own_attributes: None,
                };
                (a.key.clone(), a_clone)
            })
            .collect()
    }
}

/// Covert a [`HashMap`] of attributes to a [`Attributes`] representation
pub fn to_attributes(from: HashMap<String, AttributeValue>) -> Attributes {
    from.into_iter()
        .map(|(key, value)| Attribute {
            key,
            value,
            own_attributes: None,
        })
        .collect()
}

///
/// An event consists of multiple (event) attributes ([Attributes])
///
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Event {
    /// Event attributes
    pub attributes: Attributes,
}
impl Event {
    /// Create a new event with the provided activity
    ///
    /// Implicitly assumes usage of the concept XES extension (i.e., uses [`ACTIVITY_NAME`] as key)
    pub fn new(activity: String) -> Self {
        Event {
            attributes: to_attributes(
                vec![(ACTIVITY_NAME.to_string(), AttributeValue::String(activity))]
                    .into_iter()
                    .collect(),
            ),
        }
    }
}

///
/// A trace consists of a list of events and trace attributes (See also [`Event`] and [`Attributes`])
///
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Trace {
    /// Trace-level attributes
    pub attributes: Attributes,
    /// Events contained in trace
    pub events: Vec<Event>,
}

///
/// Event log consisting of a list of [`Trace`]s and log [`Attributes`]
///
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct EventLog {
    /// Top-level attributes
    pub attributes: Attributes,
    /// Traces contained in log
    pub traces: Vec<Trace>,
    /// XES Extensions
    pub extensions: Option<Vec<EventLogExtension>>,
    /// XES Event classifiers
    pub classifiers: Option<Vec<EventLogClassifier>>,
    /// Global trace attributes
    pub global_trace_attrs: Option<Attributes>,
    ///  Global event attributes
    pub global_event_attrs: Option<Attributes>,
}

impl EventLog {
    ///
    /// Try to get the [`EventLogClassifier`] with the associated name
    ///
    pub fn get_classifier_by_name<S>(&self, name: S) -> Option<EventLogClassifier>
    where
        std::string::String: PartialEq<S>,
    {
        self.classifiers
            .as_ref()
            .and_then(|classifiers| classifiers.iter().find(|c| c.name == name).cloned())
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
/// An XES Extension
pub struct EventLogExtension {
    /// Extension name
    pub name: String,
    /// Prefix of attributes defined by the extension
    pub prefix: String,
    /// URI pointing to XESEXT of the XES extension 
    pub uri: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
/// Event classifier
/// 
/// Enables classifying events by a set of attributes to consider for the _class identity_
pub struct EventLogClassifier {
    /// Name of the classifier
    pub name: String,
    /// List of attribute keys to consider for the _class identity_
    ///
    /// Note: Currently keys might not be correctly parsed (i.e., they are just split at a " " while the XES standard defined more complicated detection)
    ///
    /// TODO: Investigate aligning parsing implementation with XES standard
    pub keys: Vec<String>,
}
impl EventLogClassifier {
    /// Delimiter for combining the values defined by the classifer to form a single class identity string
    pub const DELIMITER: &'static str = "+";
    ///
    /// Get the class identity (joined with [`EventLogClassifier::DELIMITER`])
    ///
    /// Missing attributes and attributes with a type different than [`AttributeValue::String`] are represented by an empty String.
    ///
    ///
    /// Note: Currently classifier keys might not be correctly parsed (i.e., they are just split at a " " while the XES standard defined more complicated detection)
    ///
    /// TODO: Investigate aligning parsing implementation with XES standard
    ///
    pub fn get_class_identity(&self, ev: &Event) -> String {
        let mut ret: String = String::new();
        let mut first = true;
        for k in &self.keys {
            let s = match ev.attributes.get_by_key(k).map(|at| at.value.clone()) {
                Some(AttributeValue::String(s)) => s,
                _ => String::new(),
            };
            if !first {
                ret.push_str(EventLogClassifier::DELIMITER)
            } else {
                first = false;
            }
            ret.push_str(&s);
        }
        ret
    }
}