typedb_driver/concept/
value.rs

1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements.  See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership.  The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License.  You may obtain a copy of the License at
9 *
10 *   http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied.  See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20use 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;
31
32/// Represents the type of primitive value is held by a Value or Attribute.
33#[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    /// Retrieves the `ValueType` of this value concept.
103    ///
104    /// # Examples
105    ///
106    /// ```rust
107    /// value.get_type();
108    /// ```
109    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    /// Retrieves the name of the `ValueType` of this value concept.
125    ///
126    /// # Examples
127    ///
128    /// ```rust
129    /// value.get_type_name();
130    /// ```
131    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 {
148            Some(*bool)
149        } else {
150            None
151        }
152    }
153
154    pub fn get_integer(&self) -> Option<i64> {
155        if let Value::Integer(integer) = self {
156            Some(*integer)
157        } else {
158            None
159        }
160    }
161
162    pub fn get_double(&self) -> Option<f64> {
163        if let Value::Double(double) = self {
164            Some(*double)
165        } else {
166            None
167        }
168    }
169
170    pub fn get_string(&self) -> Option<&str> {
171        if let Value::String(string) = self {
172            Some(&**string)
173        } else {
174            None
175        }
176    }
177
178    pub fn get_decimal(&self) -> Option<Decimal> {
179        if let Value::Decimal(decimal) = self {
180            Some(*decimal)
181        } else {
182            None
183        }
184    }
185
186    pub fn get_date(&self) -> Option<NaiveDate> {
187        if let Value::Date(naive_date) = self {
188            Some(*naive_date)
189        } else {
190            None
191        }
192    }
193
194    pub fn get_datetime(&self) -> Option<NaiveDateTime> {
195        if let Value::Datetime(datetime) = self {
196            Some(*datetime)
197        } else {
198            None
199        }
200    }
201
202    pub fn get_datetime_tz(&self) -> Option<DateTime<TimeZone>> {
203        if let Value::DatetimeTZ(datetime_tz) = self {
204            Some(*datetime_tz)
205        } else {
206            None
207        }
208    }
209
210    pub fn get_duration(&self) -> Option<Duration> {
211        if let Value::Duration(duration) = self {
212            Some(*duration)
213        } else {
214            None
215        }
216    }
217
218    pub fn get_struct(&self) -> Option<&Struct> {
219        if let Value::Struct(struct_, _) = self {
220            Some(struct_)
221        } else {
222            None
223        }
224    }
225}
226
227impl fmt::Display for Value {
228    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
229        match self {
230            Self::Boolean(bool) => write!(f, "{}", bool),
231            Self::Integer(integer) => write!(f, "{}", integer),
232            Self::Double(double) => write!(f, "{}", double),
233            Self::String(string) => write!(f, "\"{}\"", string),
234            Self::Decimal(decimal) => write!(f, "{}", decimal),
235            Self::Date(date) => write!(f, "{}", date.format("%Y-%m-%d")),
236            Self::Datetime(datetime) => write!(f, "{}", datetime.format("%FT%T%.9f")),
237            Self::DatetimeTZ(datetime_tz) => match datetime_tz.timezone() {
238                TimeZone::IANA(tz) => write!(f, "{} {}", datetime_tz.format("%FT%T%.9f"), tz.name()),
239                TimeZone::Fixed(_) => write!(f, "{}", datetime_tz.format("%FT%T%.9f%:z")),
240            },
241            Self::Duration(duration) => write!(f, "{}", duration),
242            Self::Struct(value, name) => write!(f, "{} {}", name, value),
243        }
244    }
245}
246
247impl fmt::Debug for Value {
248    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249        write!(f, "{}: ", self.get_type_name())?;
250        match self {
251            Value::Boolean(bool) => write!(f, "{}", bool),
252            Value::Integer(integer) => write!(f, "{}", integer),
253            Value::Double(double) => write!(f, "{}", double),
254            Value::Decimal(decimal) => write!(f, "{}", decimal),
255            Value::String(string) => write!(f, "\"{}\"", string),
256            Value::Date(date) => write!(f, "{}", date),
257            Value::Datetime(datetime) => write!(f, "{}", datetime),
258            Value::DatetimeTZ(datetime_tz) => write!(f, "{}", datetime_tz),
259            Value::Duration(duration) => write!(f, "{}", duration),
260            Value::Struct(value, name) => write!(f, "{} {}", name, value),
261        }
262    }
263}
264
265/// A fixed-point decimal number.
266/// Holds exactly 19 digits after the decimal point and a 64-bit value before the decimal point.
267#[repr(C)]
268#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
269pub struct Decimal {
270    integer: i64,
271    fractional: u64,
272}
273
274impl Decimal {
275    const FRACTIONAL_PART_DENOMINATOR_LOG10: u32 = 19;
276    pub const FRACTIONAL_PART_DENOMINATOR: u64 = 10u64.pow(Decimal::FRACTIONAL_PART_DENOMINATOR_LOG10);
277    pub const MIN: Self = Self::new(i64::MIN, 0);
278    pub const MAX: Self = Self::new(i64::MAX, Decimal::FRACTIONAL_PART_DENOMINATOR - 1);
279
280    pub const fn new(integer: i64, fractional: u64) -> Self {
281        assert!(fractional < Decimal::FRACTIONAL_PART_DENOMINATOR);
282        Self { integer, fractional }
283    }
284
285    /// Get the integer part of the decimal as normal signed 64 bit number
286    pub fn integer_part(&self) -> i64 {
287        self.integer
288    }
289
290    /// Get the fractional part of the decimal, in multiples of 10^-19 (Decimal::FRACTIONAL_PART_DENOMINATOR)
291    /// This means, the smallest decimal representable is 10^-19, and up to 19 decimal places are supported.
292    pub fn fractional_part(&self) -> u64 {
293        self.fractional
294    }
295}
296
297impl Neg for Decimal {
298    type Output = Self;
299
300    fn neg(self) -> Self::Output {
301        Self::default() - self
302    }
303}
304
305impl Add for Decimal {
306    type Output = Self;
307
308    fn add(self, rhs: Self) -> Self::Output {
309        let lhs = self;
310        let (fractional, carry) = match lhs.fractional.overflowing_add(rhs.fractional) {
311            (frac, false) if frac < Self::FRACTIONAL_PART_DENOMINATOR => (frac, 0),
312            (frac, true) if frac < Self::FRACTIONAL_PART_DENOMINATOR => {
313                (frac + 0u64.wrapping_sub(Self::FRACTIONAL_PART_DENOMINATOR), 1)
314            }
315            (frac, false) => (frac - Self::FRACTIONAL_PART_DENOMINATOR, 1),
316            (_, true) => unreachable!(),
317        };
318        let integer = lhs.integer + rhs.integer + carry;
319
320        Self::new(integer, fractional)
321    }
322}
323
324impl Sub for Decimal {
325    type Output = Self;
326
327    fn sub(self, rhs: Self) -> Self::Output {
328        let lhs = self;
329        let (fractional, carry) = match lhs.fractional.overflowing_sub(rhs.fractional) {
330            (frac, false) => (frac, 0),
331            (frac, true) => (frac.wrapping_add(Self::FRACTIONAL_PART_DENOMINATOR), 1),
332        };
333        let integer = lhs.integer - rhs.integer - carry;
334
335        Self::new(integer, fractional)
336    }
337}
338
339impl fmt::Display for Decimal {
340    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
341        fmt::Debug::fmt(self, f)
342    }
343}
344
345impl fmt::Debug for Decimal {
346    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
347        if self.fractional == 0 {
348            write!(f, "{}.0", self.integer_part())?;
349        } else {
350            // count number of tailing 0's that don't have to be represented
351            let mut tail_0s = 0;
352            let mut fractional = self.fractional;
353            while fractional % 10 == 0 {
354                tail_0s += 1;
355                fractional /= 10;
356            }
357
358            let fractional_width = Self::FRACTIONAL_PART_DENOMINATOR_LOG10 - tail_0s;
359            write!(f, "{}.{:0width$}dec", self.integer_part(), fractional, width = fractional_width as usize)?;
360        }
361        Ok(())
362    }
363}
364
365/// Offset for datetime-tz. Can be retrieved from an IANA Tz or a FixedOffset.
366#[derive(Copy, Clone, Debug)]
367pub enum Offset {
368    IANA(<Tz as chrono::TimeZone>::Offset),
369    Fixed(FixedOffset),
370}
371
372impl chrono::Offset for Offset {
373    fn fix(&self) -> FixedOffset {
374        match self {
375            Self::IANA(inner) => inner.fix(),
376            Self::Fixed(inner) => inner.fix(),
377        }
378    }
379}
380
381impl fmt::Display for Offset {
382    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
383        match self {
384            Self::IANA(inner) => fmt::Display::fmt(inner, f),
385            Self::Fixed(inner) => fmt::Display::fmt(inner, f),
386        }
387    }
388}
389
390/// TimeZone for datetime-tz. Can be represented as an IANA Tz or as a FixedOffset.
391#[derive(Copy, Clone, Debug)]
392pub enum TimeZone {
393    IANA(Tz),
394    Fixed(FixedOffset),
395}
396
397impl Default for TimeZone {
398    fn default() -> Self {
399        Self::IANA(Tz::default())
400    }
401}
402
403impl chrono::TimeZone for TimeZone {
404    type Offset = Offset;
405
406    fn from_offset(offset: &Self::Offset) -> Self {
407        match offset {
408            Offset::IANA(offset) => Self::IANA(Tz::from_offset(offset)),
409            Offset::Fixed(offset) => Self::Fixed(FixedOffset::from_offset(offset)),
410        }
411    }
412
413    fn offset_from_local_date(&self, local: &NaiveDate) -> MappedLocalTime<Self::Offset> {
414        match self {
415            Self::IANA(inner) => inner.offset_from_local_date(local).map(Offset::IANA),
416            Self::Fixed(inner) => inner.offset_from_local_date(local).map(Offset::Fixed),
417        }
418    }
419
420    fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> MappedLocalTime<Self::Offset> {
421        match self {
422            Self::IANA(inner) => inner.offset_from_local_datetime(local).map(Offset::IANA),
423            Self::Fixed(inner) => inner.offset_from_local_datetime(local).map(Offset::Fixed),
424        }
425    }
426
427    fn offset_from_utc_date(&self, utc: &NaiveDate) -> Self::Offset {
428        match self {
429            TimeZone::IANA(inner) => Offset::IANA(inner.offset_from_utc_date(utc)),
430            TimeZone::Fixed(inner) => Offset::Fixed(inner.offset_from_utc_date(utc)),
431        }
432    }
433
434    fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> Self::Offset {
435        match self {
436            TimeZone::IANA(inner) => Offset::IANA(inner.offset_from_utc_datetime(utc)),
437            TimeZone::Fixed(inner) => Offset::Fixed(inner.offset_from_utc_datetime(utc)),
438        }
439    }
440}
441
442/// A relative duration, which contains months, days, and nanoseconds.
443/// Can be used for calendar-relative durations (eg 7 days forward), or for absolute durations using the nanosecond component
444/// When used as an absolute duration, convertible to chrono::Duration
445#[repr(C)]
446#[derive(Clone, Copy, Hash, PartialEq, Eq)]
447pub struct Duration {
448    pub months: u32,
449    pub days: u32,
450    pub nanos: u64,
451}
452
453impl Duration {
454    const NANOS_PER_SEC: u64 = 1_000_000_000;
455    const NANOS_PER_MINUTE: u64 = 60 * Self::NANOS_PER_SEC;
456    const NANOS_PER_HOUR: u64 = 60 * 60 * Self::NANOS_PER_SEC;
457    const DAYS_PER_WEEK: u32 = 7;
458    const MONTHS_PER_YEAR: u32 = 12;
459
460    pub fn new(months: u32, days: u32, nanos: u64) -> Self {
461        Self { months, days, nanos }
462    }
463
464    pub fn months(&self) -> u32 {
465        self.months
466    }
467
468    pub fn days(&self) -> u32 {
469        self.days
470    }
471
472    pub fn nanos(&self) -> u64 {
473        self.nanos
474    }
475
476    fn is_empty(&self) -> bool {
477        self.months == 0 && self.days == 0 && self.nanos == 0
478    }
479}
480
481impl TryFrom<Duration> for chrono::Duration {
482    type Error = crate::Error;
483
484    fn try_from(duration: Duration) -> Result<Self, Self::Error> {
485        if duration.months != 0 || duration.days != 0 {
486            Err(Error::Other(String::from(
487                "Converting TypeDB duration to chrono::Duration is only possible when months and days are not set.",
488            )))
489        } else {
490            match i64::try_from(duration.nanos) {
491                Ok(nanos) => Ok(chrono::Duration::nanoseconds(nanos)),
492                Err(_) => {
493                    Err(Error::Other(String::from("Duration u64 nanos exceeded i64 required for chrono::Duration")))
494                }
495            }
496        }
497    }
498}
499
500// TODO: Duration parsing is basically a copy of TypeDB server's code, can be made a dependency.
501#[derive(Debug)]
502pub struct DurationParseError;
503
504struct Segment {
505    number: u64,
506    symbol: u8,
507    number_len: usize,
508}
509
510fn read_u32(str: &str) -> Result<(Segment, &str), DurationParseError> {
511    let mut i = 0;
512    while i + 1 < str.len() && str.as_bytes()[i].is_ascii_digit() {
513        i += 1;
514    }
515    if i == 0 {
516        return Err(DurationParseError);
517    }
518    let value = str[..i].parse().map_err(|_| DurationParseError)?;
519    Ok((Segment { number: value, symbol: str.as_bytes()[i], number_len: i }, &str[i + 1..]))
520}
521
522impl FromStr for Duration {
523    type Err = DurationParseError;
524
525    fn from_str(mut str: &str) -> Result<Self, Self::Err> {
526        let mut months = 0;
527        let mut days = 0;
528        let mut nanos = 0;
529
530        if str.as_bytes()[0] != b'P' {
531            return Err(DurationParseError);
532        }
533        str = &str[1..];
534
535        let mut parsing_time = false;
536        let mut previous_symbol = None;
537        while !str.is_empty() {
538            if str.as_bytes()[0] == b'T' {
539                parsing_time = true;
540                str = &str[1..];
541            }
542
543            let (Segment { number, symbol, number_len }, tail) = read_u32(str)?;
544            str = tail;
545
546            if previous_symbol == Some(b'.') && symbol != b'S' {
547                return Err(DurationParseError);
548            }
549
550            match symbol {
551                b'Y' => months += number as u32 * Self::MONTHS_PER_YEAR,
552                b'M' if !parsing_time => months += number as u32,
553
554                b'W' => days += number as u32 * Self::DAYS_PER_WEEK,
555                b'D' => days += number as u32,
556
557                b'H' => nanos += number * Self::NANOS_PER_HOUR,
558                b'M' if parsing_time => nanos += number * Self::NANOS_PER_MINUTE,
559                b'.' => nanos += number * Self::NANOS_PER_SEC,
560                b'S' if previous_symbol != Some(b'.') => nanos += number * Self::NANOS_PER_SEC,
561                b'S' if previous_symbol == Some(b'.') => nanos += number * 10u64.pow(9 - number_len as u32),
562                _ => return Err(DurationParseError),
563            }
564            previous_symbol = Some(symbol);
565        }
566
567        Ok(Self { months, days, nanos })
568    }
569}
570
571/// ISO-8601 compliant representation of a duration
572impl fmt::Display for Duration {
573    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
574        if self.is_empty() {
575            f.write_str("PT0S")?;
576            return Ok(());
577        }
578
579        write!(f, "P")?;
580
581        if self.months > 0 || self.days > 0 {
582            let years = self.months / Self::MONTHS_PER_YEAR;
583            let months = self.months % Self::MONTHS_PER_YEAR;
584            let days = self.days;
585            if years > 0 {
586                write!(f, "{years}Y")?;
587            }
588            if months > 0 {
589                write!(f, "{months}M")?;
590            }
591            if days > 0 {
592                write!(f, "{days}D")?;
593            }
594        }
595
596        if self.nanos > 0 {
597            write!(f, "T")?;
598
599            let hours = self.nanos / Self::NANOS_PER_HOUR;
600            let minutes = (self.nanos % Self::NANOS_PER_HOUR) / Self::NANOS_PER_MINUTE;
601            let seconds = (self.nanos % Self::NANOS_PER_MINUTE) / Self::NANOS_PER_SEC;
602            let nanos = self.nanos % Self::NANOS_PER_SEC;
603
604            if hours > 0 {
605                write!(f, "{hours}H")?;
606            }
607            if minutes > 0 {
608                write!(f, "{minutes}M")?;
609            }
610            if seconds > 0 && nanos == 0 {
611                write!(f, "{seconds}S")?;
612            } else if nanos > 0 {
613                write!(f, "{seconds}.{nanos:09}S")?;
614            }
615        }
616
617        Ok(())
618    }
619}
620
621impl fmt::Debug for Duration {
622    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
623        write!(f, "months: {}, days: {}, nanos: {}", self.months, self.days, self.nanos)
624    }
625}
626
627#[derive(Clone, PartialEq)]
628pub struct Struct {
629    pub(crate) fields: HashMap<String, Option<Value>>,
630}
631
632impl Struct {
633    pub fn fields(&self) -> &HashMap<String, Option<Value>> {
634        &self.fields
635    }
636}
637
638impl fmt::Display for Struct {
639    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
640        fmt::Debug::fmt(self, f)
641    }
642}
643
644impl fmt::Debug for Struct {
645    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
646        write!(f, "{:?}", self.fields)
647    }
648}