Skip to main content

qraft_core/expression/
in.rs

1//! `in (...)` predicates and lowering helpers for collections.
2
3use crate::{
4    LowerCompatible, NullOf, PredicateType, TypeMeta, UnaryType, count_idents,
5    expression::Expression,
6    impl_for_all_tuples,
7    lower::{Instructions, LowerCtx},
8    ty::Nullability,
9};
10
11/// A typed `in` or `not in` predicate.
12pub struct InList<L, R> {
13    lhs: L,
14    rhs: R,
15    negated: bool,
16}
17
18#[qraft_expression_macro::as_expression]
19impl<L, R> Expression for InList<L, R>
20where
21    L: Expression,
22    L::Type: Nullability,
23    R: LowerIn<L::Type>,
24    UnaryType<NullOf<L::Type>>: PredicateType,
25{
26    type Type = <UnaryType<NullOf<L::Type>> as PredicateType>::Output;
27
28    fn lower(&self, ctx: &mut LowerCtx) -> usize {
29        let lhs = self.lhs.lower(ctx);
30        let rhs = self.rhs.lower_in(ctx);
31        ctx.lower_in(self.negated, lhs, rhs)
32    }
33}
34
35/// Adds `in_` and `not_in` to any expression.
36pub trait In: Sized + Expression {
37    fn one_of<R>(self, list: R) -> InList<Self, R>
38    where
39        R: LowerIn<Self::Type>,
40    {
41        self.in_(list)
42    }
43
44    fn in_<R>(self, list: R) -> InList<Self, R>
45    where
46        R: LowerIn<Self::Type>,
47    {
48        InList {
49            lhs: self,
50            rhs: list,
51            negated: false,
52        }
53    }
54
55    fn not_in<R>(self, list: R) -> InList<Self, R>
56    where
57        R: LowerIn<Self::Type>,
58    {
59        InList {
60            lhs: self,
61            rhs: list,
62            negated: true,
63        }
64    }
65}
66
67impl<E> In for E where E: Expression {}
68
69/// Lowers a list-like value into the right-hand side of an `in (...)` predicate.
70pub trait LowerIn<T> {
71    /// Appends the list contents to the lowering context and returns the subtree size.
72    fn lower_in(&self, ctx: &mut LowerCtx) -> usize;
73}
74
75impl<Type, A> LowerIn<Type> for (A,)
76where
77    Type: TypeMeta,
78    A: LowerCompatible<Type>,
79{
80    fn lower_in(&self, ctx: &mut LowerCtx) -> usize {
81        let (a,) = self;
82        let inner = a.lower_compatible(ctx);
83        ctx.instrs.push_seperated(1);
84        inner + 1
85    }
86}
87
88impl<Type, A> LowerIn<Type> for Vec<A>
89where
90    Type: TypeMeta,
91    A: LowerCompatible<Type>,
92{
93    fn lower_in(&self, ctx: &mut LowerCtx) -> usize {
94        let len = self.len();
95        let mut inner = 0;
96        for item in self {
97            inner += item.lower_compatible(ctx);
98        }
99        ctx.instrs.push_seperated(len);
100        inner + 1
101    }
102}
103
104impl<Type, A, const N: usize> LowerIn<Type> for [A; N]
105where
106    Type: TypeMeta,
107    A: LowerCompatible<Type>,
108{
109    fn lower_in(&self, ctx: &mut LowerCtx) -> usize {
110        let len = self.len();
111        let mut inner = 0;
112        for item in self {
113            inner += item.lower_compatible(ctx);
114        }
115        ctx.instrs.push_seperated(len);
116        inner + 1
117    }
118}
119
120macro_rules! impl_in_macro {
121    ($($T:ident),+) => {
122        impl<Type, $($T,)+> LowerIn<Type> for ($($T,)+)
123        where
124            Type: TypeMeta,
125            $($T: LowerCompatible<Type>,)+
126        {
127            fn lower_in(&self, ctx: &mut LowerCtx) -> usize {
128                let mut inner = 0;
129                #[allow(non_snake_case)]
130                let ($($T,)+) = self;
131                $(
132                    inner += $T.lower_compatible(ctx);
133                )+
134                let count = count_idents!($($T,)+);
135                ctx.instrs.push_seperated(count);
136                inner + 1
137            }
138        }
139    };
140}
141
142impl_for_all_tuples!(impl_in_macro);