rust_query/value/
optional.rs1use std::marker::PhantomData;
2
3use sea_query::{ExprTrait, Nullable};
4
5use crate::{
6 IntoSelect,
7 dummy_impl::{Cached, Cacher, ColumnImpl, Prepared, Row, Select, SelectImpl},
8 value::DynTypedExpr,
9};
10
11use super::{Expr, IntoExpr, MyTyp};
12
13pub fn optional<'outer, S, R>(
41 f: impl for<'inner> FnOnce(&mut Optional<'outer, 'inner, S>) -> R,
42) -> R {
43 let mut optional = Optional {
44 nulls: Vec::new(),
45 _p: PhantomData,
46 _p2: PhantomData,
47 };
48 f(&mut optional)
49}
50
51pub struct Optional<'outer, 'inner, S> {
56 nulls: Vec<DynTypedExpr>,
57 _p: PhantomData<fn(&'inner ()) -> &'inner &'outer ()>,
58 _p2: PhantomData<S>,
59}
60
61impl<'outer, 'inner, S> Optional<'outer, 'inner, S> {
62 #[doc(alias = "join")]
66 pub fn and<T: MyTyp>(
67 &mut self,
68 col: impl IntoExpr<'inner, S, Typ = Option<T>>,
69 ) -> Expr<'inner, S, T> {
70 let column = col.into_expr();
71 self.nulls.push(DynTypedExpr::erase(column.is_none()));
72 Expr::adhoc(move |b| column.inner.build_expr(b))
74 }
75
76 pub fn is_none(&self) -> Expr<'outer, S, bool> {
77 let nulls = self.nulls.clone();
78 Expr::adhoc(move |b| {
79 nulls
80 .iter()
81 .map(|x| (x.func)(b))
82 .reduce(|a, b| a.or(b))
83 .unwrap_or(false.into())
84 })
85 }
86
87 pub fn is_some(&self) -> Expr<'outer, S, bool> {
89 self.is_none().not()
90 }
91
92 pub fn then_expr<T: MyTyp<Sql: Nullable> + 'outer>(
94 &self,
95 col: impl IntoExpr<'inner, S, Typ = T>,
96 ) -> Expr<'outer, S, Option<T>> {
97 const NULL: sea_query::Expr = sea_query::Expr::Keyword(sea_query::Keyword::Null);
98
99 let col = col.into_expr().inner;
100 let is_none = self.is_none().inner;
101 Expr::adhoc(move |b| {
102 sea_query::Expr::case(is_none.build_expr(b), NULL)
103 .finally(col.build_expr(b))
104 .into()
105 })
106 }
107
108 pub fn then<Out: 'static>(
110 &self,
111 d: impl IntoSelect<'inner, S, Out = Out>,
112 ) -> Select<'outer, S, Option<Out>> {
113 Select::new(OptionalImpl {
114 inner: d.into_select().inner,
115 is_some: ColumnImpl {
116 expr: DynTypedExpr::erase(self.is_some()),
117 _p: PhantomData,
118 },
119 })
120 }
121}
122
123pub struct OptionalImpl<X> {
124 inner: X,
125 is_some: ColumnImpl<bool>,
126}
127
128impl<X: SelectImpl> SelectImpl for OptionalImpl<X> {
129 type Out = Option<X::Out>;
130 type Prepared = OptionalPrepared<X::Prepared>;
131
132 fn prepare(self, cacher: &mut Cacher) -> Self::Prepared {
133 OptionalPrepared {
134 is_some: self.is_some.prepare(cacher),
135 inner: self.inner.prepare(cacher),
136 }
137 }
138}
139
140pub struct OptionalPrepared<X> {
141 inner: X,
142 is_some: Cached<bool>,
143}
144
145impl<X: Prepared> Prepared for OptionalPrepared<X> {
146 type Out = Option<X::Out>;
147
148 fn call(&mut self, row: Row<'_>) -> Self::Out {
149 if row.get(self.is_some) {
150 Some(self.inner.call(row))
151 } else {
152 None
153 }
154 }
155}