cliffy_core/
combinators.rs1use crate::behavior::{behavior, Behavior};
8use crate::geometric::{FromGeometric, IntoGeometric};
9
10pub fn when<T, F>(condition: &Behavior<bool>, then_value: F) -> Behavior<Option<T>>
29where
30 T: IntoGeometric + FromGeometric + Clone + Default + 'static,
31 F: Fn() -> T + 'static,
32{
33 let initial = if condition.sample() {
34 Some(then_value())
35 } else {
36 None
37 };
38
39 let result = behavior(initial);
40 let result_clone = result.clone();
41
42 condition.subscribe(move |&cond| {
43 if cond {
44 result_clone.set(Some(then_value()));
45 } else {
46 result_clone.set(None);
47 }
48 });
49
50 result
51}
52
53pub fn combine<A, B, C, F>(a: &Behavior<A>, b: &Behavior<B>, f: F) -> Behavior<C>
70where
71 A: IntoGeometric + FromGeometric + Clone + 'static,
72 B: IntoGeometric + FromGeometric + Clone + 'static,
73 C: IntoGeometric + FromGeometric + Clone + 'static,
74 F: Fn(A, B) -> C + Clone + 'static,
75{
76 a.combine(b, f)
77}
78
79pub fn combine3<A, B, C, D, F>(
94 a: &Behavior<A>,
95 b: &Behavior<B>,
96 c: &Behavior<C>,
97 f: F,
98) -> Behavior<D>
99where
100 A: IntoGeometric + FromGeometric + Clone + 'static,
101 B: IntoGeometric + FromGeometric + Clone + 'static,
102 C: IntoGeometric + FromGeometric + Clone + 'static,
103 D: IntoGeometric + FromGeometric + Clone + 'static,
104 F: Fn(A, B, C) -> D + Clone + 'static,
105{
106 let ab = a.combine(b, |x, y| (x, y));
107 ab.combine(c, move |(x, y), z| f(x, y, z))
108}
109
110pub fn if_else<T, TF, EF>(condition: &Behavior<bool>, then_value: TF, else_value: EF) -> Behavior<T>
133where
134 T: IntoGeometric + FromGeometric + Clone + 'static,
135 TF: Fn() -> T + 'static,
136 EF: Fn() -> T + 'static,
137{
138 let initial = if condition.sample() {
139 then_value()
140 } else {
141 else_value()
142 };
143
144 let result = behavior(initial);
145 let result_clone = result.clone();
146
147 condition.subscribe(move |&cond| {
148 if cond {
149 result_clone.set(then_value());
150 } else {
151 result_clone.set(else_value());
152 }
153 });
154
155 result
156}
157
158pub fn constant<T>(value: T) -> Behavior<T>
169where
170 T: IntoGeometric + FromGeometric + Clone + 'static,
171{
172 behavior(value)
173}
174
175pub fn map2<A, B, C, F>(a: &Behavior<A>, b: &Behavior<B>, f: F) -> Behavior<C>
179where
180 A: IntoGeometric + FromGeometric + Clone + 'static,
181 B: IntoGeometric + FromGeometric + Clone + 'static,
182 C: IntoGeometric + FromGeometric + Clone + 'static,
183 F: Fn(A, B) -> C + Clone + 'static,
184{
185 combine(a, b, f)
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191
192 #[test]
193 fn test_when_true() {
194 let condition = behavior(true);
195 let result = when(&condition, || 42i32);
196 assert_eq!(result.sample(), Some(42));
197 }
198
199 #[test]
200 fn test_when_false() {
201 let condition = behavior(false);
202 let result = when(&condition, || 42i32);
203 assert_eq!(result.sample(), None);
204 }
205
206 #[test]
207 fn test_when_reactive() {
208 let condition = behavior(true);
209 let result = when(&condition, || "visible".to_string());
210
211 assert_eq!(result.sample(), Some("visible".to_string()));
212
213 condition.set(false);
214 assert_eq!(result.sample(), None);
215
216 condition.set(true);
217 assert_eq!(result.sample(), Some("visible".to_string()));
218 }
219
220 #[test]
221 fn test_combine() {
222 let a = behavior(10i32);
223 let b = behavior(5i32);
224 let sum = combine(&a, &b, |x, y| x + y);
225
226 assert_eq!(sum.sample(), 15);
227
228 a.set(20);
229 assert_eq!(sum.sample(), 25);
230 }
231
232 #[test]
233 fn test_combine3() {
234 let a = behavior(1i32);
235 let b = behavior(2i32);
236 let c = behavior(3i32);
237 let sum = combine3(&a, &b, &c, |x, y, z| x + y + z);
238
239 assert_eq!(sum.sample(), 6);
240 }
241
242 #[test]
243 fn test_if_else() {
244 let is_dark_mode = behavior(false);
245 let theme = if_else(&is_dark_mode, || "dark".to_string(), || "light".to_string());
246
247 assert_eq!(theme.sample(), "light");
248
249 is_dark_mode.set(true);
250 assert_eq!(theme.sample(), "dark");
251 }
252
253 #[test]
254 fn test_constant() {
255 let c = constant(42i32);
256 assert_eq!(c.sample(), 42);
257 }
258}