1use super::expression_nodes::{ExpressionNode, NodeSource};
2use super::{
3 impl_untyped_binding_expression, ExpressionError, ObservableSink, Publish, TargetTypeConverter,
4 UntypedBindingExpression, UntypedBindingExpressionBase, ValueType, ValueTypes, WeakValue,
5};
6use crate::data::converters::{composite_format, IValueConverter};
7use crate::data::{
8 BindingChainException, BindingError, BindingErrorType, BindingMode, BindingNotification, BindingOperations,
9 BindingPriority, UpdateSourceTrigger,
10};
11use crate::logging::{LogArea, LogEventLevel, Logger};
12use crate::input::InputElement;
13use crate::interactivity::RoutedEventHandlerToken;
14use crate::reactive::IDisposable;
15use crate::threading::{Dispatcher, DispatcherPriority, DispatcherTimer};
16use crate::utilities::CultureInfo;
17use crate::{BoxedValue, FerroObject, FerroProperty, Ref, UnsetValueType};
18use std::any::Any;
19use std::cell::{Cell, RefCell};
20use std::rc::{Rc, Weak};
21use std::time::Duration;
22
23pub struct BindingExpressionOptions {
25 pub delay: i32,
28 pub fallback_value: Option<BoxedValue>,
30 pub converter: Option<Rc<dyn IValueConverter>>,
31 pub converter_culture: Option<CultureInfo>,
34 pub converter_parameter: Option<BoxedValue>,
35 pub enable_data_validation: bool,
36 pub mode: BindingMode,
37 pub priority: BindingPriority,
38 pub string_format: Option<String>,
39 pub target_null_value: Option<BoxedValue>,
42 pub target_property: Option<&'static FerroProperty>,
43 pub target_type_converter: Option<TargetTypeConverter>,
44 pub update_source_trigger: UpdateSourceTrigger,
45}
46
47impl Default for BindingExpressionOptions {
48 fn default() -> Self {
49 Self {
50 delay: 0,
51 fallback_value: Some(FerroProperty::unset_value()),
52 converter: None,
53 converter_culture: None,
54 converter_parameter: None,
55 enable_data_validation: false,
56 mode: BindingMode::OneWay,
57 priority: BindingPriority::LocalValue,
58 string_format: None,
59 target_null_value: Some(FerroProperty::unset_value()),
60 target_property: None,
61 target_type_converter: None,
62 update_source_trigger: UpdateSourceTrigger::PropertyChanged,
63 }
64 }
65}
66
67struct UncommonFields {
69 delay: Duration,
70 delay_timer: RefCell<Option<Rc<DispatcherTimer>>>,
71 delay_tick: RefCell<Option<Rc<dyn IDisposable>>>,
72 converter: Option<Rc<dyn IValueConverter>>,
73 converter_culture: Option<CultureInfo>,
74 converter_parameter: Option<BoxedValue>,
75 fallback_value: Option<BoxedValue>,
76 string_format: Option<String>,
77 target_null_value: Option<BoxedValue>,
78 update_source_trigger: UpdateSourceTrigger,
79}
80
81fn is_unset(value: &Option<BoxedValue>) -> bool {
82 value.as_ref().is_some_and(|v| v.is::<UnsetValueType>())
83}
84
85pub struct BindingExpression {
89 this: Weak<BindingExpression>,
90 base: UntypedBindingExpressionBase,
91 source: Option<WeakValue>,
93 mode: BindingMode,
94 nodes: Vec<Rc<dyn ExpressionNode>>,
95 target_type_converter: Option<TargetTypeConverter>,
96 uncommon: Option<Box<UncommonFields>>,
97 update_target_depth: Cell<u32>,
98 should_update_one_time_binding_target: Cell<bool>,
99 target_subscription: RefCell<Option<Rc<dyn IDisposable>>>,
100 lost_focus_token: RefCell<Option<RoutedEventHandlerToken>>,
101}
102
103impl BindingExpression {
104 pub fn new(
109 source: Option<BoxedValue>,
110 nodes: Vec<Rc<dyn ExpressionNode>>,
111 options: BindingExpressionOptions,
112 ) -> Rc<Self> {
113 if options.mode == BindingMode::Default {
114 panic!("Binding mode cannot be Default.");
115 }
116 if options.update_source_trigger == UpdateSourceTrigger::Default {
117 panic!("UpdateSourceTrigger cannot be Default.");
118 }
119 let source = source.filter(|s| !s.is::<UnsetValueType>());
120 let string_format = options.string_format.filter(|s| !s.trim().is_empty());
121 let has_target_null_value = options.target_null_value.as_ref().is_some_and(|v| !v.is::<UnsetValueType>());
122
123 let uncommon = if options.delay > 0
124 || options.converter.is_some()
125 || options.converter_culture.is_some()
126 || options.converter_parameter.is_some()
127 || !is_unset(&options.fallback_value)
128 || string_format.is_some()
129 || has_target_null_value
130 || options.update_source_trigger != UpdateSourceTrigger::PropertyChanged
131 {
132 Some(Box::new(UncommonFields {
133 delay: Duration::from_millis(options.delay.max(0) as u64),
134 delay_timer: RefCell::new(None),
135 delay_tick: RefCell::new(None),
136 converter: options.converter,
137 converter_culture: options.converter_culture,
138 converter_parameter: options.converter_parameter,
139 fallback_value: options.fallback_value,
140 string_format: string_format.map(|s| if s.contains('{') { s } else { format!("{{0:{s}}}") }),
141 target_null_value: if has_target_null_value {
142 options.target_null_value
143 } else {
144 Some(FerroProperty::unset_value())
145 },
146 update_source_trigger: options.update_source_trigger,
147 }))
148 } else {
149 None
150 };
151
152 let mode = options.mode;
153 let this = Rc::new_cyclic(|this: &Weak<BindingExpression>| BindingExpression {
154 this: this.clone(),
155 base: UntypedBindingExpressionBase::new(
156 this.clone(),
157 options.priority,
158 options.target_property,
159 options.enable_data_validation,
160 ),
161 source: source.as_ref().map(WeakValue::new),
162 mode,
163 nodes,
164 target_type_converter: options.target_type_converter,
165 uncommon,
166 update_target_depth: Cell::new(0),
167 should_update_one_time_binding_target: Cell::new(mode == BindingMode::OneTime),
168 target_subscription: RefCell::new(None),
169 lost_focus_token: RefCell::new(None),
170 });
171
172 let mut leaf_accessor = None;
173 for (i, node) in this.nodes.iter().enumerate() {
174 node.set_owner(Rc::downgrade(&this), i);
175 if let Some(n) = node.as_property_accessor_node() {
176 leaf_accessor = Some(n);
177 }
178 }
179 if options.enable_data_validation {
180 if let Some(leaf) = leaf_accessor {
181 leaf.enable_data_validation();
182 }
183 }
184 this
185 }
186
187 pub fn source_type(&self) -> Option<ValueType> {
190 self.leaf_node().and_then(|n| n.as_settable().and_then(|s| s.value_type()))
191 }
192
193 pub fn converter(&self) -> Option<&Rc<dyn IValueConverter>> {
194 self.uncommon.as_ref().and_then(|u| u.converter.as_ref())
195 }
196
197 pub fn converter_culture(&self) -> CultureInfo {
200 self.uncommon
201 .as_ref()
202 .and_then(|uncommon| uncommon.converter_culture.clone())
203 .unwrap_or_else(CultureInfo::current_culture)
204 }
205
206 pub fn converter_parameter(&self) -> Option<&BoxedValue> {
207 self.uncommon.as_ref().and_then(|u| u.converter_parameter.as_ref())
208 }
209
210 pub fn fallback_value(&self) -> Option<BoxedValue> {
212 match &self.uncommon {
213 Some(u) => u.fallback_value.clone(),
214 None => Some(FerroProperty::unset_value()),
215 }
216 }
217
218 pub fn leaf_node(&self) -> Option<&Rc<dyn ExpressionNode>> {
219 self.nodes.last()
220 }
221
222 pub fn nodes(&self) -> &[Rc<dyn ExpressionNode>] {
223 &self.nodes
224 }
225
226 pub fn mode(&self) -> BindingMode {
227 self.mode
228 }
229
230 pub fn string_format(&self) -> Option<&str> {
231 self.uncommon.as_ref().and_then(|u| u.string_format.as_deref())
232 }
233
234 pub fn target_null_value(&self) -> Option<BoxedValue> {
236 match &self.uncommon {
237 Some(u) => u.target_null_value.clone(),
238 None => Some(FerroProperty::unset_value()),
239 }
240 }
241
242 pub fn delay(&self) -> Duration {
245 self.uncommon.as_ref().map_or(Duration::ZERO, |u| u.delay)
246 }
247
248 fn stop_delay_timer(&self) {
249 if let Some(u) = &self.uncommon {
250 let timer = u.delay_timer.borrow().clone();
251 if let Some(timer) = timer {
252 timer.stop();
253 }
254 }
255 }
256
257 pub fn update_source_trigger(&self) -> UpdateSourceTrigger {
258 self.uncommon.as_ref().map_or(UpdateSourceTrigger::PropertyChanged, |u| u.update_source_trigger)
259 }
260
261 pub fn error_type(&self) -> BindingErrorType {
263 self.base.error_type()
264 }
265
266 pub fn to_observable(self: &Rc<Self>, target: Option<&FerroObject>) -> Rc<ObservableSink> {
269 ObservableSink::create(self, target)
270 }
271
272 pub(crate) fn on_node_value_changed(
274 &self,
275 node_index: usize,
276 value: Option<BoxedValue>,
277 data_validation_error: Option<&BindingError>,
278 ) {
279 debug_assert!(node_index < self.nodes.len());
280 let count = self.nodes.len();
281
282 if node_index == count - 1 {
283 if self.mode == BindingMode::OneTime {
284 if !self.should_update_one_time_binding_target.get() && !self.nodes[node_index].is_data_context_node() {
286 return;
287 }
288 self.should_update_one_time_binding_target.set(false);
289 }
290
291 let error = data_validation_error
292 .map(|e| ExpressionError::new(e.clone(), BindingErrorType::DataValidationError));
293
294 if self.mode != BindingMode::OneWayToSource {
297 let force_update = self.mode == BindingMode::OneWay || self.update_target_depth.get() > 0;
301 self.convert_and_publish_value(value, error, force_update);
302 } else if self.base.is_data_validation_enabled() {
303 self.base.publish_value(Publish::Unchanged, error, false);
308 }
309 } else if self.mode == BindingMode::OneWayToSource && node_index + 2 == count && value.is_some() {
310 self.nodes[node_index + 1].set_source(NodeSource::Value(value), data_validation_error);
315 self.write_target_value_to_source();
316 } else {
317 if self.mode == BindingMode::OneTime && self.nodes[node_index].is_data_context_node() {
318 self.should_update_one_time_binding_target.set(true);
319 }
320 self.nodes[node_index + 1].set_source(NodeSource::Value(value), data_validation_error);
321 }
322 }
323
324 pub(crate) fn on_node_null_short_circuit(&self, node_index: usize) {
328 for node in &self.nodes[node_index + 1..] {
329 node.propagate_null_short_circuit_value();
330 }
331 self.on_node_value_changed(self.nodes.len() - 1, None, None);
333 }
334
335 pub(crate) fn on_node_error(&self, node_index: isize, error: &str) {
338 for node in &self.nodes[(node_index + 1) as usize..] {
342 node.set_source(NodeSource::Unset, None);
343 }
344
345 if self.mode == BindingMode::OneWayToSource {
346 return;
347 }
348
349 let error_point = self.calculate_error_point(node_index);
350 if let Some(target) = self.should_log_error() {
351 self.log_at(&target, error, &error_point, LogEventLevel::Warning);
352 }
353
354 let binding_error = ExpressionError::new(
356 BindingError::new(BindingChainException::with_expression(error, self.description(), error_point)),
357 BindingErrorType::Error,
358 );
359 self.convert_and_publish_value(Some(FerroProperty::unset_value()), Some(binding_error), false);
360 }
361
362 pub(crate) fn on_data_validation_error(&self, error: BindingError) {
363 let binding_error = ExpressionError::new(error, BindingErrorType::DataValidationError);
364 self.base.publish_value(Publish::Unchanged, Some(binding_error), false);
365 }
366
367 fn calculate_error_point(&self, node_index: isize) -> String {
368 let mut result = String::new();
370 if node_index >= 0 {
371 self.nodes[node_index as usize].build_string(&mut result);
372 } else {
373 result.push_str("(source)");
374 }
375 result
376 }
377
378 fn log_at(&self, target: &FerroObject, error: &str, error_point: &str, level: LogEventLevel) {
379 let Some(log) = Logger::try_get(level, LogArea::BINDING) else { return };
380 let property = self.base.target_property().map_or("(unknown)", |p| p.name());
381 let description = self.description();
382 log.log_with_values(
383 Some(target as &dyn Any),
384 "An error occurred binding {Property} to {Expression} at {ExpressionErrorPoint}: {Message}",
385 &[&property, &description, &error_point, &error],
386 );
387 }
388
389 fn convert_and_publish_value(
390 &self,
391 value: Option<BoxedValue>,
392 error: Option<ExpressionError>,
393 force_update: bool,
394 ) {
395 let mut value = value;
396 let mut error = error;
397 let mut is_target_null_value = false;
398 let target_type = self.base.target_type();
399
400 if let Some(converter) = self.converter() {
403 if !is_unset(&value) && !BindingOperations::is_do_nothing(value.as_ref()) {
404 value = self.base.convert(
405 &|| self.should_log_error(),
406 &|| self.description(),
407 &**converter,
408 Some(&self.converter_culture()),
409 self.converter_parameter(),
410 value.as_ref(),
411 target_type,
412 &mut error,
413 );
414 let converted = value.clone();
416 if let Some(n) = converted.as_ref().and_then(|v| v.downcast_ref::<BindingNotification>()) {
417 if let Some(e) = n.error() {
418 error = Some(ExpressionError::new(e, n.error_type()));
419 }
420 value = if n.has_value() { n.value() } else { Some(FerroProperty::unset_value()) };
421 }
422 }
423 }
424
425 if BindingOperations::is_do_nothing(value.as_ref()) {
427 return;
428 }
429
430 let target_null_value = self.target_null_value();
433 if value.is_none() && !is_unset(&target_null_value) {
434 value = self.convert_fallback(target_null_value, "TargetNullValue");
435 is_target_null_value = true;
436 }
437
438 if !is_unset(&value) {
440 match self.string_format() {
441 Some(string_format)
442 if (target_type.is_object() || target_type.is_string()) && !is_target_null_value =>
443 {
444 value = match composite_format::format_values_with(
447 string_format,
448 std::slice::from_ref(&value),
449 &self.converter_culture(),
450 ) {
451 Ok(s) => self.convert_from(Some(Rc::new(s)), &mut error),
452 Err(e) => {
453 error = Some(ExpressionError::new(BindingError::new(e), BindingErrorType::Error));
454 Some(FerroProperty::unset_value())
455 }
456 };
457 }
458 _ => value = self.convert_from(value, &mut error),
459 }
460 }
461
462 let fallback_value = self.fallback_value();
465 if is_unset(&value) && !is_unset(&fallback_value) {
466 value = self.convert_fallback(fallback_value, "FallbackValue");
467 }
468
469 self.base.publish_value(Publish::Value(value), error, force_update);
470 }
471
472 fn write_target_value_to_source(&self) {
473 debug_assert!(matches!(self.mode, BindingMode::TwoWay | BindingMode::OneWayToSource));
474 self.stop_delay_timer();
475 if let (Some(target), Some(property)) = (self.base.try_get_target(), self.base.target_property()) {
476 let value = ValueTypes::normalize(target.get_value_untyped(property));
477 self.write_value_to_source(value);
478 }
479 }
480
481 fn convert_fallback(&self, fallback: Option<BoxedValue>, fallback_name: &str) -> Option<BoxedValue> {
482 let target_type = self.base.target_type();
483 if is_unset(&fallback) || (self.base.target_property().is_none() && target_type.is_object()) {
484 return fallback;
485 }
486 let converted = match &self.target_type_converter {
488 Some(converter) => converter.try_convert(fallback.as_ref(), target_type, &self.converter_culture()),
489 None => ValueTypes::try_convert(fallback.as_ref(), target_type),
490 };
491 if let Some(result) = converted {
492 return result;
493 }
494 if let Some(target) = self.base.try_get_target() {
495 self.base.log(
496 &target,
497 &self.description(),
498 &format!(
499 "Could not convert {fallback_name} '{}' to '{target_type}'.",
500 ValueTypes::to_display_string(fallback.as_ref())
501 ),
502 LogEventLevel::Error,
503 );
504 }
505 Some(FerroProperty::unset_value())
506 }
507
508 fn convert_from(&self, value: Option<BoxedValue>, error: &mut Option<ExpressionError>) -> Option<BoxedValue> {
511 let target_type = self.base.target_type();
512 if self.base.target_property().is_none() && target_type.is_object() {
513 return value;
514 }
515 let converted = match &self.target_type_converter {
518 Some(converter) => converter.try_convert(value.as_ref(), target_type, &self.converter_culture()),
519 None => ValueTypes::try_convert(value.as_ref(), target_type),
520 };
521 if let Some(result) = converted {
522 return result;
523 }
524 let message = format!(
525 "Could not convert '{}' ({}) to '{}'.",
526 ValueTypes::to_display_string(value.as_ref()),
527 value.as_ref().map_or("null", |v| (**v).type_name()),
528 target_type
529 );
530 if let Some(target) = self.should_log_error() {
531 self.base.log(&target, &self.description(), &message, LogEventLevel::Warning);
532 }
533 *error = Some(ExpressionError::new(BindingError::message(message), BindingErrorType::Error));
534 Some(FerroProperty::unset_value())
535 }
536
537 fn try_convert_back(&self, value_type: ValueType, value: &mut Option<BoxedValue>) -> bool {
541 let Some(converter) = self.converter() else { return true };
542 if is_unset(value) || BindingOperations::is_do_nothing(value.as_ref()) {
543 return true;
544 }
545 *value = self.base.convert_back(
546 &|| self.should_log_error(),
547 &|| self.description(),
548 &**converter,
549 Some(&self.converter_culture()),
550 self.converter_parameter(),
551 value.as_ref(),
552 value_type,
553 );
554 let converted = value.clone();
555 if let Some(notification) = converted.as_ref().and_then(|v| v.downcast_ref::<BindingNotification>()) {
556 if let Some(error) = notification.error() {
557 match notification.error_type() {
558 BindingErrorType::DataValidationError => {
559 if self.base.is_data_validation_enabled() {
560 self.on_data_validation_error(error);
561 }
562 }
563 _ => {
564 if let Some(target) = self.should_log_error() {
565 self.base.log(&target, &self.description(), &error.to_string(), LogEventLevel::Warning);
566 }
567 self.base.publish_value(
568 Publish::Unchanged,
569 Some(ExpressionError::new(error, BindingErrorType::Error)),
570 false,
571 );
572 }
573 }
574 return false;
575 }
576 *value = notification.value();
577 }
578 true
579 }
580
581 fn on_target_property_changed(&self) {
582 debug_assert!(matches!(self.mode, BindingMode::TwoWay | BindingMode::OneWayToSource));
583 let delay = self.delay();
584 let Some(uncommon) = self.uncommon.as_ref().filter(|_| !delay.is_zero()) else {
585 self.write_target_value_to_source();
588 return;
589 };
590
591 let existing = uncommon.delay_timer.borrow().clone();
592 let timer = match existing {
593 Some(timer) => {
594 timer.stop();
595 timer
596 }
597 None => {
598 let timer = DispatcherTimer::with_interval(
599 delay,
600 DispatcherPriority::DEFAULT,
601 &Dispatcher::current_dispatcher(),
602 );
603 let weak = self.this.clone();
604 let tick = timer.tick(move |_| {
605 if let Some(this) = weak.upgrade() {
606 this.write_target_value_to_source();
607 }
608 });
609 uncommon.delay_tick.replace(Some(tick));
610 uncommon.delay_timer.replace(Some(timer.clone()));
611 timer
612 }
613 };
614 timer.start();
615 }
616}
617
618impl UntypedBindingExpression for BindingExpression {
619 fn base(&self) -> &UntypedBindingExpressionBase {
620 &self.base
621 }
622
623 fn description(&self) -> String {
624 let mut b = String::new();
625 if let Some(leaf) = self.leaf_node() {
626 leaf.build_string_with_nodes(&mut b, &self.nodes);
627 }
628 b
629 }
630
631 fn update_source_core(&self) {
632 if matches!(self.mode, BindingMode::TwoWay | BindingMode::OneWayToSource) {
633 self.write_target_value_to_source();
634 }
635 }
636
637 fn update_target_core(&self) {
638 let Some(first) = self.nodes.first() else { return };
639 let source = first.state().source();
640 self.update_target_depth.set(self.update_target_depth.get() + 1);
641 for node in &self.nodes {
642 node.set_source(NodeSource::Unset, None);
643 }
644 first.set_source(NodeSource::Value(source), None);
647 self.update_target_depth.set(self.update_target_depth.get() - 1);
648 }
649
650 fn write_value_to_source(&self, value: Option<BoxedValue>) -> bool {
651 self.stop_delay_timer();
652 let mut value = value;
653 let Some(leaf) = self.leaf_node() else { return false };
654 let Some(setter) = leaf.as_settable() else { return false };
655 let Some(type_) = setter.value_type() else { return false };
656
657 if !self.try_convert_back(type_, &mut value) {
660 return false;
661 }
662
663 if BindingOperations::is_do_nothing(value.as_ref()) {
665 return true;
666 }
667
668 if let Some(converter) = &self.target_type_converter {
671 if let Some(converted) = converter.try_convert(value.as_ref(), type_, &self.converter_culture()) {
672 value = converted;
673 } else if !is_unset(&self.fallback_value()) {
674 value = self.fallback_value();
675 } else if self.base.is_data_validation_enabled() {
676 self.on_data_validation_error(BindingError::message(format!(
677 "Could not convert '{}' ({}) to {}.",
678 ValueTypes::to_display_string(value.as_ref()),
679 value.as_ref().map_or("null", |v| (**v).type_name()),
680 type_
681 )));
682 return false;
683 } else {
684 return false;
685 }
686 }
687
688 let current = leaf.state().value();
691 let current = current.and_then(ValueTypes::normalize);
692 let new = value.clone().and_then(ValueTypes::normalize);
693 if ValueTypes::identity_equals(current.as_ref(), new.as_ref())
694 && self.base.error_type() == BindingErrorType::None
695 {
696 return true;
697 }
698
699 setter.write_value_to_source(value.as_ref(), &self.nodes).unwrap_or(false)
700 }
701
702 fn should_log_error(&self) -> Option<Ref<FerroObject>> {
703 let target = self.base.try_get_target()?;
704 if let Some(first) = self.nodes.first() {
705 if let Some(source_node) = first.as_source_node() {
706 if !source_node.should_log_errors(first.state(), &target) {
707 return None;
708 }
709 }
710 }
711 Some(target)
712 }
713
714 fn start_core(&self) {
715 let source = self.source.as_ref().and_then(WeakValue::upgrade);
716 let Some(source) = source else {
717 self.on_node_error(-1, "Binding Source is null.");
718 return;
719 };
720
721 match self.nodes.first() {
722 Some(first) => first.set_source(NodeSource::Value(Some(source)), None),
723 None => self.convert_and_publish_value(Some(source), None, false),
724 }
725
726 if matches!(self.mode, BindingMode::TwoWay | BindingMode::OneWayToSource) {
727 if let (Some(target), Some(property)) = (self.base.try_get_target(), self.base.target_property()) {
728 if self.update_source_trigger() == UpdateSourceTrigger::PropertyChanged {
729 let weak = self.this.clone();
730 let id = property.id();
731 let subscription = target.property_changed(move |e| {
732 if e.property().id() == id {
733 if let Some(this) = weak.upgrade() {
734 this.on_target_property_changed();
735 }
736 }
737 });
738 self.target_subscription.replace(Some(subscription));
739 } else if self.update_source_trigger() == UpdateSourceTrigger::LostFocus {
740 if let Some(element) = target.downcast_ref::<InputElement>() {
741 let weak = self.this.clone();
742 let token = element.add_handler(InputElement::lost_focus_event(), move |_, _| {
743 if let Some(this) = weak.upgrade() {
744 this.write_target_value_to_source();
745 }
746 });
747 self.lost_focus_token.replace(Some(token));
748 }
749 }
750 }
751 }
752 }
753
754 fn stop_core(&self) {
755 self.stop_delay_timer();
756 let token = self.lost_focus_token.borrow_mut().take();
757 if let Some(token) = token {
758 if let Some(target) = self.base.try_get_target() {
759 if let Some(element) = target.downcast_ref::<InputElement>() {
760 element.remove_handler(InputElement::lost_focus_event(), token);
761 }
762 }
763 }
764 for node in &self.nodes {
765 node.set_source(NodeSource::Unset, None);
766 }
767 let subscription = self.target_subscription.borrow_mut().take();
768 if let Some(s) = subscription {
769 s.dispose();
770 }
771 }
772}
773
774impl_untyped_binding_expression!(BindingExpression);
775
776impl Drop for BindingExpression {
777 fn drop(&mut self) {
778 if let Some(s) = self.target_subscription.get_mut().take() {
779 s.dispose();
780 }
781 }
782}