ferroui_base/data/core/plugins/
indei_validation_plugin.rs1use super::{AccessorListener, DataValidationBase, IDataValidationPlugin, IPropertyAccessor};
2use crate::data::core::{ValueType, WeakValue};
3use crate::data::model::ModelTypes;
4use crate::data::{
5 AggregateException, BindingError, BindingErrorType, BindingNotification, BindingPriority,
6 DataValidationException,
7};
8use crate::reactive::IDisposable;
9use crate::BoxedValue;
10use std::cell::Cell;
11use std::rc::{Rc, Weak};
12
13pub struct IndeiValidationPlugin;
16
17impl IDataValidationPlugin for IndeiValidationPlugin {
18 fn match_(&self, reference: &WeakValue, _member_name: &str) -> bool {
19 instance(reference).is_some_and(|t| ModelTypes::find(&*t).is_some_and(|m| m.as_notify_data_error_info(&*t).is_some()))
20 }
21
22 fn start(
23 &self,
24 reference: &WeakValue,
25 property_name: &str,
26 inner: Rc<dyn IPropertyAccessor>,
27 ) -> Rc<dyn IPropertyAccessor> {
28 Rc::new_cyclic(|this| Validator {
29 this: this.clone(),
30 validation: DataValidationBase::new(inner),
31 reference: reference.clone(),
32 name: property_name.into(),
33 token: Cell::new(None),
34 })
35 }
36}
37
38fn instance(reference: &WeakValue) -> Option<BoxedValue> {
42 reference.upgrade().map(|target| super::markup_members::instance_of(&target))
43}
44
45struct Validator {
46 this: Weak<Validator>,
47 validation: DataValidationBase,
48 reference: WeakValue,
49 name: Box<str>,
50 token: Cell<Option<u64>>,
51}
52
53impl Validator {
54 fn create_binding_notification(&self, value: Option<BoxedValue>) -> BoxedValue {
55 if let Some(target) = instance(&self.reference) {
56 if let Some(model) = ModelTypes::find(&*target) {
57 if let Some(indei) = model.as_notify_data_error_info(&*target) {
58 let errors = indei.get_errors(Some(&self.name));
59 if !errors.is_empty() {
60 return Rc::new(BindingNotification::with_error_and_fallback(
61 generate_exception(errors),
62 BindingErrorType::DataValidationError,
63 value,
64 ));
65 }
66 }
67 }
68 }
69 Rc::new(BindingNotification::new(value))
70 }
71
72 fn remove_handler(&self) {
73 if let Some(token) = self.token.take() {
74 if let Some(target) = instance(&self.reference) {
75 if let Some(model) = ModelTypes::find(&*target) {
76 if let Some(indei) = model.as_notify_data_error_info(&*target) {
77 indei.errors_changed().remove(token);
78 }
79 }
80 }
81 }
82 }
83}
84
85fn generate_exception(mut errors: Vec<BoxedValue>) -> BindingError {
86 if errors.len() == 1 {
87 BindingError::new(DataValidationException::new(errors.pop()))
88 } else {
89 BindingError::new(AggregateException::new(
90 errors.into_iter().map(|e| DataValidationException::new(Some(e))).collect(),
91 ))
92 }
93}
94
95impl IDisposable for Validator {
96 fn dispose(&self) {
97 if self.validation.base.is_subscribed() {
98 self.unsubscribe();
99 }
100 }
101}
102
103impl IPropertyAccessor for Validator {
104 fn property_type(&self) -> Option<ValueType> {
105 self.validation.inner.property_type()
106 }
107
108 fn value(&self) -> Option<BoxedValue> {
109 self.validation.inner.value()
110 }
111
112 fn set_value(&self, value: Option<&BoxedValue>, priority: BindingPriority) -> Result<bool, BindingError> {
113 self.validation.inner.set_value(value, priority)
114 }
115
116 fn subscribe(&self, listener: AccessorListener) {
117 self.validation.base.set_listener(listener);
118 if let Some(target) = instance(&self.reference) {
119 if let Some(model) = ModelTypes::find(&*target) {
120 if let Some(indei) = model.as_notify_data_error_info(&*target) {
121 let weak = self.this.clone();
122 let token = indei.errors_changed().add(Rc::new(move |name: &str| {
123 if let Some(this) = weak.upgrade() {
124 if name.is_empty() || name == &*this.name {
125 let value = this.value();
126 let notification = this.create_binding_notification(value);
127 this.validation.base.publish_value(Some(notification));
128 }
129 }
130 }));
131 self.token.set(Some(token));
132 }
133 }
134 }
135 let weak = self.this.clone();
136 self.validation.subscribe_core(Rc::new(move |value| {
137 if let Some(this) = weak.upgrade() {
138 let notification = this.create_binding_notification(value);
139 this.validation.base.publish_value(Some(notification));
140 }
141 }));
142 }
143
144 fn unsubscribe(&self) {
145 self.remove_handler();
146 self.validation.unsubscribe_core();
147 self.validation.base.clear_listener();
148 }
149}
150
151impl Drop for Validator {
152 fn drop(&mut self) {
153 self.remove_handler();
154 }
155}