1use std::{borrow::Cow, cmp::Ordering, ops::Deref, str::FromStr};
2
3use ordered_float::OrderedFloat;
4pub use uuid::Uuid;
5
6#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8pub struct AttrKey(Cow<'static, str>);
9
10impl AttrKey {
11 pub const fn new(k: String) -> Self {
12 Self(Cow::Owned(k))
13 }
14
15 pub const fn new_static(k: &'static str) -> Self {
16 Self(Cow::Borrowed(k))
17 }
18}
19
20impl From<&str> for AttrKey {
21 fn from(s: &str) -> Self {
22 AttrKey(Cow::from(s.to_owned()))
23 }
24}
25
26impl From<String> for AttrKey {
27 fn from(s: String) -> Self {
28 AttrKey(Cow::from(s))
29 }
30}
31
32impl AsRef<str> for AttrKey {
33 fn as_ref(&self) -> &str {
34 self.0.as_ref()
35 }
36}
37
38impl From<AttrKey> for String {
39 fn from(k: AttrKey) -> Self {
40 match k.0 {
41 Cow::Borrowed(b) => b.to_owned(),
42 Cow::Owned(o) => o,
43 }
44 }
45}
46
47impl std::fmt::Display for AttrKey {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
49 write!(f, "{}", self.0)
50 }
51}
52
53#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
60#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
61pub struct BigInt(Box<i128>);
62
63impl BigInt {
64 pub fn new_attr_val(big_i: i128) -> AttrVal {
65 if big_i < (i64::MIN as i128) || big_i > (i64::MAX as i128) {
67 AttrVal::BigInt(BigInt(Box::new(big_i)))
68 } else {
69 AttrVal::Integer(big_i as i64)
70 }
71 }
72}
73impl std::fmt::Display for BigInt {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 self.0.fmt(f)
76 }
77}
78
79impl AsRef<i128> for BigInt {
80 fn as_ref(&self) -> &i128 {
81 self.0.as_ref()
82 }
83}
84
85impl Deref for BigInt {
86 type Target = i128;
87
88 fn deref(&self) -> &Self::Target {
89 self.0.as_ref()
90 }
91}
92
93#[derive(Copy, Clone, Eq, PartialEq, Debug, Ord, PartialOrd, Hash)]
99#[repr(transparent)]
100#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
101#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
102pub struct Nanoseconds(u64);
103
104impl Nanoseconds {
105 pub fn get_raw(&self) -> u64 {
106 self.0
107 }
108}
109
110impl From<u64> for Nanoseconds {
111 fn from(n: u64) -> Self {
112 Nanoseconds(n)
113 }
114}
115
116impl std::fmt::Display for Nanoseconds {
117 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118 write!(f, "{}ns", self.0)
119 }
120}
121
122impl FromStr for Nanoseconds {
123 type Err = std::num::ParseIntError;
124 fn from_str(s: &str) -> Result<Self, Self::Err> {
125 Ok(Nanoseconds(s.parse::<u64>()?))
126 }
127}
128
129#[derive(Eq, PartialEq, Clone, Debug, Hash)]
135#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
136#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
137pub struct LogicalTime(Box<[u64; 4]>);
138
139impl LogicalTime {
140 pub fn unary<A: Into<u64>>(a: A) -> Self {
141 LogicalTime(Box::new([0, 0, 0, a.into()]))
142 }
143
144 pub fn binary<A: Into<u64>, B: Into<u64>>(a: A, b: B) -> Self {
145 LogicalTime(Box::new([0, 0, a.into(), b.into()]))
146 }
147
148 pub fn trinary<A: Into<u64>, B: Into<u64>, C: Into<u64>>(a: A, b: B, c: C) -> Self {
149 LogicalTime(Box::new([0, a.into(), b.into(), c.into()]))
150 }
151
152 pub fn quaternary<A: Into<u64>, B: Into<u64>, C: Into<u64>, D: Into<u64>>(
153 a: A,
154 b: B,
155 c: C,
156 d: D,
157 ) -> Self {
158 LogicalTime(Box::new([a.into(), b.into(), c.into(), d.into()]))
159 }
160
161 pub fn get_raw(&self) -> &[u64; 4] {
162 &self.0
163 }
164}
165
166impl Ord for LogicalTime {
167 fn cmp(&self, other: &Self) -> Ordering {
168 for (a, b) in self.0.iter().zip(other.0.iter()) {
169 match a.cmp(b) {
170 Ordering::Equal => (), Ordering::Less => return Ordering::Less,
172 Ordering::Greater => return Ordering::Greater,
173 }
174 }
175
176 Ordering::Equal
177 }
178}
179
180impl PartialOrd for LogicalTime {
181 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
182 Some(self.cmp(other))
183 }
184}
185
186impl std::fmt::Display for LogicalTime {
187 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
188 write!(f, "{}:{}:{}:{}", self.0[0], self.0[1], self.0[2], self.0[3])
189 }
190}
191
192impl FromStr for LogicalTime {
193 type Err = ();
194 fn from_str(s: &str) -> Result<Self, Self::Err> {
195 let mut segments = s.rsplit(':');
196
197 if let Ok(mut time) = segments.try_fold(Vec::new(), |mut acc, segment| {
198 segment.parse::<u64>().map(|t| {
199 acc.insert(0, t);
200 acc
201 })
202 }) {
203 while time.len() < 4 {
204 time.insert(0, 0)
205 }
206
207 let time_array = time.into_boxed_slice().try_into().map_err(|_| ())?;
208
209 Ok(LogicalTime(time_array))
210 } else {
211 Err(())
212 }
213 }
214}
215
216pub const TIMELINE_ID_SIGIL: char = '%';
221
222#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Debug)]
225#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
226#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
227pub struct TimelineId(Uuid);
228
229impl TimelineId {
230 pub fn zero() -> Self {
231 TimelineId(Uuid::nil())
232 }
233
234 pub fn allocate() -> Self {
235 TimelineId(Uuid::new_v4())
236 }
237
238 pub fn get_raw(&self) -> &Uuid {
239 &self.0
240 }
241}
242
243impl From<Uuid> for TimelineId {
244 fn from(uuid: Uuid) -> Self {
245 TimelineId(uuid)
246 }
247}
248
249impl std::fmt::Display for TimelineId {
250 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
251 self.0.fmt(f)
252 }
253}
254
255pub type OpaqueEventId = [u8; 16];
260
261#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
262#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
263#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
264pub struct EventCoordinate {
265 pub timeline_id: TimelineId,
266 pub id: OpaqueEventId,
267}
268impl EventCoordinate {
269 pub fn as_bytes(&self) -> [u8; 32] {
270 let mut bytes = [0u8; 32];
271 bytes[0..16].copy_from_slice(self.timeline_id.0.as_bytes());
272 bytes[16..32].copy_from_slice(&self.id);
273 bytes
274 }
275
276 pub fn from_byte_slice(bytes: &[u8]) -> Option<Self> {
277 if bytes.len() != 32 {
278 return None;
279 }
280
281 Some(EventCoordinate {
282 timeline_id: Uuid::from_slice(&bytes[0..16]).ok()?.into(),
283 id: bytes[16..32].try_into().ok()?,
284 })
285 }
286}
287
288impl std::fmt::Display for EventCoordinate {
289 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
290 write!(f, "{TIMELINE_ID_SIGIL}")?;
291
292 for byte in self.timeline_id.0.as_bytes() {
294 write!(f, "{byte:02x}")?;
295 }
296
297 write!(f, ":{}", EncodeHexWithoutLeadingZeroes(&self.id))
298 }
299}
300
301pub struct EncodeHexWithoutLeadingZeroes<'a>(pub &'a [u8]);
302
303impl<'a> std::fmt::Display for EncodeHexWithoutLeadingZeroes<'a> {
304 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
305 let mut cursor = 0;
306 let bytes = self.0;
307 while bytes[cursor] == 0 && cursor < bytes.len() - 1 {
308 cursor += 1;
309 }
310
311 if cursor == bytes.len() {
312 write!(f, "0")?;
313 } else {
314 for byte in bytes.iter().skip(cursor) {
315 write!(f, "{byte:02x}")?;
316 }
317 }
318
319 Ok(())
320 }
321}
322
323#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
328pub enum AttrVal {
329 TimelineId(Box<TimelineId>),
330 EventCoordinate(Box<EventCoordinate>),
331 String(Cow<'static, str>),
332 Integer(i64),
333 BigInt(BigInt),
334 Float(OrderedFloat<f64>),
335 Bool(bool),
336 Timestamp(Nanoseconds),
337 LogicalTime(LogicalTime),
338}
339
340impl AttrVal {
341 pub fn attr_type(&self) -> AttrType {
342 match self {
343 AttrVal::TimelineId(_) => AttrType::TimelineId,
344 AttrVal::EventCoordinate(_) => AttrType::EventCoordinate,
345 AttrVal::String(_) => AttrType::String,
346 AttrVal::Integer(_) => AttrType::Integer,
347 AttrVal::BigInt(_) => AttrType::BigInt,
348 AttrVal::Float(_) => AttrType::Float,
349 AttrVal::Bool(_) => AttrType::Bool,
350 AttrVal::Timestamp(_) => AttrType::Nanoseconds,
351 AttrVal::LogicalTime(_) => AttrType::LogicalTime,
352 }
353 }
354
355 pub fn as_timeline_id(self) -> std::result::Result<TimelineId, WrongAttrTypeError> {
356 self.try_into()
357 }
358
359 pub fn as_event_coordinate(self) -> std::result::Result<EventCoordinate, WrongAttrTypeError> {
360 self.try_into()
361 }
362
363 pub fn as_string(self) -> std::result::Result<Cow<'static, str>, WrongAttrTypeError> {
364 self.try_into()
365 }
366
367 pub fn as_int(self) -> std::result::Result<i64, WrongAttrTypeError> {
368 self.try_into()
369 }
370
371 pub fn as_bigint(self) -> std::result::Result<i128, WrongAttrTypeError> {
372 self.try_into()
373 }
374
375 pub fn as_float(self) -> std::result::Result<f64, WrongAttrTypeError> {
376 self.try_into()
377 }
378
379 pub fn as_bool(self) -> std::result::Result<bool, WrongAttrTypeError> {
380 self.try_into()
381 }
382
383 pub fn as_timestamp(self) -> std::result::Result<Nanoseconds, WrongAttrTypeError> {
384 self.try_into()
385 }
386
387 pub fn as_logical_time(self) -> std::result::Result<LogicalTime, WrongAttrTypeError> {
388 self.try_into()
389 }
390}
391
392impl std::fmt::Display for AttrVal {
393 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
394 match self {
395 AttrVal::String(s) => s.fmt(f),
396 AttrVal::Integer(i) => i.fmt(f),
397 AttrVal::BigInt(bi) => bi.fmt(f),
398 AttrVal::Float(fp) => fp.fmt(f),
399 AttrVal::Bool(b) => b.fmt(f),
400 AttrVal::Timestamp(ns) => ns.fmt(f),
401 AttrVal::LogicalTime(lt) => lt.fmt(f),
402 AttrVal::EventCoordinate(ec) => ec.fmt(f),
403 AttrVal::TimelineId(tid) => tid.fmt(f),
404 }
405 }
406}
407
408impl FromStr for AttrVal {
409 type Err = std::convert::Infallible;
410
411 fn from_str(s: &str) -> Result<Self, Self::Err> {
412 Ok(if let Ok(v) = s.to_lowercase().parse::<bool>() {
416 v.into()
417 } else if let Ok(v) = s.parse::<i128>() {
418 v.into()
420 } else if let Ok(v) = s.parse::<f64>() {
421 v.into()
422 } else if let Ok(v) = s.parse::<LogicalTime>() {
423 v.into()
424 } else if let Ok(v) = s.parse::<Uuid>() {
425 v.into()
426 } else {
427 AttrVal::String(s.trim_matches(|c| c == '"' || c == '\'').to_owned().into())
430 })
431 }
432}
433
434impl From<String> for AttrVal {
435 fn from(s: String) -> AttrVal {
436 AttrVal::String(Cow::Owned(s))
437 }
438}
439
440impl From<&str> for AttrVal {
441 fn from(s: &str) -> AttrVal {
442 AttrVal::String(Cow::Owned(s.to_owned()))
443 }
444}
445
446impl From<Cow<'static, str>> for AttrVal {
447 fn from(s: Cow<'static, str>) -> Self {
448 AttrVal::String(s)
449 }
450}
451
452impl From<&String> for AttrVal {
453 fn from(s: &String) -> Self {
454 AttrVal::String(Cow::Owned(s.clone()))
455 }
456}
457
458impl From<bool> for AttrVal {
459 fn from(b: bool) -> AttrVal {
460 AttrVal::Bool(b)
461 }
462}
463
464impl From<Nanoseconds> for AttrVal {
465 fn from(ns: Nanoseconds) -> AttrVal {
466 AttrVal::Timestamp(ns)
467 }
468}
469
470impl From<LogicalTime> for AttrVal {
471 fn from(lt: LogicalTime) -> AttrVal {
472 AttrVal::LogicalTime(lt)
473 }
474}
475
476impl From<Uuid> for AttrVal {
477 fn from(u: Uuid) -> AttrVal {
478 AttrVal::TimelineId(Box::new(u.into()))
479 }
480}
481
482impl From<EventCoordinate> for AttrVal {
483 fn from(coord: EventCoordinate) -> Self {
484 AttrVal::EventCoordinate(Box::new(coord))
485 }
486}
487
488impl From<TimelineId> for AttrVal {
489 fn from(timeline_id: TimelineId) -> Self {
490 AttrVal::TimelineId(Box::new(timeline_id))
491 }
492}
493
494#[derive(Hash, Eq, PartialEq, Copy, Clone, Debug, PartialOrd, Ord)]
495pub enum AttrType {
496 TimelineId,
497 EventCoordinate,
498 String,
499 Integer,
500 BigInt,
501 Float,
502 Bool,
503 Nanoseconds,
504 LogicalTime,
505 Any,
506}
507
508impl std::fmt::Display for AttrType {
509 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
510 match self {
511 AttrType::TimelineId => "TimelineId",
512 AttrType::String => "String",
513 AttrType::Integer => "Integer",
514 AttrType::BigInt => "BigInteger",
515 AttrType::Float => "Float",
516 AttrType::Bool => "Bool",
517 AttrType::Nanoseconds => "Nanoseconds",
518 AttrType::LogicalTime => "LogicalTime",
519 AttrType::Any => "Any",
520 AttrType::EventCoordinate => "Coordinate",
521 }
522 .fmt(f)
523 }
524}
525
526pub mod conversion {
527 use std::convert::TryFrom;
528
529 use super::*;
530
531 macro_rules! impl_from_integer {
532 ($ty:ty) => {
533 impl From<$ty> for AttrVal {
534 fn from(i: $ty) -> Self {
535 AttrVal::Integer(i as i64)
536 }
537 }
538 };
539 }
540
541 impl_from_integer!(i8);
542 impl_from_integer!(i16);
543 impl_from_integer!(i32);
544 impl_from_integer!(i64);
545 impl_from_integer!(u8);
546 impl_from_integer!(u16);
547 impl_from_integer!(u32);
548
549 macro_rules! impl_from_bigint {
550 ($ty:ty) => {
551 impl From<$ty> for AttrVal {
552 fn from(i: $ty) -> Self {
553 BigInt::new_attr_val(i as i128)
554 }
555 }
556 };
557 }
558
559 impl_from_bigint!(u64);
560 impl_from_bigint!(i128);
561
562 macro_rules! impl_from_float {
563 ($ty:ty) => {
564 impl From<$ty> for AttrVal {
565 fn from(f: $ty) -> Self {
566 AttrVal::Float((f as f64).into())
567 }
568 }
569 };
570 }
571
572 impl_from_float!(f32);
573 impl_from_float!(f64);
574
575 macro_rules! impl_try_from_attr_val {
576 ($variant:path, $ty:ty, $expected:path) => {
577 impl TryFrom<AttrVal> for $ty {
578 type Error = WrongAttrTypeError;
579
580 fn try_from(value: AttrVal) -> std::result::Result<Self, Self::Error> {
581 if let $variant(x) = value {
582 Ok(x.into())
583 } else {
584 Err(WrongAttrTypeError {
585 actual: value.attr_type(),
586 expected: $expected,
587 })
588 }
589 }
590 }
591 };
592 }
593
594 macro_rules! impl_try_from_attr_val_deref {
595 ($variant:path, $ty:ty, $expected:path) => {
596 impl TryFrom<AttrVal> for $ty {
597 type Error = WrongAttrTypeError;
598
599 fn try_from(value: AttrVal) -> std::result::Result<Self, Self::Error> {
600 if let $variant(x) = value {
601 Ok((*x).clone())
602 } else {
603 Err(WrongAttrTypeError {
604 actual: value.attr_type(),
605 expected: $expected,
606 })
607 }
608 }
609 }
610 };
611 }
612
613 impl_try_from_attr_val_deref!(AttrVal::TimelineId, TimelineId, AttrType::TimelineId);
614 impl_try_from_attr_val_deref!(
615 AttrVal::EventCoordinate,
616 EventCoordinate,
617 AttrType::EventCoordinate
618 );
619
620 impl_try_from_attr_val!(AttrVal::Integer, i64, AttrType::Integer);
621 impl_try_from_attr_val!(AttrVal::String, Cow<'static, str>, AttrType::String);
622 impl_try_from_attr_val_deref!(AttrVal::BigInt, i128, AttrType::BigInt);
623 impl_try_from_attr_val!(AttrVal::Float, f64, AttrType::Float);
624 impl_try_from_attr_val!(AttrVal::Bool, bool, AttrType::Bool);
625 impl_try_from_attr_val!(AttrVal::LogicalTime, LogicalTime, AttrType::LogicalTime);
626 impl_try_from_attr_val!(AttrVal::Timestamp, Nanoseconds, AttrType::Nanoseconds);
627}
628
629#[derive(Debug, thiserror::Error, Eq, PartialEq)]
630#[error("Wrong attribute type: expected {expected:?}, found {actual:?}")]
631pub struct WrongAttrTypeError {
632 actual: AttrType,
633 expected: AttrType,
634}
635
636#[cfg(test)]
637mod tests {
638 use super::*;
639
640 #[test]
641 fn parse_logical_time() {
642 let reference = Ok(LogicalTime::quaternary(0u64, 0u64, 0u64, 42u64));
643
644 assert_eq!(reference, "42".parse());
646 assert_eq!(reference, "0:42".parse());
647 assert_eq!(reference, "0:0:42".parse());
648 assert_eq!(reference, "0:0:0:42".parse());
649
650 assert_eq!(Err(()), ":".parse::<LogicalTime>());
652 assert_eq!(Err(()), "::".parse::<LogicalTime>());
653 assert_eq!(Err(()), ":0".parse::<LogicalTime>());
654 assert_eq!(Err(()), "0:".parse::<LogicalTime>());
655 assert_eq!(Err(()), "127.0.0.1:8080".parse::<LogicalTime>());
656 assert_eq!(Err(()), "localhost:8080".parse::<LogicalTime>());
657 assert_eq!(Err(()), "example.com:8080".parse::<LogicalTime>());
658 }
659
660 #[test]
661 fn parse_attr_vals() {
662 assert_eq!(Ok(AttrVal::Bool(false)), "false".parse());
664 assert_eq!(Ok(AttrVal::Bool(true)), "true".parse());
665
666 assert_eq!(Ok(AttrVal::Integer(37)), "37".parse());
668
669 assert_eq!(
671 Ok(BigInt::new_attr_val(36893488147419103232i128)),
672 "36893488147419103232".parse()
673 );
674
675 assert_eq!(Ok(AttrVal::Float(76.37f64.into())), "76.37".parse());
677
678 assert_eq!(
680 Ok(AttrVal::TimelineId(Box::new(
681 Uuid::parse_str("bec14bc0-1dea-4b68-b138-62f7b6827e35")
682 .unwrap()
683 .into()
684 ))),
685 "bec14bc0-1dea-4b68-b138-62f7b6827e35".parse()
686 );
687
688 let lt_ref = Ok(AttrVal::LogicalTime(LogicalTime::quaternary(
696 0u64, 0u64, 0u64, 42u64,
697 )));
698 assert_eq!(lt_ref, "0:42".parse());
699 assert_eq!(lt_ref, "0:0:42".parse());
700 assert_eq!(lt_ref, "0:0:0:42".parse());
701
702 assert_eq!(
704 Ok(AttrVal::String("Hello, World!".into())),
705 "\"Hello, World!\"".parse()
706 );
707 assert_eq!(
708 Ok(AttrVal::String("Hello, World!".into())),
709 "'Hello, World!'".parse()
710 );
711 assert_eq!(
712 Ok(AttrVal::String("Hello, World!".into())),
713 "Hello, World!".parse()
714 );
715
716 assert_eq!(Ok(AttrVal::String("".into())), "\"\"".parse());
717 assert_eq!(Ok(AttrVal::String("".into())), "\"".parse());
718
719 assert_eq!(Ok(AttrVal::String("".into())), "''".parse());
720 assert_eq!(Ok(AttrVal::String("".into())), "'".parse());
721
722 assert_eq!(Ok(AttrVal::String("".into())), "".parse());
723 }
724}