1use crate::data::core::{ValueType, ValueTypes};
2use crate::data::{AggregateError, BindingErrorType, BindingNotification, BindingOperations};
3use crate::{BoxedValue, DoNothingType, FerroProperty, PropertyValue, UnsetValueType};
4use std::any::Any;
5use std::fmt;
6use std::rc::Rc;
7
8#[derive(Clone)]
10pub struct BindingError(Rc<dyn std::error::Error>);
11
12impl BindingError {
13 pub fn new(error: impl std::error::Error + 'static) -> Self {
14 Self(Rc::new(error))
15 }
16
17 pub fn message(message: impl Into<String>) -> Self {
18 #[derive(Debug)]
19 struct Message(String);
20 impl fmt::Display for Message {
21 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22 f.write_str(&self.0)
23 }
24 }
25 impl std::error::Error for Message {}
26 Self(Rc::new(Message(message.into())))
27 }
28
29 pub fn inner(&self) -> &(dyn std::error::Error + 'static) {
30 &*self.0
31 }
32}
33
34impl fmt::Debug for BindingError {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 fmt::Debug::fmt(&self.0, f)
37 }
38}
39
40impl fmt::Display for BindingError {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 fmt::Display::fmt(&self.0, f)
43 }
44}
45
46impl PartialEq for BindingError {
47 fn eq(&self, other: &Self) -> bool {
48 Rc::ptr_eq(&self.0, &other.0)
49 }
50}
51
52bitflags::bitflags! {
53 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
58 pub struct BindingValueType: i32 {
59 const UNSET_VALUE = 0;
62 const DO_NOTHING = 1;
64 const VALUE = 2 | Self::HAS_VALUE.bits();
66 const BINDING_ERROR = 3 | Self::HAS_ERROR.bits();
68 const DATA_VALIDATION_ERROR = 4 | Self::HAS_ERROR.bits();
70 const BINDING_ERROR_WITH_FALLBACK = Self::BINDING_ERROR.bits() | Self::HAS_VALUE.bits();
72 const DATA_VALIDATION_ERROR_WITH_FALLBACK = Self::DATA_VALIDATION_ERROR.bits() | Self::HAS_VALUE.bits();
74 const TYPE_MASK = 0x00ff;
76 const HAS_VALUE = 0x0100;
78 const HAS_ERROR = 0x0200;
80 }
81}
82
83#[derive(Clone, Debug, PartialEq)]
86pub struct BindingValue<T> {
87 type_: BindingValueType,
88 value: Option<T>,
89 error: Option<BindingError>,
90}
91
92impl<T> BindingValue<T> {
93 #[inline]
95 pub fn new(value: T) -> Self {
96 Self { type_: BindingValueType::VALUE, value: Some(value), error: None }
97 }
98
99 #[inline]
101 pub fn unset() -> Self {
102 Self { type_: BindingValueType::UNSET_VALUE, value: None, error: None }
103 }
104
105 #[inline]
107 pub fn do_nothing() -> Self {
108 Self { type_: BindingValueType::DO_NOTHING, value: None, error: None }
109 }
110
111 pub fn binding_error(error: BindingError) -> Self {
113 Self { type_: BindingValueType::BINDING_ERROR, value: None, error: Some(error) }
114 }
115
116 pub fn binding_error_with_fallback(error: BindingError, fallback: Option<T>) -> Self {
118 let type_ = if fallback.is_some() {
119 BindingValueType::BINDING_ERROR_WITH_FALLBACK
120 } else {
121 BindingValueType::BINDING_ERROR
122 };
123 Self { type_, value: fallback, error: Some(error) }
124 }
125
126 pub fn data_validation_error(error: BindingError) -> Self {
128 Self { type_: BindingValueType::DATA_VALIDATION_ERROR, value: None, error: Some(error) }
129 }
130
131 pub fn data_validation_error_with_fallback(error: BindingError, fallback: Option<T>) -> Self {
133 let type_ = if fallback.is_some() {
134 BindingValueType::DATA_VALIDATION_ERROR_WITH_FALLBACK
135 } else {
136 BindingValueType::DATA_VALIDATION_ERROR
137 };
138 Self { type_, value: fallback, error: Some(error) }
139 }
140
141 #[inline]
143 pub fn has_value(&self) -> bool {
144 self.value.is_some()
145 }
146
147 #[inline]
149 pub fn has_error(&self) -> bool {
150 self.error.is_some()
151 }
152
153 #[inline]
154 pub fn value_type(&self) -> BindingValueType {
155 self.type_
156 }
157
158 #[inline]
160 pub fn value(&self) -> &T {
161 self.value.as_ref().expect("BindingValue has no value")
162 }
163
164 #[inline]
165 pub fn error(&self) -> Option<&BindingError> {
166 self.error.as_ref()
167 }
168
169 #[inline]
171 pub fn into_option(self) -> Option<T> {
172 self.value
173 }
174
175 #[inline]
177 pub fn get_value_or_default(self, default: T) -> T {
178 self.value.unwrap_or(default)
179 }
180
181 pub fn with_value(self, value: T) -> Self {
184 let type_ = match self.type_ {
185 BindingValueType::DO_NOTHING => panic!("Cannot add value to DoNothing binding value."),
186 BindingValueType::BINDING_ERROR => BindingValueType::BINDING_ERROR_WITH_FALLBACK,
187 BindingValueType::DATA_VALIDATION_ERROR => {
188 BindingValueType::DATA_VALIDATION_ERROR_WITH_FALLBACK
189 }
190 BindingValueType::UNSET_VALUE => BindingValueType::VALUE,
191 other => other,
192 };
193 Self { type_, value: Some(value), error: self.error }
194 }
195
196 pub fn map<U>(self, f: impl FnOnce(T) -> U) -> BindingValue<U> {
198 BindingValue { type_: self.type_, value: self.value.map(f), error: self.error }
199 }
200}
201
202impl<T: Clone> BindingValue<T> {
203 pub fn to_optional(&self) -> Option<T> {
206 self.value.clone()
207 }
208
209 pub fn get_value_or_default_option(&self) -> Option<T> {
211 self.value.clone()
212 }
213}
214
215impl<T: Clone + 'static> BindingValue<T> {
216 pub fn get_value_or_default_as<TResult: Clone + 'static>(&self) -> Option<TResult> {
219 self.value.as_ref().and_then(|value| (value as &dyn Any).downcast_ref::<TResult>().cloned())
220 }
221
222 pub fn get_value_or_default_as_or<TResult: Clone + 'static>(&self, default_value: TResult) -> Option<TResult> {
225 match &self.value {
226 Some(value) => (value as &dyn Any).downcast_ref::<TResult>().cloned(),
227 None => Some(default_value),
228 }
229 }
230}
231
232impl<T: PropertyValue> BindingValue<T> {
233 fn box_value(value: &T) -> Option<BoxedValue> {
236 ValueTypes::normalize(Rc::new(value.clone()))
237 }
238
239 pub fn to_untyped(&self) -> Option<BoxedValue> {
246 let error = || self.error.clone().expect("a binding value with an error state has an error");
247 if self.type_ == BindingValueType::UNSET_VALUE {
248 Some(FerroProperty::unset_value())
249 } else if self.type_ == BindingValueType::DO_NOTHING {
250 Some(BindingOperations::do_nothing())
251 } else if self.type_ == BindingValueType::VALUE {
252 Self::box_value(self.value())
253 } else if self.type_ == BindingValueType::BINDING_ERROR {
254 Some(Rc::new(BindingNotification::with_error(error(), BindingErrorType::Error)))
255 } else if self.type_ == BindingValueType::BINDING_ERROR_WITH_FALLBACK {
256 Some(Rc::new(BindingNotification::with_error_and_fallback(
257 error(),
258 BindingErrorType::Error,
259 Self::box_value(self.value()),
260 )))
261 } else if self.type_ == BindingValueType::DATA_VALIDATION_ERROR {
262 Some(Rc::new(BindingNotification::with_error(error(), BindingErrorType::DataValidationError)))
263 } else if self.type_ == BindingValueType::DATA_VALIDATION_ERROR_WITH_FALLBACK {
264 Some(Rc::new(BindingNotification::with_error_and_fallback(
265 error(),
266 BindingErrorType::DataValidationError,
267 Self::box_value(self.value()),
268 )))
269 } else {
270 panic!("Invalid BindingValueType.")
271 }
272 }
273
274 pub fn from_untyped(value: Option<&BoxedValue>) -> Self {
277 Self::from_untyped_with_type(value, ValueType::of::<T>())
278 }
279
280 pub fn from_untyped_with_type(value: Option<&BoxedValue>, target_type: ValueType) -> Self {
288 if value.is_some_and(|value| value.is::<UnsetValueType>()) {
289 return Self::unset();
290 } else if value.is_some_and(|value| value.is::<DoNothingType>()) {
291 return Self::do_nothing();
292 }
293
294 let mut type_ = BindingValueType::VALUE;
295 let mut v: Option<T> = None;
296 let mut error: Option<BindingError> = None;
297 let mut errors: Option<Vec<BindingError>> = None;
298 let mut value: Option<BoxedValue> = value.cloned();
299
300 let notification = value.as_ref().and_then(|value| {
301 value.downcast_ref::<BindingNotification>().map(|n| (n.error(), n.error_type(), n.has_value(), n.value()))
302 });
303 if let Some((notification_error, error_type, has_value, notification_value)) = notification {
304 error = notification_error;
305 type_ = match error_type {
306 BindingErrorType::Error => BindingValueType::BINDING_ERROR,
307 BindingErrorType::DataValidationError => BindingValueType::DATA_VALIDATION_ERROR,
308 BindingErrorType::None => BindingValueType::VALUE,
309 };
310
311 if has_value {
312 type_ |= BindingValueType::HAS_VALUE;
313 }
314 value = notification_value;
315 }
316
317 if type_.contains(BindingValueType::HAS_VALUE) {
318 let typed = ValueTypes::try_convert(value.as_ref(), target_type)
319 .flatten()
320 .and_then(|typed| typed.downcast_ref::<T>().cloned());
321 match typed {
322 Some(typed) => v = Some(typed),
323 None => {
324 let e = BindingError::message(format!(
325 "Unable to convert object '{}' of type '{}' to type '{}'.",
326 ValueTypes::to_display_string(value.as_ref()),
327 value
328 .as_ref()
329 .map(|value| ValueTypes::type_full_name(ValueType::of_value(&**value)))
330 .unwrap_or_default(),
331 ValueTypes::type_full_name(target_type)
332 ));
333
334 match error.take() {
335 None => error = Some(e),
336 Some(first) => errors.get_or_insert_with(|| vec![first]).push(e),
337 }
338
339 type_ = BindingValueType::BINDING_ERROR;
340 }
341 }
342 }
343
344 if let Some(errors) = errors {
345 error = Some(BindingError::new(AggregateError::new(errors)));
346 }
347
348 Self { type_, value: v, error }
349 }
350}
351
352impl<T: fmt::Display> fmt::Display for BindingValue<T> {
357 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
358 match (&self.error, &self.value) {
359 (Some(error), _) => write!(f, "Error: {error}"),
360 (None, Some(value)) => fmt::Display::fmt(value, f),
361 (None, None) => f.write_str("(null)"),
362 }
363 }
364}
365
366impl<T> From<T> for BindingValue<T> {
367 #[inline]
368 fn from(value: T) -> Self {
369 Self::new(value)
370 }
371}
372
373impl<T> From<Option<T>> for BindingValue<T> {
374 fn from(value: Option<T>) -> Self {
376 match value {
377 Some(v) => Self::new(v),
378 None => Self::unset(),
379 }
380 }
381}
382
383#[cfg(test)]
384mod tests {
385 use super::*;
387
388 fn boxed<T: PartialEq + 'static>(value: T) -> BoxedValue {
389 Rc::new(value)
390 }
391
392 fn notification(value: &Option<BoxedValue>) -> &BindingNotification {
393 value.as_ref().and_then(|value| value.downcast_ref::<BindingNotification>()).expect("a binding notification")
394 }
395
396 #[test]
397 fn to_untyped_gives_the_value_or_the_marker_of_the_state() {
398 let value = BindingValue::new(5).to_untyped();
399 assert_eq!(Some(&5), value.as_ref().and_then(|value| value.downcast_ref::<i32>()));
400 assert!(BindingValue::<i32>::unset().to_untyped().is_some_and(|value| value.is::<UnsetValueType>()));
401 assert!(BindingValue::<i32>::do_nothing().to_untyped().is_some_and(|value| value.is::<DoNothingType>()));
402 }
403
404 #[test]
405 fn to_untyped_gives_a_notification_for_an_error() {
406 let untyped = BindingValue::<i32>::binding_error(BindingError::message("broken")).to_untyped();
407 let n = notification(&untyped);
408 assert_eq!(BindingErrorType::Error, n.error_type());
409 assert!(!n.has_value());
410 assert_eq!(Some(String::from("broken")), n.error().map(|error| error.to_string()));
411
412 let untyped =
413 BindingValue::data_validation_error_with_fallback(BindingError::message("invalid"), Some(7)).to_untyped();
414 let n = notification(&untyped);
415 assert_eq!(BindingErrorType::DataValidationError, n.error_type());
416 assert!(n.has_value());
417 assert_eq!(Some(7), n.value().and_then(|value| value.downcast_ref::<i32>().copied()));
418 }
419
420 #[test]
421 fn from_untyped_reads_values_and_markers() {
422 assert_eq!(BindingValue::new(5), BindingValue::<i32>::from_untyped(Some(&boxed(5))));
423 assert_eq!(BindingValue::<i32>::unset(), BindingValue::from_untyped(Some(&FerroProperty::unset_value())));
424 assert_eq!(BindingValue::<i32>::do_nothing(), BindingValue::from_untyped(Some(&BindingOperations::do_nothing())));
425 }
426
427 #[test]
428 fn from_untyped_reads_a_notification() {
429 let error = BindingError::message("invalid");
430 let n: BoxedValue = Rc::new(BindingNotification::with_error_and_fallback(
431 error.clone(),
432 BindingErrorType::DataValidationError,
433 Some(boxed(7)),
434 ));
435 let value = BindingValue::<i32>::from_untyped(Some(&n));
436 assert_eq!(BindingValueType::DATA_VALIDATION_ERROR_WITH_FALLBACK, value.value_type());
437 assert_eq!(7, *value.value());
438 assert_eq!(Some(&error), value.error());
439
440 let n: BoxedValue = Rc::new(BindingNotification::with_error(error.clone(), BindingErrorType::Error));
441 let value = BindingValue::<i32>::from_untyped(Some(&n));
442 assert_eq!(BindingValueType::BINDING_ERROR, value.value_type());
443 assert!(!value.has_value());
444 assert_eq!(Some(&error), value.error());
445 }
446
447 #[test]
448 fn from_untyped_gives_a_binding_error_for_a_value_of_another_type() {
449 #[derive(Clone, PartialEq)]
450 struct Unrelated;
451
452 let value = BindingValue::<i32>::from_untyped(Some(&boxed(Unrelated)));
453 assert_eq!(BindingValueType::BINDING_ERROR, value.value_type());
454 assert!(!value.has_value());
455 let message = value.error().map(|error| error.to_string()).unwrap_or_default();
456 assert!(message.starts_with("Unable to convert object '"), "{message}");
457
458 let n: BoxedValue = Rc::new(BindingNotification::with_error_and_fallback(
460 BindingError::message("invalid"),
461 BindingErrorType::DataValidationError,
462 Some(boxed(Unrelated)),
463 ));
464 let value = BindingValue::<i32>::from_untyped(Some(&n));
465 assert_eq!(BindingValueType::BINDING_ERROR, value.value_type());
466 let count = value
467 .error()
468 .and_then(|error| error.inner().downcast_ref::<AggregateError>())
469 .map(|error| error.inner_errors().len());
470 assert_eq!(Some(2), count);
471 }
472
473 #[test]
474 fn to_string_formats_the_error_the_value_or_null() {
475 assert_eq!("5", BindingValue::new(5).to_string());
476 assert_eq!("(null)", BindingValue::<i32>::unset().to_string());
477 assert_eq!("Error: broken", BindingValue::<i32>::binding_error(BindingError::message("broken")).to_string());
478 assert_eq!(
479 "Error: broken",
480 BindingValue::binding_error_with_fallback(BindingError::message("broken"), Some(1)).to_string()
481 );
482 }
483
484 #[test]
485 fn the_value_or_a_default_can_be_read() {
486 assert_eq!(Some(5), BindingValue::new(5).to_optional());
487 assert_eq!(None, BindingValue::<i32>::unset().to_optional());
488 assert_eq!(Some(5), BindingValue::new(5).get_value_or_default_option());
489 assert_eq!(None, BindingValue::<i32>::do_nothing().get_value_or_default_option());
490 assert_eq!(9, BindingValue::<i32>::unset().get_value_or_default(9));
491
492 assert_eq!(Some(5), BindingValue::new(5).get_value_or_default_as::<i32>());
493 assert_eq!(None, BindingValue::new(5).get_value_or_default_as::<String>());
494 assert_eq!(None, BindingValue::<i32>::unset().get_value_or_default_as::<i32>());
495 assert_eq!(Some(5), BindingValue::new(5).get_value_or_default_as_or(9));
496 assert_eq!(None, BindingValue::new(5).get_value_or_default_as_or(String::from("x")));
497 assert_eq!(Some(9), BindingValue::<i32>::unset().get_value_or_default_as_or(9));
498 }
499}