toasty-core 0.5.0

Core types, schema representations, and driver interface for Toasty
Documentation
use jiff::tz::TimeZone;

use crate::{
    Result,
    stmt::{Type, Value},
};

impl Type {
    /// Casts a [`Value`] to this jiff temporal type, returning the converted value.
    ///
    /// Supports conversions between:
    /// - `String` and any jiff type (parsing ISO 8601 format)
    /// - Any jiff type and `String` (formatting with fixed 9-digit nanosecond precision)
    /// - `Timestamp` and `Zoned` (via UTC timezone)
    /// - `Timestamp`/`Zoned` and `DateTime` (via UTC timezone)
    ///
    /// Returns `Ok(None)` if the conversion is not supported for this
    /// value/type combination. Returns `Err` if parsing fails.
    pub fn cast_jiff(&self, value: &Value) -> Result<Option<Value>> {
        Ok(Some(match (value, self) {
            // String -> jiff
            (Value::String(value), Type::Timestamp) => {
                let v = value.clone();
                Value::Timestamp(
                    value.parse().map_err(|_| {
                        crate::Error::type_conversion(Value::String(v), "Timestamp")
                    })?,
                )
            }
            (Value::String(value), Type::Zoned) => {
                let v = value.clone();
                Value::Zoned(
                    value
                        .parse()
                        .map_err(|_| crate::Error::type_conversion(Value::String(v), "Zoned"))?,
                )
            }
            (Value::String(value), Type::Date) => {
                let v = value.clone();
                Value::Date(
                    value
                        .parse()
                        .map_err(|_| crate::Error::type_conversion(Value::String(v), "Date"))?,
                )
            }
            (Value::String(value), Type::Time) => {
                let v = value.clone();
                Value::Time(
                    value
                        .parse()
                        .map_err(|_| crate::Error::type_conversion(Value::String(v), "Time"))?,
                )
            }
            (Value::String(value), Type::DateTime) => {
                let v = value.clone();
                Value::DateTime(
                    value
                        .parse()
                        .map_err(|_| crate::Error::type_conversion(Value::String(v), "DateTime"))?,
                )
            }

            // Identity
            (value @ Value::Timestamp(_), Type::Timestamp) => value.clone(),
            (value @ Value::Zoned(_), Type::Zoned) => value.clone(),
            (value @ Value::Date(_), Type::Date) => value.clone(),
            (value @ Value::Time(_), Type::Time) => value.clone(),
            (value @ Value::DateTime(_), Type::DateTime) => value.clone(),

            // jiff -> String
            //
            // Types with sub-second precision use fixed 9-digit nanosecond
            // formatting so that the resulting strings sort lexicographically
            // in chronological order (ISO 8601 guarantees this for
            // uniform-precision representations).
            (Value::Timestamp(value), Type::String) => Value::String(format!("{value:.9}")),
            (Value::Zoned(value), Type::String) => Value::String(format!("{value:.9}")),
            (Value::Date(value), Type::String) => Value::String(value.to_string()),
            (Value::Time(value), Type::String) => Value::String(format!("{value:.9}")),
            (Value::DateTime(value), Type::String) => Value::String(format!("{value:.9}")),

            // UTC <-> Zoned
            (Value::Timestamp(value), Type::Zoned) => Value::Zoned(value.to_zoned(TimeZone::UTC)),
            (Value::Zoned(value), Type::Timestamp) => Value::Timestamp(value.into()),

            // UTC <-> Civil
            (Value::Timestamp(value), Type::DateTime) => {
                Value::DateTime(value.to_zoned(TimeZone::UTC).into())
            }
            (Value::DateTime(value), Type::Timestamp) => Value::Timestamp(
                value
                    .to_zoned(TimeZone::UTC)
                    .expect("value was too close to minimum or maximum DateTime")
                    .into(),
            ),

            // Zoned <-> Civil
            (Value::Zoned(value), Type::DateTime) => Value::DateTime(value.into()),
            (Value::DateTime(value), Type::Zoned) => Value::Zoned(
                value
                    .to_zoned(TimeZone::UTC)
                    .expect("value was too close to minimum or maximum DateTime"),
            ),

            _ => return Ok(None),
        }))
    }
}