1use super::{FuncMultiValueConverter, FuncValueConverter, FuncValueConverterWithParameter};
2use super::{IMultiValueConverter, IValueConverter};
3use crate::data::core::ValueTypes;
4use crate::BoxedValue;
5use std::rc::Rc;
6
7pub struct ObjectConverters;
9
10type Object = Option<BoxedValue>;
12
13fn equals(a: &BoxedValue, b: &Object) -> bool {
15 ValueTypes::identity_equals(Some(a), b.as_ref())
16}
17
18impl ObjectConverters {
19 pub fn is_null() -> Rc<dyn IValueConverter> {
22 crate::ferro_thread_local! {
23 static IS_NULL: Rc<dyn IValueConverter> =
24 Rc::new(FuncValueConverter::<Object, bool>::new(|x| x.is_none()));
25 }
26 IS_NULL.with(Rc::clone)
27 }
28
29 pub fn is_not_null() -> Rc<dyn IValueConverter> {
31 crate::ferro_thread_local! {
32 static IS_NOT_NULL: Rc<dyn IValueConverter> =
33 Rc::new(FuncValueConverter::<Object, bool>::new(|x| x.is_some()));
34 }
35 IS_NOT_NULL.with(Rc::clone)
36 }
37
38 pub fn equal() -> Rc<dyn IValueConverter> {
41 crate::ferro_thread_local! {
42 static EQUAL: Rc<dyn IValueConverter> =
43 Rc::new(FuncValueConverterWithParameter::<Object, Object, bool>::new(|a, b| match &a {
44 Some(a) => equals(a, &b),
45 None => b.is_none(),
46 }));
47 }
48 EQUAL.with(Rc::clone)
49 }
50
51 pub fn not_equal() -> Rc<dyn IValueConverter> {
54 crate::ferro_thread_local! {
55 static NOT_EQUAL: Rc<dyn IValueConverter> =
56 Rc::new(FuncValueConverterWithParameter::<Object, Object, bool>::new(|a, b| match &a {
57 Some(a) => !equals(a, &b),
58 None => b.is_some(),
59 }));
60 }
61 NOT_EQUAL.with(Rc::clone)
62 }
63
64 pub fn are_all_null() -> Rc<dyn IMultiValueConverter> {
66 crate::ferro_thread_local! {
67 static ARE_ALL_NULL: Rc<dyn IMultiValueConverter> =
68 Rc::new(FuncMultiValueConverter::<Object, bool>::new(|x| x.iter().all(|item| item.is_none())));
69 }
70 ARE_ALL_NULL.with(Rc::clone)
71 }
72
73 pub fn are_any_null() -> Rc<dyn IMultiValueConverter> {
76 crate::ferro_thread_local! {
77 static ARE_ANY_NULL: Rc<dyn IMultiValueConverter> =
78 Rc::new(FuncMultiValueConverter::<Object, bool>::new(|x| x.iter().any(|item| item.is_none())));
79 }
80 ARE_ANY_NULL.with(Rc::clone)
81 }
82
83 pub fn are_all_equal() -> Rc<dyn IMultiValueConverter> {
86 crate::ferro_thread_local! {
87 static ARE_ALL_EQUAL: Rc<dyn IMultiValueConverter> =
88 Rc::new(FuncMultiValueConverter::<Object, bool>::new(ObjectConverters::equality_function));
89 }
90 ARE_ALL_EQUAL.with(Rc::clone)
91 }
92
93 fn equality_function(values: &[Object]) -> bool {
96 let mut values = values.iter();
97
98 let Some(first) = values.next() else {
100 return true;
101 };
102
103 if first.is_none() {
105 return false;
106 }
107
108 for item in values {
109 match item {
110 Some(item) if equals(item, first) => {}
111 _ => return false,
112 }
113 }
114
115 true
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122 use crate::data::core::ValueType;
123 use crate::data::BindingError;
124 use crate::FerroObject;
125
126 fn as_bool(result: Result<Option<BoxedValue>, BindingError>) -> bool {
127 let value = result.expect("no error").expect("not null");
128 *value.downcast_ref::<bool>().expect("a bool")
129 }
130
131 fn new_object() -> BoxedValue {
133 Rc::new(FerroObject::new())
134 }
135
136 fn text(value: Option<&str>) -> Object {
139 value.map(|v| Rc::new(v.to_string()) as BoxedValue)
140 }
141
142 const VALUE: Option<&str> = Some("value");
143
144 #[test]
145 fn object_converters_tests_are_all_null_works() {
146 let cases = [
147 (None, None, None, true),
148 (None, None, VALUE, false),
149 (None, VALUE, None, false),
150 (VALUE, None, None, false),
151 (VALUE, VALUE, VALUE, false),
152 ];
153 for (value1, value2, value3, valid) in cases {
154 let converter = ObjectConverters::are_all_null();
155 let result = converter.convert(&[text(value1), text(value2), text(value3)], ValueType::of::<bool>(), None, &crate::utilities::CultureInfo::current_culture());
156 assert_eq!(as_bool(result), valid, "{value1:?} {value2:?} {value3:?}");
157 }
158 }
159
160 #[test]
161 fn object_converters_tests_are_any_null_works() {
162 let cases = [
163 (None, None, None, true),
164 (None, None, VALUE, true),
165 (None, VALUE, None, true),
166 (VALUE, None, None, true),
167 (VALUE, VALUE, VALUE, false),
168 ];
169 for (value1, value2, value3, valid) in cases {
170 let converter = ObjectConverters::are_any_null();
171 let result = converter.convert(&[text(value1), text(value2), text(value3)], ValueType::of::<bool>(), None, &crate::utilities::CultureInfo::current_culture());
172 assert_eq!(as_bool(result), valid, "{value1:?} {value2:?} {value3:?}");
173 }
174 }
175
176 #[test]
177 fn object_converters_tests_are_equal_works() {
178 let cases: [([Object; 3], bool); 6] = [
179 ([text(VALUE), text(VALUE), text(VALUE)], true),
180 ([None, text(VALUE), None], false),
181 ([text(VALUE), None, text(VALUE)], false),
182 ([text(VALUE), text(VALUE), text(Some("value1"))], false),
183 ([text(Some("value1")), text(VALUE), text(Some("value1"))], false),
184 ([text(VALUE), text(VALUE), Some(Rc::new(1i32))], false),
185 ];
186 for (values, valid) in cases {
187 let converter = ObjectConverters::are_all_equal();
188 let result = converter.convert(&values, ValueType::of::<bool>(), None, &crate::utilities::CultureInfo::current_culture());
189 assert_eq!(as_bool(result), valid);
190 }
191 }
192
193 #[test]
194 fn object_converters_tests_are_equal_edge_works() {
195 for (empty, unique, valid) in [(true, true, true), (false, true, false), (false, false, true)] {
196 let values: Vec<Object> = if empty {
197 Vec::new()
198 } else if unique {
199 vec![text(Some("1")), text(Some("2"))]
200 } else {
201 vec![text(Some("1")), text(Some("1"))]
202 };
203
204 let converter = ObjectConverters::are_all_equal();
205 let result = converter.convert(&values, ValueType::of::<bool>(), None, &crate::utilities::CultureInfo::current_culture());
206 assert_eq!(as_bool(result), valid, "empty: {empty}, unique: {unique}");
207 }
208 }
209
210 mod equal {
213 use super::*;
214
215 #[test]
216 fn returns_true_if_value_and_parameter_are_null() {
217 let result = ObjectConverters::equal().convert(None, ValueType::object(), None, &crate::utilities::CultureInfo::invariant_culture());
218
219 assert!(as_bool(result));
220 }
221
222 #[test]
223 fn returns_false_if_value_is_null_and_parameter_is_not_null() {
224 let result = ObjectConverters::equal().convert(None, ValueType::object(), Some(&new_object()), &crate::utilities::CultureInfo::invariant_culture());
225
226 assert!(!as_bool(result));
227 }
228
229 #[test]
230 fn returns_false_if_value_and_parameter_are_different_objects() {
231 let result = ObjectConverters::equal().convert(Some(&new_object()), ValueType::object(), Some(&new_object()), &crate::utilities::CultureInfo::invariant_culture());
232
233 assert!(!as_bool(result));
234 }
235
236 #[test]
237 fn returns_true_if_value_and_parameter_are_same_object() {
238 let target = new_object();
239 let result = ObjectConverters::equal().convert(Some(&target), ValueType::object(), Some(&target), &crate::utilities::CultureInfo::invariant_culture());
240
241 assert!(as_bool(result));
242 }
243
244 #[test]
247 fn returns_false_if_value_is_not_null_and_parameter_is_null() {
248 let result = ObjectConverters::equal().convert(Some(&new_object()), ValueType::object(), None, &crate::utilities::CultureInfo::invariant_culture());
249
250 assert!(!as_bool(result));
251 }
252
253 #[test]
254 fn compares_values_by_value() {
255 let a: BoxedValue = Rc::new(5i32);
256 let b: BoxedValue = Rc::new(5i32);
257 let c: BoxedValue = Rc::new(5i64);
258
259 assert!(as_bool(ObjectConverters::equal().convert(Some(&a), ValueType::object(), Some(&b), &crate::utilities::CultureInfo::invariant_culture())));
260 assert!(!as_bool(ObjectConverters::equal().convert(Some(&a), ValueType::object(), Some(&c), &crate::utilities::CultureInfo::invariant_culture())));
261 }
262
263 #[test]
264 fn same_handle_in_different_boxes_is_equal() {
265 let object = FerroObject::new();
266 let a: BoxedValue = Rc::new(object.clone());
267 let b: BoxedValue = Rc::new(object);
268
269 assert!(as_bool(ObjectConverters::equal().convert(Some(&a), ValueType::object(), Some(&b), &crate::utilities::CultureInfo::invariant_culture())));
270 }
271 }
272
273 mod is_null {
276 use super::*;
277
278 #[test]
279 fn returns_true_if_value_is_null() {
280 let result = ObjectConverters::is_null().convert(None, ValueType::object(), None, &crate::utilities::CultureInfo::invariant_culture());
281
282 assert!(as_bool(result));
283 }
284
285 #[test]
286 fn returns_false_if_value_is_not_null() {
287 let result = ObjectConverters::is_null().convert(Some(&new_object()), ValueType::object(), None, &crate::utilities::CultureInfo::invariant_culture());
288
289 assert!(!as_bool(result));
290 }
291
292 #[test]
295 fn returns_true_if_value_is_an_empty_nullable() {
296 let value: BoxedValue = Rc::new(Option::<i32>::None);
297 let result = ObjectConverters::is_null().convert(Some(&value), ValueType::object(), None, &crate::utilities::CultureInfo::invariant_culture());
298
299 assert!(as_bool(result));
300 }
301 }
302
303 mod is_not_null {
306 use super::*;
307
308 #[test]
309 fn returns_true_if_value_is_not_null() {
310 let result = ObjectConverters::is_not_null().convert(Some(&new_object()), ValueType::object(), None, &crate::utilities::CultureInfo::invariant_culture());
311
312 assert!(as_bool(result));
313 }
314
315 #[test]
316 fn returns_false_if_value_is_null() {
317 let result = ObjectConverters::is_not_null().convert(None, ValueType::object(), None, &crate::utilities::CultureInfo::invariant_culture());
318
319 assert!(!as_bool(result));
320 }
321 }
322
323 mod not_equal {
326 use super::*;
327
328 #[test]
329 fn returns_false_if_value_and_parameter_are_null() {
330 let result = ObjectConverters::not_equal().convert(None, ValueType::object(), None, &crate::utilities::CultureInfo::invariant_culture());
331
332 assert!(!as_bool(result));
333 }
334
335 #[test]
336 fn returns_true_if_value_is_null_and_parameter_is_not_null() {
337 let result = ObjectConverters::not_equal().convert(None, ValueType::object(), Some(&new_object()), &crate::utilities::CultureInfo::invariant_culture());
338
339 assert!(as_bool(result));
340 }
341
342 #[test]
343 fn returns_true_if_value_and_parameter_are_different_objects() {
344 let result =
345 ObjectConverters::not_equal().convert(Some(&new_object()), ValueType::object(), Some(&new_object()), &crate::utilities::CultureInfo::invariant_culture());
346
347 assert!(as_bool(result));
348 }
349
350 #[test]
351 fn returns_false_if_value_and_parameter_are_same_object() {
352 let target = new_object();
353 let result = ObjectConverters::not_equal().convert(Some(&target), ValueType::object(), Some(&target), &crate::utilities::CultureInfo::invariant_culture());
354
355 assert!(!as_bool(result));
356 }
357 }
358}