teaql_core/
safe_expression.rs1use std::sync::Arc;
2
3use crate::{BaseEntity, SmartList, Value};
4
5pub trait TeaqlEmpty {
6 fn teaql_is_empty(&self) -> bool;
7}
8
9impl TeaqlEmpty for String {
10 fn teaql_is_empty(&self) -> bool {
11 self.is_empty()
12 }
13}
14
15impl TeaqlEmpty for &str {
16 fn teaql_is_empty(&self) -> bool {
17 self.is_empty()
18 }
19}
20
21impl<T> TeaqlEmpty for Vec<T> {
22 fn teaql_is_empty(&self) -> bool {
23 self.is_empty()
24 }
25}
26
27impl<T> TeaqlEmpty for SmartList<T> {
28 fn teaql_is_empty(&self) -> bool {
29 self.is_empty()
30 }
31}
32
33impl<T> TeaqlEmpty for Option<T> {
34 fn teaql_is_empty(&self) -> bool {
35 self.is_none()
36 }
37}
38
39impl TeaqlEmpty for Value {
40 fn teaql_is_empty(&self) -> bool {
41 match self {
42 Self::Null => true,
43 Self::Text(value) => value.is_empty(),
44 Self::List(values) => values.is_empty(),
45 Self::Object(values) => values.is_empty(),
46 Self::Json(serde_json::Value::Null) => true,
47 Self::Json(serde_json::Value::String(value)) => value.is_empty(),
48 Self::Json(serde_json::Value::Array(values)) => values.is_empty(),
49 Self::Json(serde_json::Value::Object(values)) => values.is_empty(),
50 _ => false,
51 }
52 }
53}
54
55#[derive(Clone)]
56pub struct SafeExpression<R, T> {
57 root: Arc<R>,
58 #[allow(clippy::type_complexity)]
59 evaluator: Arc<dyn Fn(&R) -> Option<T> + Send + Sync>,
60}
61
62impl<R, T> SafeExpression<R, T>
63where
64 R: Send + Sync + 'static,
65 T: 'static,
66{
67 pub fn new(root: R, evaluator: impl Fn(&R) -> Option<T> + Send + Sync + 'static) -> Self {
68 Self {
69 root: Arc::new(root),
70 evaluator: Arc::new(evaluator),
71 }
72 }
73
74 pub fn eval(&self) -> Option<T> {
75 (self.evaluator)(&self.root)
76 }
77
78 pub fn eval_with(&self, root: &R) -> Option<T> {
79 (self.evaluator)(root)
80 }
81
82 pub fn apply<U>(self, mapper: impl Fn(T) -> U + Send + Sync + 'static) -> SafeExpression<R, U>
83 where
84 U: 'static,
85 {
86 self.apply_optional(move |value| Some(mapper(value)))
87 }
88
89 pub fn apply_optional<U>(
90 self,
91 mapper: impl Fn(T) -> Option<U> + Send + Sync + 'static,
92 ) -> SafeExpression<R, U>
93 where
94 U: 'static,
95 {
96 let root = Arc::clone(&self.root);
97 let evaluator = Arc::clone(&self.evaluator);
98 SafeExpression {
99 root,
100 evaluator: Arc::new(move |root| evaluator(root).and_then(&mapper)),
101 }
102 }
103
104 pub fn or_else(&self, default_value: T) -> T {
105 self.eval().unwrap_or(default_value)
106 }
107
108 pub fn or_else_with(&self, default_value: impl FnOnce() -> T) -> T {
109 self.eval().unwrap_or_else(default_value)
110 }
111
112 pub fn or_else_throw<E>(&self, error: impl FnOnce() -> E) -> Result<T, E> {
113 self.eval().ok_or_else(error)
114 }
115
116 pub fn is_null(&self) -> bool {
117 self.eval().is_none()
118 }
119
120 pub fn is_not_null(&self) -> bool {
121 self.eval().is_some()
122 }
123
124 pub fn is_empty(&self) -> bool
125 where
126 T: TeaqlEmpty,
127 {
128 self.eval()
129 .map(|value| value.teaql_is_empty())
130 .unwrap_or(true)
131 }
132
133 pub fn is_not_empty(&self) -> bool
134 where
135 T: TeaqlEmpty,
136 {
137 !self.is_empty()
138 }
139
140 pub fn when_is_null(&self, function: impl FnOnce()) {
141 if self.is_null() {
142 function();
143 }
144 }
145
146 pub fn when_is_not_null(&self, consumer: impl FnOnce(T)) {
147 if let Some(value) = self.eval() {
148 consumer(value);
149 }
150 }
151
152 pub fn when_is_empty(&self, function: impl FnOnce())
153 where
154 T: TeaqlEmpty,
155 {
156 if self.is_empty() {
157 function();
158 }
159 }
160
161 pub fn when_not_empty(&self, consumer: impl FnOnce(T))
162 where
163 T: TeaqlEmpty,
164 {
165 if let Some(value) = self.eval().filter(|value| !value.teaql_is_empty()) {
166 consumer(value);
167 }
168 }
169}
170
171impl<R> SafeExpression<R, R>
172where
173 R: Clone + Send + Sync + 'static,
174{
175 pub fn value(root: R) -> Self {
176 Self::new(root, |root| Some(root.clone()))
177 }
178
179 pub fn root(&self) -> &R {
180 &self.root
181 }
182}
183
184impl<R, E> SafeExpression<R, E>
185where
186 R: Send + Sync + 'static,
187 E: BaseEntity + Clone + 'static,
188{
189 pub fn entity_id(self) -> SafeExpression<R, u64> {
190 self.apply(|entity| entity.id())
191 }
192
193 pub fn entity_version(self) -> SafeExpression<R, i64> {
194 self.apply(|entity| entity.version_value())
195 }
196
197 pub fn update_entity_id(self, id: u64) -> SafeExpression<R, E> {
198 self.apply(move |mut entity| {
199 entity.set_id(id);
200 entity
201 })
202 }
203}
204
205impl<R, T> SafeExpression<R, SmartList<T>>
206where
207 R: Send + Sync + 'static,
208 T: Clone + 'static,
209{
210 pub fn size(self) -> SafeExpression<R, usize> {
211 self.apply(|list| list.len())
212 }
213
214 pub fn first(self) -> SafeExpression<R, T> {
215 self.apply_optional(|list| list.first().cloned())
216 }
217
218 pub fn get(self, index: usize) -> SafeExpression<R, T> {
219 self.apply_optional(move |list| list.get(index).cloned())
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use std::sync::Arc;
226 use std::sync::atomic::{AtomicUsize, Ordering};
227
228 use super::SafeExpression;
229
230 #[test]
231 fn safe_expression_eval_with_uses_the_supplied_root() {
232 let expression = SafeExpression::new(2_i32, |root| Some(root * 3));
233
234 assert_eq!(expression.eval(), Some(6));
235 assert_eq!(expression.eval_with(&4), Some(12));
236 }
237
238 #[test]
239 fn safe_expression_apply_optional_short_circuits_remaining_mappers() {
240 let optional_calls = Arc::new(AtomicUsize::new(0));
241 let remaining_calls = Arc::new(AtomicUsize::new(0));
242
243 let optional_calls_for_mapper = Arc::clone(&optional_calls);
244 let remaining_calls_for_mapper = Arc::clone(&remaining_calls);
245 let expression = SafeExpression::value(5_i32)
246 .apply_optional(move |_| {
247 optional_calls_for_mapper.fetch_add(1, Ordering::SeqCst);
248 None::<i32>
249 })
250 .apply(move |value| {
251 remaining_calls_for_mapper.fetch_add(1, Ordering::SeqCst);
252 value * 2
253 });
254
255 assert_eq!(expression.eval(), None);
256 assert_eq!(optional_calls.load(Ordering::SeqCst), 1);
257 assert_eq!(remaining_calls.load(Ordering::SeqCst), 0);
258 }
259
260 #[test]
261 fn safe_expression_lazy_fallback_and_error_only_run_for_missing_values() {
262 let mut present_fallback_calls = 0;
263 let present = SafeExpression::value(7_i32);
264 assert_eq!(
265 present.or_else_with(|| {
266 present_fallback_calls += 1;
267 9
268 }),
269 7
270 );
271 assert_eq!(present_fallback_calls, 0);
272 assert_eq!(present.or_else_throw(|| "unused error"), Ok(7));
273
274 let missing = SafeExpression::new((), |_| None::<i32>);
275 let mut missing_fallback_calls = 0;
276 assert_eq!(
277 missing.or_else_with(|| {
278 missing_fallback_calls += 1;
279 9
280 }),
281 9
282 );
283 assert_eq!(missing_fallback_calls, 1);
284 assert_eq!(
285 missing.or_else_throw(|| "missing value"),
286 Err("missing value")
287 );
288 }
289
290 #[test]
291 fn safe_expression_callbacks_only_run_for_their_matching_branch() {
292 let present = SafeExpression::value("teaql".to_owned());
293 let mut present_null_calls = 0;
294 let mut present_value = None;
295 present.when_is_null(|| present_null_calls += 1);
296 present.when_is_not_null(|value| present_value = Some(value));
297 assert_eq!(present_null_calls, 0);
298 assert_eq!(present_value.as_deref(), Some("teaql"));
299
300 let missing = SafeExpression::new((), |_| None::<String>);
301 let mut missing_null_calls = 0;
302 let mut missing_value_calls = 0;
303 missing.when_is_null(|| missing_null_calls += 1);
304 missing.when_is_not_null(|_| missing_value_calls += 1);
305 assert_eq!(missing_null_calls, 1);
306 assert_eq!(missing_value_calls, 0);
307 }
308}