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};
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: MyTyp>(
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::Expr = sea_query::Expr::Keyword(sea_query::Keyword::Null);
96
97 let col = col.into_expr().inner;
98 let is_none = self.is_none().inner;
99 Expr::adhoc(move |b| {
100 sea_query::Expr::case(is_none.build_expr(b), NULL)
101 .finally(col.build_expr(b))
102 .into()
103 })
104 }
105
106 pub fn then<Out: 'static>(
108 &self,
109 d: impl IntoSelect<'inner, S, Out = Out>,
110 ) -> Select<'outer, S, Option<Out>> {
111 Select::new(OptionalImpl {
112 inner: d.into_select().inner,
113 is_some: ColumnImpl {
114 expr: self.is_some().into_expr().inner,
115 },
116 })
117 }
118}
119
120pub struct OptionalImpl<X> {
121 inner: X,
122 is_some: ColumnImpl<bool>,
123}
124
125impl<X: SelectImpl> SelectImpl for OptionalImpl<X> {
126 type Out = Option<X::Out>;
127 type Prepared = OptionalPrepared<X::Prepared>;
128
129 fn prepare(self, cacher: &mut Cacher) -> Self::Prepared {
130 OptionalPrepared {
131 is_some: self.is_some.prepare(cacher),
132 inner: self.inner.prepare(cacher),
133 }
134 }
135}
136
137pub struct OptionalPrepared<X> {
138 inner: X,
139 is_some: Cached<bool>,
140}
141
142impl<X: Prepared> Prepared for OptionalPrepared<X> {
143 type Out = Option<X::Out>;
144
145 fn call(&mut self, row: Row<'_>) -> Self::Out {
146 if row.get(self.is_some) {
147 Some(self.inner.call(row))
148 } else {
149 None
150 }
151 }
152}