ferroui_base/data/core/expression_nodes/
expression_node.rs1use crate::data::core::plugins::IPropertyAccessor;
2use crate::data::core::{BindingExpression, ValueType, ValueTypes, WeakValue};
3use crate::data::{BindingError, BindingErrorType, BindingNotification};
4use crate::{BoxedValue, FerroObject, FerroProperty, Ref};
5use std::any::Any;
6use std::cell::{Cell, RefCell};
7use std::rc::{Rc, Weak};
8
9#[derive(Clone)]
11pub enum NodeSource {
12 Unset,
13 Value(Option<BoxedValue>),
14}
15
16pub struct NodeState {
19 index: Cell<usize>,
20 owner: RefCell<Option<Weak<BindingExpression>>>,
21 source: RefCell<Option<WeakValue>>,
22 value: RefCell<HeldValue>,
23}
24
25enum HeldValue {
27 Value(Option<BoxedValue>),
28 Element(Option<WeakValue>),
31}
32
33impl Default for NodeState {
34 fn default() -> Self {
35 Self::new()
36 }
37}
38
39impl NodeState {
40 pub fn new() -> Self {
41 Self {
42 index: Cell::new(0),
43 owner: RefCell::new(None),
44 source: RefCell::new(None),
45 value: RefCell::new(HeldValue::Value(Some(FerroProperty::unset_value()))),
46 }
47 }
48
49 pub fn locating_an_element() -> Self {
60 Self { value: RefCell::new(HeldValue::Element(Some(WeakValue::new(&FerroProperty::unset_value())))), ..Self::new() }
61 }
62
63 pub fn index(&self) -> usize {
65 self.index.get()
66 }
67
68 pub fn owner(&self) -> Option<Rc<BindingExpression>> {
70 self.owner.borrow().as_ref().and_then(Weak::upgrade)
71 }
72
73 pub fn source(&self) -> Option<BoxedValue> {
75 let source = self.source.borrow().clone();
76 source.and_then(|s| s.upgrade())
77 }
78
79 pub fn source_object(&self) -> Option<Ref<FerroObject>> {
81 self.source().and_then(|s| ValueTypes::as_object(&*s))
82 }
83
84 pub fn value(&self) -> Option<BoxedValue> {
86 match &*self.value.borrow() {
87 HeldValue::Value(value) => value.clone(),
88 HeldValue::Element(value) => value.as_ref().and_then(WeakValue::upgrade),
89 }
90 }
91
92 pub(crate) fn set_raw_value(&self, value: Option<BoxedValue>) {
93 let mut held = self.value.borrow_mut();
94 let old = match &*held {
95 HeldValue::Value(_) => std::mem::replace(&mut *held, HeldValue::Value(value)),
96 HeldValue::Element(_) => {
97 std::mem::replace(&mut *held, HeldValue::Element(value.as_ref().map(WeakValue::new)))
98 }
99 };
100 drop(held);
103 drop(old);
104 }
105
106 pub fn clear_value(&self) {
108 self.set_value(Some(FerroProperty::unset_value()), None);
109 }
110
111 pub fn short_circuit_null(&self) {
115 self.set_raw_value(None);
116 if let Some(owner) = self.owner() {
117 owner.on_node_null_short_circuit(self.index());
118 }
119 }
120
121 pub fn set_data_validation_error(&self, error: BindingError) {
123 if let Some(owner) = self.owner() {
124 owner.on_data_validation_error(error);
125 }
126 }
127
128 pub fn set_error(&self, message: &str) {
131 self.set_raw_value(Some(FerroProperty::unset_value()));
132 if let Some(owner) = self.owner() {
133 owner.on_node_error(self.index() as isize, message);
134 }
135 }
136
137 pub fn set_value_or_notification(&self, value_or_notification: Option<BoxedValue>) {
140 let Some(notification) = value_or_notification.as_ref().and_then(|v| v.downcast_ref::<BindingNotification>())
141 else {
142 self.set_value(value_or_notification, None);
143 return;
144 };
145 match notification.error_type() {
146 BindingErrorType::Error => {
147 let message = notification.error().map(|e| e.to_string()).unwrap_or_default();
148 self.set_error(&message);
149 }
150 BindingErrorType::DataValidationError => {
151 if notification.has_value() {
152 let inner = notification.value();
153 if inner.as_ref().is_some_and(|v| v.is::<BindingNotification>()) {
154 self.set_value_or_notification(inner);
155 } else {
156 self.set_value(inner, notification.error().as_ref());
157 }
158 } else if let Some(error) = notification.error() {
159 self.set_data_validation_error(error);
160 }
161 }
162 BindingErrorType::None => self.set_value_or_notification_inner(notification.value()),
163 }
164 }
165
166 fn set_value_or_notification_inner(&self, value: Option<BoxedValue>) {
167 if value.as_ref().is_some_and(|v| v.is::<BindingNotification>()) {
168 self.set_value_or_notification(value);
169 } else {
170 self.set_value(value, None);
171 }
172 }
173
174 pub fn set_value(&self, value: Option<BoxedValue>, data_validation_error: Option<&BindingError>) {
176 debug_assert!(!value.as_ref().is_some_and(|v| v.is::<BindingNotification>()));
177 self.set_raw_value(value.clone());
178 if let Some(owner) = self.owner() {
179 owner.on_node_value_changed(self.index(), value, data_validation_error);
180 }
181 }
182
183 pub fn validate_non_null_source(&self, source: Option<&BoxedValue>) -> bool {
186 if source.is_none() {
187 if let Some(owner) = self.owner() {
188 owner.on_node_error(self.index() as isize - 1, "Value is null.");
189 }
190 self.set_raw_value(None);
191 return false;
192 }
193 true
194 }
195}
196
197pub trait ISettableNode {
199 fn value_type(&self) -> Option<ValueType>;
202
203 fn write_value_to_source(
205 &self,
206 value: Option<&BoxedValue>,
207 nodes: &[Rc<dyn ExpressionNode>],
208 ) -> Result<bool, BindingError>;
209}
210
211pub trait IPropertyAccessorNode {
213 fn property_name(&self) -> &str;
214 fn accessor(&self) -> Option<Rc<dyn IPropertyAccessor>>;
215 fn enable_data_validation(&self);
216}
217
218pub trait SourceNode {
220 fn select_source(
223 &self,
224 source: Option<&Option<BoxedValue>>,
225 target: &Ref<FerroObject>,
226 _anchor: Option<&Ref<FerroObject>>,
227 ) -> Option<BoxedValue> {
228 match source {
229 Some(source) => source.clone(),
230 None => Some(Rc::new(target.clone())),
231 }
232 }
233
234 fn should_log_errors(&self, state: &NodeState, _target: &FerroObject) -> bool {
235 state.value().is_some()
236 }
237}
238
239pub trait ExpressionNode: Any {
242 fn state(&self) -> &NodeState;
243
244 fn build_string(&self, _builder: &mut String) {}
246
247 fn build_string_with_nodes(&self, builder: &mut String, nodes: &[Rc<dyn ExpressionNode>]) {
249 let index = self.state().index();
250 if index > 0 {
251 nodes[index - 1].build_string_with_nodes(builder, nodes);
252 }
253 self.build_string(builder);
254 }
255
256 fn on_source_changed(&self, source: Option<&BoxedValue>, data_validation_error: Option<&BindingError>);
258
259 fn unsubscribe(&self, _old_source: &BoxedValue) {}
261
262 fn as_settable(&self) -> Option<&dyn ISettableNode> {
263 None
264 }
265
266 fn as_property_accessor_node(&self) -> Option<&dyn IPropertyAccessorNode> {
267 None
268 }
269
270 fn as_source_node(&self) -> Option<&dyn SourceNode> {
271 None
272 }
273
274 fn is_data_context_node(&self) -> bool {
276 false
277 }
278}
279
280impl dyn ExpressionNode {
281 pub fn set_owner(&self, owner: Weak<BindingExpression>, index: usize) {
283 let state = self.state();
284 let mut slot = state.owner.borrow_mut();
285 if slot.is_some() {
286 panic!("The expression node already has an owner.");
287 }
288 *slot = Some(owner);
289 state.index.set(index);
290 }
291
292 pub fn set_source(&self, source: NodeSource, data_validation_error: Option<&BindingError>) {
295 let state = self.state();
296 let old_source = state.source();
297 let unchanged = match (&source, &old_source) {
298 (NodeSource::Unset, None) => true,
299 (NodeSource::Value(Some(new)), Some(old)) => Rc::ptr_eq(new, old) || (**new).value_eq(&**old),
300 _ => false,
301 };
302 if unchanged {
303 return;
304 }
305 if let Some(old) = &old_source {
306 self.unsubscribe(old);
307 }
308 match source {
309 NodeSource::Unset => {
310 state.source.replace(None);
314 state.set_raw_value(Some(FerroProperty::unset_value()));
315 }
316 NodeSource::Value(value) => {
317 state.source.replace(value.as_ref().map(WeakValue::new));
318 self.on_source_changed(value.as_ref(), data_validation_error);
319 }
320 }
321 }
322
323 pub(crate) fn propagate_null_short_circuit_value(&self) {
326 self.set_source(NodeSource::Unset, None);
327 self.state().set_raw_value(None);
328 }
329}
330
331pub(crate) fn append_member(builder: &mut String, name: &str) {
333 if !builder.is_empty() && !builder.ends_with('!') {
334 builder.push('.');
335 }
336 builder.push_str(name);
337}