qraft-core 0.1.2

Core type system, query model, decoding, and SQL lowering primitives for qraft.
Documentation
//! `in (...)` predicates and lowering helpers for collections.

use crate::{
    LowerCompatible, NullOf, PredicateType, TypeMeta, UnaryType, count_idents,
    expression::Expression,
    impl_for_all_tuples,
    lower::{Instructions, LowerCtx},
    ty::Nullability,
};

/// A typed `in` or `not in` predicate.
pub struct InList<L, R> {
    lhs: L,
    rhs: R,
    negated: bool,
}

#[qraft_expression_macro::as_expression]
impl<L, R> Expression for InList<L, R>
where
    L: Expression,
    L::Type: Nullability,
    R: LowerIn<L::Type>,
    UnaryType<NullOf<L::Type>>: PredicateType,
{
    type Type = <UnaryType<NullOf<L::Type>> as PredicateType>::Output;

    fn lower(&self, ctx: &mut LowerCtx) -> usize {
        let lhs = self.lhs.lower(ctx);
        let rhs = self.rhs.lower_in(ctx);
        ctx.lower_in(self.negated, lhs, rhs)
    }
}

/// Adds `in_` and `not_in` to any expression.
pub trait In: Sized + Expression {
    fn one_of<R>(self, list: R) -> InList<Self, R>
    where
        R: LowerIn<Self::Type>,
    {
        self.in_(list)
    }

    fn in_<R>(self, list: R) -> InList<Self, R>
    where
        R: LowerIn<Self::Type>,
    {
        InList {
            lhs: self,
            rhs: list,
            negated: false,
        }
    }

    fn not_in<R>(self, list: R) -> InList<Self, R>
    where
        R: LowerIn<Self::Type>,
    {
        InList {
            lhs: self,
            rhs: list,
            negated: true,
        }
    }
}

impl<E> In for E where E: Expression {}

/// Lowers a list-like value into the right-hand side of an `in (...)` predicate.
pub trait LowerIn<T> {
    /// Appends the list contents to the lowering context and returns the subtree size.
    fn lower_in(&self, ctx: &mut LowerCtx) -> usize;
}

impl<Type, A> LowerIn<Type> for (A,)
where
    Type: TypeMeta,
    A: LowerCompatible<Type>,
{
    fn lower_in(&self, ctx: &mut LowerCtx) -> usize {
        let (a,) = self;
        let inner = a.lower_compatible(ctx);
        ctx.instrs.push_seperated(1);
        inner + 1
    }
}

impl<Type, A> LowerIn<Type> for Vec<A>
where
    Type: TypeMeta,
    A: LowerCompatible<Type>,
{
    fn lower_in(&self, ctx: &mut LowerCtx) -> usize {
        let len = self.len();
        let mut inner = 0;
        for item in self {
            inner += item.lower_compatible(ctx);
        }
        ctx.instrs.push_seperated(len);
        inner + 1
    }
}

impl<Type, A, const N: usize> LowerIn<Type> for [A; N]
where
    Type: TypeMeta,
    A: LowerCompatible<Type>,
{
    fn lower_in(&self, ctx: &mut LowerCtx) -> usize {
        let len = self.len();
        let mut inner = 0;
        for item in self {
            inner += item.lower_compatible(ctx);
        }
        ctx.instrs.push_seperated(len);
        inner + 1
    }
}

macro_rules! impl_in_macro {
    ($($T:ident),+) => {
        impl<Type, $($T,)+> LowerIn<Type> for ($($T,)+)
        where
            Type: TypeMeta,
            $($T: LowerCompatible<Type>,)+
        {
            fn lower_in(&self, ctx: &mut LowerCtx) -> usize {
                let mut inner = 0;
                #[allow(non_snake_case)]
                let ($($T,)+) = self;
                $(
                    inner += $T.lower_compatible(ctx);
                )+
                let count = count_idents!($($T,)+);
                ctx.instrs.push_seperated(count);
                inner + 1
            }
        }
    };
}

impl_for_all_tuples!(impl_in_macro);