qraft-core 0.1.2

Core type system, query model, decoding, and SQL lowering primitives for qraft.
Documentation
use std::{borrow::Cow, rc::Rc, sync::Arc};

#[cfg(feature = "chrono")]
use chrono::{DateTime, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime, Utc};
use quex::{
    Date as QuexDate, DateTime as QuexDateTime, DateTimeTz as QuexDateTimeTz, Encode,
    Time as QuexTime,
};
#[cfg(feature = "serde_json")]
use serde_json::Value as JsonValue;
#[cfg(feature = "uuid")]
use uuid::Uuid;

use crate::{
    BigInt, Blob, Bool, Date, Double, Float, Int, Nullable, Text, Time, Timestamp, UBigInt, UInt,
    expression::{AsExpression, Expression},
    lower::LowerCtx,
    param::encode_param,
    ty::TypeMeta,
};

/// Default SQL marker type for a Rust value.
pub trait DefaultMeta {
    type Meta: TypeMeta;
}

/// Explicit typed compatibility between a Rust value and a qraft SQL type.
pub trait Compatible<T: TypeMeta>: Encode {}

/// Internal lowering bridge used by operator APIs.
#[doc(hidden)]
pub trait LowerCompatible<T: TypeMeta> {
    fn lower_compatible(&self, ctx: &mut LowerCtx) -> usize;
}

impl<T> DefaultMeta for Option<T>
where
    T: DefaultMeta,
{
    type Meta = Nullable<T::Meta>;
}

impl<T> DefaultMeta for &T
where
    T: DefaultMeta + ?Sized,
{
    type Meta = T::Meta;
}

impl<T, U> Compatible<T> for &U
where
    T: TypeMeta,
    U: Compatible<T> + ?Sized,
{
}

impl<T, V> LowerCompatible<T> for V
where
    T: TypeMeta,
    V: Compatible<T> + ?Sized,
{
    fn lower_compatible(&self, ctx: &mut LowerCtx) -> usize {
        let param = encode_param(self, ctx.data);
        ctx.lower_param(param)
    }
}

macro_rules! impl_text_lower_compatible {
    ($($ty:ty),+ $(,)?) => {
        $(
            impl LowerCompatible<Text> for $ty {
                fn lower_compatible(&self, ctx: &mut LowerCtx) -> usize {
                    let param = encode_param(self.as_ref(), ctx.data);
                    ctx.lower_param(param)
                }
            }

            impl LowerCompatible<Nullable<Text>> for $ty {
                fn lower_compatible(&self, ctx: &mut LowerCtx) -> usize {
                    let param = encode_param(self.as_ref(), ctx.data);
                    ctx.lower_param(param)
                }
            }
        )+
    };
}

impl_text_lower_compatible!(Box<str>, Arc<str>, Rc<str>, Cow<'_, str>);

macro_rules! impl_blob_lower_compatible {
    ($($ty:ty),+ $(,)?) => {
        $(
            impl LowerCompatible<Blob> for $ty {
                fn lower_compatible(&self, ctx: &mut LowerCtx) -> usize {
                    let param = encode_param(self.as_ref(), ctx.data);
                    ctx.lower_param(param)
                }
            }

            impl LowerCompatible<Nullable<Blob>> for $ty {
                fn lower_compatible(&self, ctx: &mut LowerCtx) -> usize {
                    let param = encode_param(self.as_ref(), ctx.data);
                    ctx.lower_param(param)
                }
            }
        )+
    };
}

impl_blob_lower_compatible!(Box<[u8]>, Arc<[u8]>, Rc<[u8]>, Cow<'_, [u8]>);

impl<T, U> AsExpression<T> for &U
where
    T: TypeMeta,
    U: AsExpression<T> + ?Sized,
{
    type Expression<'e>
        = U::Expression<'e>
    where
        Self: 'e;

    fn as_expression<'e>(&'e self) -> Self::Expression<'e> {
        U::as_expression(*self)
    }
}

