Skip to main content

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, error::ConceptError};
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 { 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/// A fixed-point decimal number.
286/// Holds exactly 19 digits after the decimal point and a 64-bit value before the decimal point.
287#[repr(C)]
288#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
289pub struct Decimal {
290    /// The integer part of the decimal as normal signed 64 bit number
291    pub integer: i64,
292    /// The fractional part of the decimal, in multiples of 10^-19 (Decimal::FRACTIONAL_PART_DENOMINATOR).
293    /// This means that the smallest decimal representable is 10^-19, and up to 19 decimal places are supported.
294    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    /// Creates a new Decimal value from the raw integer and fractional parts.
304    /// The fractional part is specified in multiples of 10^-19 (Decimal::FRACTIONAL_PART_DENOMINATOR).
305    /// For an easier interface, use from_str.
306    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            // count number of tailing 0's that don't have to be represented
401            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/// Offset for datetime-tz. Can be retrieved from an IANA Tz or a FixedOffset.
416#[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/// TimeZone for datetime-tz. Can be represented as an IANA Tz or as a FixedOffset.
441#[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/// A relative duration, which contains months, days, and nanoseconds.
493/// Can be used for calendar-relative durations (eg 7 days forward), or for absolute durations using the nanosecond component
494/// When used as an absolute duration, convertible to chrono::Duration
495#[repr(C)]
496#[derive(Clone, Copy, Hash, PartialEq, Eq)]
497pub struct Duration {
498    /// Number of calendar months in the duration.
499    pub months: u32,
500    /// Number of calendar days in the duration.
501    pub days: u32,
502    /// Number of nanoseconds in the duration.
503    pub nanos: u64,
504}
505
506impl Duration {
507    /// Number of nanoseconds in one second.
508    pub const NANOS_PER_SEC: u64 = 1_000_000_000;
509    #[doc(hidden)]
510    /// cbindgen:ignore
511    pub const NANOS_PER_MINUTE: u64 = 60 * Self::NANOS_PER_SEC;
512    #[doc(hidden)]
513    /// cbindgen:ignore
514    pub const NANOS_PER_HOUR: u64 = 60 * 60 * Self::NANOS_PER_SEC;
515    /// Number of days in one week.
516    pub const DAYS_PER_WEEK: u32 = 7;
517    /// Number of months in one year.
518    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// TODO: Duration parsing is basically a copy of TypeDB server's code, can be made a dependency.
549#[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
619/// ISO-8601 compliant representation of a duration
620impl 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}