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