macro_rules! impl_default_meta {
    ($meta:ty => $($ty:ty),+ $(,)?) => {
        $(impl DefaultMeta for $ty {
            type Meta = $meta;
        })+
    };
}

impl_default_meta!(Text => String, str, Box<str>, Arc<str>, Rc<str>, Cow<'_, str>);
impl_default_meta!(Blob => Vec<u8>, [u8], Box<[u8]>, Arc<[u8]>, Rc<[u8]>, Cow<'_, [u8]>);
impl_default_meta!(Bool => bool);
impl_default_meta!(Int => i8, i16, i32);
impl_default_meta!(BigInt => i64);
impl_default_meta!(UInt => u8, u16, u32);
impl_default_meta!(UBigInt => u64);
impl_default_meta!(Float => f32);
impl_default_meta!(Double => f64);
impl_default_meta!(Date => QuexDate);
impl_default_meta!(Time => QuexTime);
impl_default_meta!(Timestamp => QuexDateTime, QuexDateTimeTz);
#[cfg(feature = "chrono")]
impl_default_meta!(Date => NaiveDate);
#[cfg(feature = "chrono")]
impl_default_meta!(Time => NaiveTime);
#[cfg(feature = "chrono")]
impl_default_meta!(Timestamp => NaiveDateTime, DateTime<Utc>, DateTime<FixedOffset>);
#[cfg(feature = "uuid")]
impl_default_meta!(Text => Uuid);
#[cfg(feature = "serde_json")]
impl_default_meta!(Text => JsonValue);

macro_rules! impl_compatible_exact {
    ($sql:ty => $($ty:ty),+ $(,)?) => {
        $(
            impl Compatible<$sql> for $ty {}
            impl Compatible<Nullable<$sql>> for $ty {}
        )+
    };
}

impl_compatible_exact!(Int => i8, i16, i32);
impl_compatible_exact!(BigInt => i64);
impl_compatible_exact!(UInt => u8, u16, u32);
impl_compatible_exact!(UBigInt => u64);
impl_compatible_exact!(Bool => bool);
impl_compatible_exact!(Float => f32);
impl_compatible_exact!(Double => f64);
impl_compatible_exact!(Date => QuexDate);
impl_compatible_exact!(Time => QuexTime);
impl_compatible_exact!(Timestamp => QuexDateTime, QuexDateTimeTz);

macro_rules! impl_compatible_bigint_widen {
    ($($ty:ty),+ $(,)?) => {
        $(
            impl Compatible<BigInt> for $ty {}
            impl Compatible<Nullable<BigInt>> for $ty {}
        )+
    };
}

impl_compatible_bigint_widen!(i8, i16, i32);

impl Compatible<Double> for f32 {}
impl Compatible<Nullable<Double>> for f32 {}

macro_rules! impl_text_compatible {
    ($($ty:ty),+ $(,)?) => {
        $(
            impl Compatible<Text> for $ty {}
            impl Compatible<Nullable<Text>> for $ty {}
        )+
    };
}

impl_text_compatible!(str, String);

macro_rules! impl_blob_compatible {
    ($($ty:ty),+ $(,)?) => {
        $(
            impl Compatible<Blob> for $ty {}
            impl Compatible<Nullable<Blob>> for $ty {}
        )+
    };
}

impl_blob_compatible!([u8], Vec<u8>);

#[cfg(feature = "chrono")]
impl Compatible<Date> for NaiveDate {}
#[cfg(feature = "chrono")]
impl Compatible<Nullable<Date>> for NaiveDate {}
#[cfg(feature = "chrono")]
impl Compatible<Time> for NaiveTime {}
#[cfg(feature = "chrono")]
impl Compatible<Nullable<Time>> for NaiveTime {}
#[cfg(feature = "chrono")]
impl Compatible<Timestamp> for NaiveDateTime {}
#[cfg(feature = "chrono")]
impl Compatible<Nullable<Timestamp>> for NaiveDateTime {}
#[cfg(feature = "chrono")]
impl Compatible<Timestamp> for DateTime<FixedOffset> {}
#[cfg(feature = "chrono")]
impl Compatible<Nullable<Timestamp>> for DateTime<FixedOffset> {}
#[cfg(feature = "chrono")]
impl Compatible<Timestamp> for DateTime<Utc> {}
#[cfg(feature = "chrono")]
impl Compatible<Nullable<Timestamp>> for DateTime<Utc> {}

#[cfg(feature = "uuid")]
impl Compatible<Text> for Uuid {}
#[cfg(feature = "uuid")]
impl Compatible<Nullable<Text>> for Uuid {}

#[cfg(feature = "serde_json")]
impl Compatible<Text> for JsonValue {}
#[cfg(feature = "serde_json")]
impl Compatible<Nullable<Text>> for JsonValue {}

impl<T, V> Compatible<Nullable<T>> for Option<V>
where
    T: TypeMeta,
    V: Compatible<T>,
{
}

pub struct TypedParam<'e, T: TypeMeta, V: ?Sized> {
    value: &'e V,
    marker: std::marker::PhantomData<T>,
}

impl<'e, T: TypeMeta, V: ?Sized> TypedParam<'e, T, V> {
    pub fn new(value: &'e V) -> Self {
        Self {
            value,
            marker: std::marker::PhantomData,
        }
    }
}

impl<T, V> Expression for TypedParam<'_, T, V>
where
    T: TypeMeta,
    V: Compatible<T> + ?Sized,
{
    type Type = T;

    fn lower(&self, ctx: &mut LowerCtx) -> usize {
        self.value.lower_compatible(ctx)
    }
}

macro_rules! impl_as_expression {
    ($sql:ty => $($ty:ty),+ $(,)?) => {
        $(
            impl AsExpression<$sql> for $ty {
                type Expression<'e>
                    = TypedParam<'e, $sql, $ty>
                where
                    Self: 'e;

                fn as_expression<'e>(&'e self) -> Self::Expression<'e> {
                    TypedParam::new(self)
                }
            }

            impl AsExpression<Nullable<$sql>> for $ty {
                type Expression<'e>
                    = TypedParam<'e, Nullable<$sql>, $ty>
                where
                    Self: 'e;

                fn as_expression<'e>(&'e self) -> Self::Expression<'e> {
                    TypedParam::new(self)
                }
            }
        )+
    };
}

