1use std::{
21 collections::HashMap,
22 fmt,
23 ops::{Add, Neg, Sub},
24 str::FromStr,
25};
26
27use chrono::{DateTime, FixedOffset, MappedLocalTime, NaiveDate, NaiveDateTime};
28use chrono_tz::Tz;
29
30use crate::{Error, error::ConceptError};
31
32#[derive(Clone, PartialEq, Eq)]
34pub enum ValueType {
35 Boolean,
36 Integer,
37 Double,
38 Decimal,
39 String,
40 Date,
41 Datetime,
42 DatetimeTZ,
43 Duration,
44 Struct(String),
45}
46
47impl ValueType {
48 pub(crate) const NONE_STR: &'static str = "none";
49 pub(crate) const BOOLEAN_STR: &'static str = "boolean";
50 pub(crate) const INTEGER_STR: &'static str = "integer";
51 pub(crate) const DOUBLE_STR: &'static str = "double";
52 pub(crate) const DECIMAL_STR: &'static str = "decimal";
53 pub(crate) const STRING_STR: &'static str = "string";
54 pub(crate) const DATE_STR: &'static str = "date";
55 pub(crate) const DATETIME_STR: &'static str = "datetime";
56 pub(crate) const DATETIME_TZ_STR: &'static str = "datetime-tz";
57 pub(crate) const DURATION_STR: &'static str = "duration";
58
59 pub fn name(&self) -> &str {
60 match self {
61 Self::Boolean => Self::BOOLEAN_STR,
62 Self::Integer => Self::INTEGER_STR,
63 Self::Double => Self::DOUBLE_STR,
64 Self::Decimal => Self::DECIMAL_STR,
65 Self::String => Self::STRING_STR,
66 Self::Date => Self::DATE_STR,
67 Self::Datetime => Self::DATETIME_STR,
68 Self::DatetimeTZ => Self::DATETIME_TZ_STR,
69 Self::Duration => Self::DURATION_STR,
70 Self::Struct(name) => name,
71 }
72 }
73}
74
75impl fmt::Display for ValueType {
76 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77 fmt::Debug::fmt(self, f)
78 }
79}
80
81impl fmt::Debug for ValueType {
82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83 write!(f, "{}", self.name())
84 }
85}
86
87#[derive(Clone, PartialEq)]
88pub enum Value {
89 Boolean(bool),
90 Integer(i64),
91 Double(f64),
92 Decimal(Decimal),
93 String(String),
94 Date(NaiveDate),
95 Datetime(NaiveDateTime),
96 DatetimeTZ(DateTime<TimeZone>),
97 Duration(Duration),
98 Struct(Struct, String),
99}
100
101impl Value {
102 pub fn get_type(&self) -> ValueType {
110 match self {
111 Self::Boolean(_) => ValueType::Boolean,
112 Self::Integer(_) => ValueType::Integer,
113 Self::Double(_) => ValueType::Double,
114 Self::String(_) => ValueType::String,
115 Self::Decimal(_) => ValueType::Decimal,
116 Self::Date(_) => ValueType::Date,
117 Self::Datetime(_) => ValueType::Datetime,
118 Self::DatetimeTZ(_) => ValueType::DatetimeTZ,
119 Self::Duration(_) => ValueType::Duration,
120 Self::Struct(_, struct_type_name) => ValueType::Struct(struct_type_name.clone()),
121 }
122 }
123
124 pub fn get_type_name(&self) -> &str {
132 match self {
133 Self::Boolean(_) => ValueType::Boolean.name(),
134 Self::Integer(_) => ValueType::Integer.name(),
135 Self::Double(_) => ValueType::Double.name(),
136 Self::String(_) => ValueType::String.name(),
137 Self::Decimal(_) => ValueType::Decimal.name(),
138 Self::Date(_) => ValueType::Date.name(),
139 Self::Datetime(_) => ValueType::Datetime.name(),
140 Self::DatetimeTZ(_) => ValueType::DatetimeTZ.name(),
141 Self::Duration(_) => ValueType::Duration.name(),
142 Self::Struct(_, struct_type_name) => struct_type_name,
143 }
144 }
145
146 pub fn get_boolean(&self) -> Option<bool> {
147 if let Value::Boolean(bool) = self { Some(*bool) } else { None }
148 }
149
150 pub fn get_integer(&self) -> Option<i64> {
151 if let Value::Integer(integer) = self { Some(*integer) } else { None }
152 }
153
154 pub fn get_double(&self) -> Option<f64> {
155 if let Value::Double(double) = self { Some(*double) } else { None }
156 }
157
158 pub fn get_string(&self) -> Option<&str> {
159 if let Value::String(string) = self { Some(&**string) } else { None }
160 }
161
162 pub fn get_decimal(&self) -> Option<Decimal> {
163 if let Value::Decimal(decimal) = self { Some(*decimal) } else { None }
164 }
165
166 pub fn get_date(&self) -> Option<NaiveDate> {
167 if let Value::Date(naive_date) = self { Some(*naive_date) } else { None }
168 }
169
170 pub fn get_datetime(&self) -> Option<NaiveDateTime> {
171 if let Value::Datetime(datetime) = self { Some(*datetime) } else { None }
172 }
173
174 pub fn get_datetime_tz(&self) -> Option<DateTime<TimeZone>> {
175 if let Value::DatetimeTZ(datetime_tz) = self { Some(*datetime_tz) } else { None }
176 }
177
178 pub fn get_duration(&self) -> Option<Duration> {
179 if let Value::Duration(duration) = self { Some(*duration) } else { None }
180 }
181
182 pub fn get_struct(&self) -> Option<&Struct> {
183 if let Value::Struct(struct_, _) = self { Some(struct_) } else { None }
184 }
185}
186
187impl fmt::Display for Value {
188 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189 match self {
190 Self::Boolean(bool) => write!(f, "{}", bool),
191 Self::Integer(integer) => write!(f, "{}", integer),
192 Self::Double(double) => write!(f, "{}", double),
193 Self::String(string) => write!(f, "\"{}\"", string),
194 Self::Decimal(decimal) => write!(f, "{}", decimal),
195 Self::Date(date) => write!(f, "{}", date.format("%Y-%m-%d")),
196 Self::Datetime(datetime) => write!(f, "{}", datetime.format("%FT%T%.9f")),
197 Self::DatetimeTZ(datetime_tz) => match datetime_tz.timezone() {
198 TimeZone::IANA(tz) => write!(f, "{} {}", datetime_tz.format("%FT%T%.9f"), tz.name()),
199 TimeZone::Fixed(_) => write!(f, "{}", datetime_tz.format("%FT%T%.9f%:z")),
200 },
201 Self::Duration(duration) => write!(f, "{}", duration),
202 Self::Struct(value, name) => write!(f, "{} {}", name, value),
203 }
204 }
205}
206
207impl fmt::Debug for Value {
208 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209 write!(f, "{}: ", self.get_type_name())?;
210 match self {
211 Value::Boolean(bool) => write!(f, "{}", bool),
212 Value::Integer(integer) => write!(f, "{}", integer),
213 Value::Double(double) => write!(f, "{}", double),
214 Value::Decimal(decimal) => write!(f, "{}", decimal),
215 Value::String(string) => write!(f, "\"{}\"", string),
216 Value::Date(date) => write!(f, "{}", date),
217 Value::Datetime(datetime) => write!(f, "{}", datetime),
218 Value::DatetimeTZ(datetime_tz) => write!(f, "{}", datetime_tz),
219 Value::Duration(duration) => write!(f, "{}", duration),
220 Value::Struct(value, name) => write!(f, "{} {}", name, value),
221 }
222 }
223}
224
225impl From<bool> for Value {
226 fn from(value: bool) -> Self {
227 Self::Boolean(value)
228 }
229}
230
231impl From<i64> for Value {
232 fn from(value: i64) -> Self {
233 Self::Integer(value)
234 }
235}
236
237impl From<f64> for Value {
238 fn from(value: f64) -> Self {
239 Self::Double(value)
240 }
241}
242
243impl From<Decimal> for Value {
244 fn from(value: Decimal) -> Self {
245 Self::Decimal(value)
246 }
247}
248
249impl From<String> for Value {
250 fn from(value: String) -> Self {
251 Self::String(value)
252 }
253}
254
255impl From<&str> for Value {
256 fn from(value: &str) -> Self {
257 Self::String(value.to_owned())
258 }
259}
260
261impl From<NaiveDate> for Value {
262 fn from(value: NaiveDate) -> Self {
263 Self::Date(value)
264 }
265}
266
267impl From<NaiveDateTime> for Value {
268 fn from(value: NaiveDateTime) -> Self {
269 Self::Datetime(value)
270 }
271}
272
273impl From<DateTime<TimeZone>> for Value {
274 fn from(value: DateTime<TimeZone>) -> Self {
275 Self::DatetimeTZ(value)
276 }
277}
278
279impl From<Duration> for Value {
280 fn from(value: Duration) -> Self {
281 Self::Duration(value)
282 }
283}
284
285#[repr(C)]
288#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
289pub struct Decimal {
290 pub integer: i64,
292 pub fractional: u64,
295}
296
297impl Decimal {
298 const FRACTIONAL_PART_DENOMINATOR_LOG10: u32 = 19;
299 pub const FRACTIONAL_PART_DENOMINATOR: u64 = 10u64.pow(Decimal::FRACTIONAL_PART_DENOMINATOR_LOG10);
300 pub const MIN: Self = Self::from_parts(i64::MIN, 0);
301 pub const MAX: Self = Self::from_parts(i64::MAX, Decimal::FRACTIONAL_PART_DENOMINATOR - 1);
302
303 pub const fn from_parts(integer: i64, fractional: u64) -> Self {
307 assert!(fractional < Decimal::FRACTIONAL_PART_DENOMINATOR);
308 Self { integer, fractional }
309 }
310}
311
312impl Neg for Decimal {
313 type Output = Self;
314
315 fn neg(self) -> Self::Output {
316 Self::default() - self
317 }
318}
319
320impl Add for Decimal {
321 type Output = Self;
322
323 fn add(self, rhs: Self) -> Self::Output {
324 let lhs = self;
325 let (fractional, carry) = match lhs.fractional.overflowing_add(rhs.fractional) {
326 (frac, false) if frac < Self::FRACTIONAL_PART_DENOMINATOR => (frac, 0),
327 (frac, true) if frac < Self::FRACTIONAL_PART_DENOMINATOR => {
328 (frac + 0u64.wrapping_sub(Self::FRACTIONAL_PART_DENOMINATOR), 1)
329 }
330 (frac, false) => (frac - Self::FRACTIONAL_PART_DENOMINATOR, 1),
331 (_, true) => unreachable!(),
332 };
333 let integer = lhs.integer + rhs.integer + carry;
334
335 Self::from_parts(integer, fractional)
336 }
337}
338
339impl Sub for Decimal {
340 type Output = Self;
341
342 fn sub(self, rhs: Self) -> Self::Output {
343 let lhs = self;
344 let (fractional, carry) = match lhs.fractional.overflowing_sub(rhs.fractional) {
345 (frac, false) => (frac, 0),
346 (frac, true) => (frac.wrapping_add(Self::FRACTIONAL_PART_DENOMINATOR), 1),
347 };
348 let integer = lhs.integer - rhs.integer - carry;
349
350 Self::from_parts(integer, fractional)
351 }
352}
353
354impl FromStr for Decimal {
355 type Err = Error;
356
357 fn from_str(mut str: &str) -> Result<Self, Self::Err> {
358 if str.ends_with("dec") {
359 str = str.trim_end_matches("dec");
360 }
361 let (str, is_negative) = if str.starts_with("-") {
362 (&str[1..], true)
363 } else if str.starts_with("+") {
364 (&str[1..], false)
365 } else {
366 (str, false)
367 };
368
369 let (integer_part, fractional_part) = str.split_once(".").unwrap_or((str, "0"));
370 let integer = integer_part.parse().map_err(|source| {
371 let reason = format!("Error parsing integer part: {source}");
372 Error::Concept(ConceptError::ErrorParsingDecimal { unparsed: str.to_owned(), reason })
373 })?;
374 let parsed_fractional = fractional_part.parse::<u64>().map_err(|source| {
375 let reason = format!("Error parsing integer part: {source}");
376 Error::Concept(ConceptError::ErrorParsingDecimal { unparsed: str.to_owned(), reason })
377 })?;
378 let num_fractional_digits = fractional_part.len() as u32;
379 if num_fractional_digits > Self::FRACTIONAL_PART_DENOMINATOR_LOG10 {
380 let reason = "The value cannot be parsed without a loss of precision".to_owned();
381 return Err(Error::Concept(ConceptError::ErrorParsingDecimal { unparsed: str.to_owned(), reason }));
382 }
383 let fractional = parsed_fractional * 10u64.pow(Self::FRACTIONAL_PART_DENOMINATOR_LOG10 - num_fractional_digits);
384
385 if is_negative { Ok(-Self::from_parts(integer, fractional)) } else { Ok(Self::from_parts(integer, fractional)) }
386 }
387}
388
389impl fmt::Display for Decimal {
390 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
391 fmt::Debug::fmt(self, f)
392 }
393}
394
395impl fmt::Debug for Decimal {
396 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
397 if self.fractional == 0 {
398 write!(f, "{}.0", self.integer)?;
399 } else {
400 let mut tail_0s = 0;
402 let mut fractional = self.fractional;
403 while fractional.is_multiple_of(10) {
404 tail_0s += 1;
405 fractional /= 10;
406 }
407
408 let fractional_width = Self::FRACTIONAL_PART_DENOMINATOR_LOG10 - tail_0s;
409 write!(f, "{}.{:0width$}dec", self.integer, fractional, width = fractional_width as usize)?;
410 }
411 Ok(())
412 }
413}
414
415#[derive(Copy, Clone, Debug)]
417pub enum Offset {
418 IANA(<Tz as chrono::TimeZone>::Offset),
419 Fixed(FixedOffset),
420}
421
422impl chrono::Offset for Offset {
423 fn fix(&self) -> FixedOffset {
424 match self {
425 Self::IANA(inner) => inner.fix(),
426 Self::Fixed(inner) => inner.fix(),
427 }
428 }
429}
430
431impl fmt::Display for Offset {
432 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
433 match self {
434 Self::IANA(inner) => fmt::Display::fmt(inner, f),
435 Self::Fixed(inner) => fmt::Display::fmt(inner, f),
436 }
437 }
438}
439
440#[derive(Copy, Clone, Debug)]
442pub enum TimeZone {
443 IANA(Tz),
444 Fixed(FixedOffset),
445}
446
447impl Default for TimeZone {
448 fn default() -> Self {
449 Self::IANA(Tz::default())
450 }
451}
452
453impl chrono::TimeZone for TimeZone {
454 type Offset = Offset;
455
456 fn from_offset(offset: &Self::Offset) -> Self {
457 match offset {
458 Offset::IANA(offset) => Self::IANA(Tz::from_offset(offset)),
459 Offset::Fixed(offset) => Self::Fixed(FixedOffset::from_offset(offset)),
460 }
461 }
462
463 fn offset_from_local_date(&self, local: &NaiveDate) -> MappedLocalTime<Self::Offset> {
464 match self {
465 Self::IANA(inner) => inner.offset_from_local_date(local).map(Offset::IANA),
466 Self::Fixed(inner) => inner.offset_from_local_date(local).map(Offset::Fixed),
467 }
468 }
469
470 fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> MappedLocalTime<Self::Offset> {
471 match self {
472 Self::IANA(inner) => inner.offset_from_local_datetime(local).map(Offset::IANA),
473 Self::Fixed(inner) => inner.offset_from_local_datetime(local).map(Offset::Fixed),
474 }
475 }
476
477 fn offset_from_utc_date(&self, utc: &NaiveDate) -> Self::Offset {
478 match self {
479 TimeZone::IANA(inner) => Offset::IANA(inner.offset_from_utc_date(utc)),
480 TimeZone::Fixed(inner) => Offset::Fixed(inner.offset_from_utc_date(utc)),
481 }
482 }
483
484 fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> Self::Offset {
485 match self {
486 TimeZone::IANA(inner) => Offset::IANA(inner.offset_from_utc_datetime(utc)),
487 TimeZone::Fixed(inner) => Offset::Fixed(inner.offset_from_utc_datetime(utc)),
488 }
489 }
490}
491
492#[repr(C)]
496#[derive(Clone, Copy, Hash, PartialEq, Eq)]
497pub struct Duration {
498 pub months: u32,
500 pub days: u32,
502 pub nanos: u64,
504}
505
506impl Duration {
507 pub const NANOS_PER_SEC: u64 = 1_000_000_000;
509 #[doc(hidden)]
510 pub const NANOS_PER_MINUTE: u64 = 60 * Self::NANOS_PER_SEC;
512 #[doc(hidden)]
513 pub const NANOS_PER_HOUR: u64 = 60 * 60 * Self::NANOS_PER_SEC;
515 pub const DAYS_PER_WEEK: u32 = 7;
517 pub const MONTHS_PER_YEAR: u32 = 12;
519
520 pub fn new(months: u32, days: u32, nanos: u64) -> Self {
521 Self { months, days, nanos }
522 }
523
524 fn is_empty(&self) -> bool {
525 self.months == 0 && self.days == 0 && self.nanos == 0
526 }
527}
528
529impl TryFrom<Duration> for chrono::Duration {
530 type Error = crate::Error;
531
532 fn try_from(duration: Duration) -> Result<Self, Self::Error> {
533 if duration.months != 0 || duration.days != 0 {
534 Err(Error::Other(String::from(
535 "Converting TypeDB duration to chrono::Duration is only possible when months and days are not set.",
536 )))
537 } else {
538 match i64::try_from(duration.nanos) {
539 Ok(nanos) => Ok(chrono::Duration::nanoseconds(nanos)),
540 Err(_) => {
541 Err(Error::Other(String::from("Duration u64 nanos exceeded i64 required for chrono::Duration")))
542 }
543 }
544 }
545 }
546}
547
548#[derive(Debug)]
550pub struct DurationParseError;
551
552struct Segment {
553 number: u64,
554 symbol: u8,
555 number_len: usize,
556}
557
558fn read_u32(str: &str) -> Result<(Segment, &str), DurationParseError> {
559 let mut i = 0;
560 while i + 1 < str.len() && str.as_bytes()[i].is_ascii_digit() {
561 i += 1;
562 }
563 if i == 0 {
564 return Err(DurationParseError);
565 }
566 let value = str[..i].parse().map_err(|_| DurationParseError)?;
567 Ok((Segment { number: value, symbol: str.as_bytes()[i], number_len: i }, &str[i + 1..]))
568}
569
570impl FromStr for Duration {
571 type Err = DurationParseError;
572
573 fn from_str(mut str: &str) -> Result<Self, Self::Err> {
574 let mut months = 0;
575 let mut days = 0;
576 let mut nanos = 0;
577
578 if str.as_bytes()[0] != b'P' {
579 return Err(DurationParseError);
580 }
581 str = &str[1..];
582
583 let mut parsing_time = false;
584 let mut previous_symbol = None;
585 while !str.is_empty() {
586 if str.as_bytes()[0] == b'T' {
587 parsing_time = true;
588 str = &str[1..];
589 }
590
591 let (Segment { number, symbol, number_len }, tail) = read_u32(str)?;
592 str = tail;
593
594 if previous_symbol == Some(b'.') && symbol != b'S' {
595 return Err(DurationParseError);
596 }
597
598 match symbol {
599 b'Y' => months += number as u32 * Self::MONTHS_PER_YEAR,
600 b'M' if !parsing_time => months += number as u32,
601
602 b'W' => days += number as u32 * Self::DAYS_PER_WEEK,
603 b'D' => days += number as u32,
604
605 b'H' => nanos += number * Self::NANOS_PER_HOUR,
606 b'M' if parsing_time => nanos += number * Self::NANOS_PER_MINUTE,
607 b'.' => nanos += number * Self::NANOS_PER_SEC,
608 b'S' if previous_symbol != Some(b'.') => nanos += number * Self::NANOS_PER_SEC,
609 b'S' if previous_symbol == Some(b'.') => nanos += number * 10u64.pow(9 - number_len as u32),
610 _ => return Err(DurationParseError),
611 }
612 previous_symbol = Some(symbol);
613 }
614
615 Ok(Self { months, days, nanos })
616 }
617}
618
619impl fmt::Display for Duration {
621 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
622 if self.is_empty() {
623 f.write_str("PT0S")?;
624 return Ok(());
625 }
626
627 write!(f, "P")?;
628
629 if self.months > 0 || self.days > 0 {
630 let years = self.months / Self::MONTHS_PER_YEAR;
631 let months = self.months % Self::MONTHS_PER_YEAR;
632 let days = self.days;
633 if years > 0 {
634 write!(f, "{years}Y")?;
635 }
636 if months > 0 {
637 write!(f, "{months}M")?;
638 }
639 if days > 0 {
640 write!(f, "{days}D")?;
641 }
642 }
643
644 if self.nanos > 0 {
645 write!(f, "T")?;
646
647 let hours = self.nanos / Self::NANOS_PER_HOUR;
648 let minutes = (self.nanos % Self::NANOS_PER_HOUR) / Self::NANOS_PER_MINUTE;
649 let seconds = (self.nanos % Self::NANOS_PER_MINUTE) / Self::NANOS_PER_SEC;
650 let nanos = self.nanos % Self::NANOS_PER_SEC;
651
652 if hours > 0 {
653 write!(f, "{hours}H")?;
654 }
655 if minutes > 0 {
656 write!(f, "{minutes}M")?;
657 }
658 if seconds > 0 && nanos == 0 {
659 write!(f, "{seconds}S")?;
660 } else if nanos > 0 {
661 write!(f, "{seconds}.{nanos:09}S")?;
662 }
663 }
664
665 Ok(())
666 }
667}
668
669impl fmt::Debug for Duration {
670 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
671 write!(f, "months: {}, days: {}, nanos: {}", self.months, self.days, self.nanos)
672 }
673}
674
675#[derive(Clone, PartialEq)]
676pub struct Struct {
677 pub(crate) fields: HashMap<String, Option<Value>>,
678}
679
680impl Struct {
681 pub fn fields(&self) -> &HashMap<String, Option<Value>> {
682 &self.fields
683 }
684}
685
686impl fmt::Display for Struct {
687 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
688 fmt::Debug::fmt(self, f)
689 }
690}
691
692impl fmt::Debug for Struct {
693 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
694 write!(f, "{:?}", self.fields)
695 }
696}