1use crate::{
2 predicates::PredicateKind,
3 strings::{StringId, StringTable},
4};
5use itertools::Itertools;
6use rust_decimal::Decimal;
7use std::{
8 collections::HashMap,
9 fmt::{Display, Formatter},
10 ops::Index,
11};
12use thiserror::Error;
13
14#[derive(Error, PartialEq, Debug)]
15pub enum EventError {
16 #[error("attribute {0} has already been defined")]
17 AlreadyPresent(String),
18 #[error("event is missing some attributes")]
19 MissingAttributes,
20 #[error("ABE refers to non-existing attribute '{0:?}'")]
21 NonExistingAttribute(String),
22 #[error("{name:?}: wrong types => expected: {expected:?}, found: {actual:?}")]
23 WrongType {
24 name: String,
25 expected: AttributeKind,
26 actual: AttributeKind,
27 },
28 #[error("{name:?}: mismatching types => expected: {expected:?}, found: {actual:?}")]
29 MismatchingTypes {
30 name: String,
31 expected: AttributeKind,
32 actual: PredicateKind,
33 },
34}
35
36#[derive(Debug)]
41pub struct EventBuilder<'atree> {
42 by_ids: Vec<AttributeValue>,
43 attributes: &'atree AttributeTable,
44 strings: &'atree StringTable,
45}
46
47impl<'atree> EventBuilder<'atree> {
48 pub(crate) fn new(attributes: &'atree AttributeTable, strings: &'atree StringTable) -> Self {
49 Self {
50 attributes,
51 strings,
52 by_ids: vec![AttributeValue::Undefined; attributes.len()],
53 }
54 }
55
56 pub fn build(self) -> Result<Event, EventError> {
81 Ok(Event(self.by_ids))
82 }
83
84 pub fn with_boolean(&mut self, name: &str, value: bool) -> Result<(), EventError> {
88 self.add_value(name, AttributeKind::Boolean, || {
89 AttributeValue::Boolean(value)
90 })
91 }
92
93 pub fn with_integer(&mut self, name: &str, value: i64) -> Result<(), EventError> {
97 self.add_value(name, AttributeKind::Integer, || {
98 AttributeValue::Integer(value)
99 })
100 }
101
102 pub fn with_float(&mut self, name: &str, number: i64, scale: u32) -> Result<(), EventError> {
106 self.add_value(name, AttributeKind::Float, || {
107 AttributeValue::Float(Decimal::new(number, scale))
108 })
109 }
110
111 pub fn with_string(&mut self, name: &str, value: &str) -> Result<(), EventError> {
115 self.add_value(name, AttributeKind::String, || {
116 let string_index = self.strings.get(value);
117 AttributeValue::String(string_index)
118 })
119 }
120
121 pub fn with_integer_list(&mut self, name: &str, value: &[i64]) -> Result<(), EventError> {
126 self.add_value(name, AttributeKind::IntegerList, || {
127 let values = value.iter().sorted().unique().cloned().collect_vec();
128 AttributeValue::IntegerList(values)
129 })
130 }
131
132 pub fn with_undefined(&mut self, name: &str) -> Result<(), EventError> {
136 let index = self
137 .attributes
138 .by_name(name)
139 .ok_or_else(|| EventError::NonExistingAttribute(name.to_string()))?;
140 self.by_ids[index.0] = AttributeValue::Undefined;
141 Ok(())
142 }
143
144 pub fn with_string_list(&mut self, name: &str, values: &[&str]) -> Result<(), EventError> {
149 self.add_value(name, AttributeKind::StringList, || {
150 let values: Vec<_> = values
151 .iter()
152 .map(|v| self.strings.get(v))
153 .sorted()
154 .unique()
155 .collect();
156 AttributeValue::StringList(values)
157 })
158 }
159
160 #[inline]
161 fn add_value<F>(&mut self, name: &str, actual: AttributeKind, f: F) -> Result<(), EventError>
162 where
163 F: FnOnce() -> AttributeValue,
164 {
165 let index = self
166 .attributes
167 .by_name(name)
168 .ok_or_else(|| EventError::NonExistingAttribute(name.to_string()))?;
169 let expected = self.attributes.by_id(index);
170 if expected != actual {
171 return Err(EventError::WrongType {
172 name: name.to_owned(),
173 expected,
174 actual,
175 });
176 }
177 self.by_ids[index.0] = f();
178 Ok(())
179 }
180}
181
182#[derive(Clone, Debug)]
185pub struct Event(Vec<AttributeValue>);
186
187impl Index<AttributeId> for Event {
188 type Output = AttributeValue;
189
190 #[inline]
191 fn index(&self, index: AttributeId) -> &Self::Output {
192 &self.0[index.0]
193 }
194}
195
196#[derive(Clone, Debug)]
197pub enum AttributeValue {
198 Boolean(bool),
199 Integer(i64),
200 Float(Decimal),
201 String(StringId),
202 IntegerList(Vec<i64>),
203 StringList(Vec<StringId>),
204 Undefined,
205}
206
207#[derive(Clone, Debug)]
208pub struct AttributeTable {
209 by_names: HashMap<String, AttributeId>,
210 by_ids: Vec<AttributeKind>,
211}
212
213#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd, Debug, Hash)]
214pub struct AttributeId(usize);
215
216impl Display for AttributeId {
217 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
218 write!(formatter, "attribute({})", self.0)
219 }
220}
221
222impl AttributeTable {
223 pub fn new(definitions: &[AttributeDefinition]) -> Result<Self, EventError> {
224 let size = definitions.len();
225 let mut by_names = HashMap::with_capacity(size);
226 let mut by_ids = Vec::with_capacity(size);
227 for (i, definition) in definitions.iter().enumerate() {
228 let name = definition.name.to_owned();
229 if by_names.contains_key(&name) {
230 return Err(EventError::AlreadyPresent(name));
231 }
232
233 by_names.insert(name, AttributeId(i));
234 by_ids.push(definition.kind.clone());
235 }
236
237 Ok(Self { by_names, by_ids })
238 }
239
240 #[inline]
241 pub fn by_name(&self, name: &str) -> Option<AttributeId> {
242 self.by_names.get(name).cloned()
243 }
244
245 #[inline]
246 pub fn by_id(&self, id: AttributeId) -> AttributeKind {
247 self.by_ids[id.0].clone()
248 }
249
250 #[inline]
251 pub fn len(&self) -> usize {
252 self.by_ids.len()
253 }
254}
255
256#[derive(Debug, Clone)]
258pub struct AttributeDefinition {
259 name: String,
260 kind: AttributeKind,
261}
262
263#[derive(Clone, PartialEq, Debug)]
264pub enum AttributeKind {
265 Boolean,
266 Integer,
267 Float,
268 String,
269 IntegerList,
270 StringList,
271}
272
273impl AttributeDefinition {
274 pub fn boolean(name: &str) -> Self {
276 let kind = AttributeKind::Boolean;
277 Self {
278 name: name.to_owned(),
279 kind,
280 }
281 }
282
283 pub fn integer(name: &str) -> Self {
285 let kind = AttributeKind::Integer;
286 Self {
287 name: name.to_owned(),
288 kind,
289 }
290 }
291
292 pub fn float(name: &str) -> Self {
294 let kind = AttributeKind::Float;
295 Self {
296 name: name.to_owned(),
297 kind,
298 }
299 }
300
301 pub fn string(name: &str) -> Self {
303 let kind = AttributeKind::String;
304 Self {
305 name: name.to_owned(),
306 kind,
307 }
308 }
309
310 pub fn integer_list(name: &str) -> Self {
312 let kind = AttributeKind::IntegerList;
313 Self {
314 name: name.to_owned(),
315 kind,
316 }
317 }
318
319 pub fn string_list(name: &str) -> Self {
321 let kind = AttributeKind::StringList;
322 Self {
323 name: name.to_owned(),
324 kind,
325 }
326 }
327}
328
329#[cfg(test)]
330mod tests {
331 use super::*;
332
333 #[test]
334 fn can_create_an_attribute_table_with_no_attributes() {
335 assert!(AttributeTable::new(&[]).is_ok())
336 }
337
338 #[test]
339 fn can_create_an_attribute_table_with_some_attributes() {
340 let definitions = [
341 AttributeDefinition::boolean("private"),
342 AttributeDefinition::string_list("deals"),
343 AttributeDefinition::integer("exchange_id"),
344 AttributeDefinition::float("bidfloor"),
345 AttributeDefinition::string("country"),
346 AttributeDefinition::integer_list("segment_ids"),
347 ];
348
349 assert!(AttributeTable::new(&definitions).is_ok());
350 }
351
352 #[test]
353 fn return_an_error_on_duplicate_definitions() {
354 let definitions = [
355 AttributeDefinition::boolean("private"),
356 AttributeDefinition::string("country"),
357 AttributeDefinition::string_list("deals"),
358 AttributeDefinition::integer("exchange_id"),
359 AttributeDefinition::float("bidfloor"),
360 AttributeDefinition::integer("country"),
361 AttributeDefinition::integer_list("segment_ids"),
362 ];
363
364 assert!(AttributeTable::new(&definitions).is_err());
365 }
366
367 #[test]
368 fn can_add_a_boolean_attribute_value() {
369 let attributes = AttributeTable::new(&[AttributeDefinition::boolean("private")]).unwrap();
370 let strings = StringTable::new();
371 let mut event_builder = EventBuilder::new(&attributes, &strings);
372
373 let result = event_builder.with_boolean("private", true);
374
375 assert!(result.is_ok());
376 }
377
378 #[test]
379 fn can_add_an_integer_attribute_value() {
380 let attributes =
381 AttributeTable::new(&[AttributeDefinition::integer("exchange_id")]).unwrap();
382 let strings = StringTable::new();
383 let mut event_builder = EventBuilder::new(&attributes, &strings);
384
385 let result = event_builder.with_integer("exchange_id", 1);
386
387 assert!(result.is_ok());
388 }
389
390 #[test]
391 fn can_add_a_float_attribute_value() {
392 let attributes = AttributeTable::new(&[AttributeDefinition::float("bidfloor")]).unwrap();
393 let strings = StringTable::new();
394 let mut event_builder = EventBuilder::new(&attributes, &strings);
395
396 let result = event_builder.with_float("bidfloor", 1, 0);
397
398 assert!(result.is_ok());
399 }
400
401 #[test]
402 fn can_add_a_string_attribute_value() {
403 let attributes = AttributeTable::new(&[AttributeDefinition::string("country")]).unwrap();
404 let strings = StringTable::new();
405 let mut event_builder = EventBuilder::new(&attributes, &strings);
406
407 let result = event_builder.with_string("country", "US");
408
409 assert!(result.is_ok());
410 }
411
412 #[test]
413 fn can_add_an_integer_list_attribute_value() {
414 let attributes =
415 AttributeTable::new(&[AttributeDefinition::integer_list("segment_ids")]).unwrap();
416 let strings = StringTable::new();
417 let mut event_builder = EventBuilder::new(&attributes, &strings);
418
419 let result = event_builder.with_integer_list("segment_ids", &[1, 2, 3]);
420
421 assert!(result.is_ok());
422 }
423
424 #[test]
425 fn can_add_an_string_list_attribute_value() {
426 let attributes =
427 AttributeTable::new(&[AttributeDefinition::string_list("deal_ids")]).unwrap();
428 let strings = StringTable::new();
429 let mut event_builder = EventBuilder::new(&attributes, &strings);
430
431 let result = event_builder.with_string_list("deal_ids", &["deal-1", "deal-2"]);
432
433 assert!(result.is_ok());
434 }
435
436 #[test]
437 fn return_an_error_when_adding_a_non_existing_attribute() {
438 let attributes =
439 AttributeTable::new(&[AttributeDefinition::string_list("deal_ids")]).unwrap();
440 let strings = StringTable::new();
441 let mut event_builder = EventBuilder::new(&attributes, &strings);
442
443 let result = event_builder.with_boolean("non_existing", true);
444
445 assert!(matches!(result, Err(EventError::NonExistingAttribute(_))));
446 }
447
448 #[test]
449 fn can_create_an_event_with_no_attributes() {
450 let attributes = AttributeTable::new(&[]).unwrap();
451 let strings = StringTable::new();
452 let event_builder = EventBuilder::new(&attributes, &strings);
453
454 assert!(event_builder.build().is_ok());
455 }
456
457 #[test]
458 fn can_create_an_event_with_attributes() {
459 let attributes = AttributeTable::new(&[
460 AttributeDefinition::boolean("private"),
461 AttributeDefinition::string_list("deals"),
462 AttributeDefinition::integer("exchange_id"),
463 AttributeDefinition::float("bidfloor"),
464 AttributeDefinition::string("country"),
465 AttributeDefinition::integer_list("segment_ids"),
466 ])
467 .unwrap();
468 let strings = StringTable::new();
469 let mut builder = EventBuilder::new(&attributes, &strings);
470
471 assert!(builder.with_boolean("private", true).is_ok());
472 assert!(builder
473 .with_string_list("deals", &["deal-1", "deal-2"])
474 .is_ok());
475 assert!(builder.with_integer("exchange_id", 1).is_ok());
476 assert!(builder.with_float("bidfloor", 1, 0).is_ok());
477 assert!(builder.with_string("country", "US").is_ok());
478 assert!(builder.with_integer_list("segment_ids", &[1, 2, 3]).is_ok());
479
480 assert!(builder.build().is_ok());
481 }
482
483 #[test]
484 fn can_create_an_event_with_a_missing_attribute() {
485 let attributes = AttributeTable::new(&[AttributeDefinition::boolean("private")]).unwrap();
486 let strings = StringTable::new();
487 let event_builder = EventBuilder::new(&attributes, &strings);
488
489 assert!(event_builder.build().is_ok());
490 }
491
492 #[test]
493 fn return_an_error_when_trying_to_add_an_attribute_with_mismatched_type() {
494 let attributes = AttributeTable::new(&[AttributeDefinition::boolean("private")]).unwrap();
495 let strings = StringTable::new();
496 let mut event_builder = EventBuilder::new(&attributes, &strings);
497
498 let result = event_builder.with_integer("private", 1);
499
500 assert!(result.is_err());
501 }
502}