impl_as_expression!(Bool => bool);
impl_as_expression!(Int => i8, i16, i32);
impl_as_expression!(BigInt => i8, i16, i32, i64);
impl_as_expression!(UInt => u8, u16, u32);
impl_as_expression!(UBigInt => u64);
impl_as_expression!(Float => f32);
impl_as_expression!(Double => f32, f64);
impl_as_expression!(Text => str, String);
impl_as_expression!(Blob => [u8], Vec<u8>);
impl_as_expression!(Date => QuexDate);
impl_as_expression!(Time => QuexTime);
impl_as_expression!(Timestamp => QuexDateTime, QuexDateTimeTz);
#[cfg(feature = "chrono")]
impl_as_expression!(Date => NaiveDate);
#[cfg(feature = "chrono")]
impl_as_expression!(Time => NaiveTime);
#[cfg(feature = "chrono")]
impl_as_expression!(Timestamp => NaiveDateTime, DateTime<Utc>, DateTime<FixedOffset>);
#[cfg(feature = "uuid")]
impl_as_expression!(Text => Uuid);
#[cfg(feature = "serde_json")]
impl_as_expression!(Text => JsonValue);

impl<T, V> AsExpression<Nullable<T>> for Option<V>
where
    T: TypeMeta,
    V: Compatible<T>,
{
    type Expression<'e>
        = TypedParam<'e, Nullable<T>, Option<V>>
    where
        Self: 'e;

    fn as_expression<'e>(&'e self) -> Self::Expression<'e> {
        TypedParam::new(self)
    }
}