rust_query/value/
optional.rs1use std::marker::PhantomData;
2
3use sea_query::{ExprTrait, Nullable};
4
5use crate::{
6 IntoSelect,
7 select::{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> {
78 let nulls = self.nulls.clone();
79 Expr::adhoc(move |b| {
80 nulls
81 .iter()
82 .map(|x| (x.func)(b))
83 .reduce(|a, b| a.or(b))
84 .unwrap_or(false.into())
85 })
86 }
87
88 pub fn is_some(&self) -> Expr<'outer, S, bool> {
90 self.is_none().not()
91 }
92
93 pub fn and_then<T: MyTyp<Sql: Nullable>>(
96 &self,
97 col: impl IntoExpr<'inner, S, Typ = Option<T>>,
98 ) -> Expr<'outer, S, Option<T>> {
99 const NULL: sea_query::Expr = sea_query::Expr::Keyword(sea_query::Keyword::Null);
100
101 let col = col.into_expr().inner;
102 let is_none = self.is_none().inner;
103 Expr::adhoc(move |b| {
104 sea_query::Expr::case(is_none.build_expr(b), NULL)
105 .finally(col.build_expr(b))
106 .into()
107 })
108 }
109
110 pub fn then<T: MyTyp<Sql: Nullable> + 'outer>(
112 &self,
113 col: impl IntoExpr<'inner, S, Typ = T>,
114 ) -> Expr<'outer, S, Option<T>> {
115 self.and_then(Some(col))
116 }
117
118 pub fn then_select<Out: 'static>(
120 &self,
121 d: impl IntoSelect<'inner, S, Out = Out>,
122 ) -> Select<'outer, S, Option<Out>> {
123 Select::new(OptionalImpl {
124 inner: d.into_select().inner,
125 is_some: ColumnImpl {
126 expr: DynTypedExpr::erase(self.is_some()),
127 _p: PhantomData,
128 },
129 })
130 }
131}
132
133pub struct OptionalImpl<X> {
134 inner: X,
135 is_some: ColumnImpl<bool>,
136}
137
138impl<X: SelectImpl> SelectImpl for OptionalImpl<X> {
139 type Out = Option<X::Out>;
140 type Prepared = OptionalPrepared<X::Prepared>;
141
142 fn prepare(self, cacher: &mut Cacher) -> Self::Prepared {
143 OptionalPrepared {
144 is_some: self.is_some.prepare(cacher),
145 inner: self.inner.prepare(cacher),
146 }
147 }
148}
149
150pub struct OptionalPrepared<X> {
151 inner: X,
152 is_some: Cached<bool>,
153}
154
155impl<X: Prepared> Prepared for OptionalPrepared<X> {
156 type Out = Option<X::Out>;
157
158 fn call(&mut self, row: Row<'_>) -> Self::Out {
159 if row.get(self.is_some) {
160 Some(self.inner.call(row))
161 } else {
162 None
163 }
164 }
165}