1use crate::data::{BindingPriority, BindingValue};
2use crate::ferro_property::{FerroPropertyInit, PropertyRoutes};
3use crate::ferro_property_metadata::MetadataTable;
4use crate::property_store::{EffectiveValueDyn, IValueEntry};
5use crate::reactive::{IDisposable, IObservable};
6use crate::{
7 BoxedValue, ObjectType, DirectPropertyMetadata, DoNothingType, FerroObject, FerroProperty,
8 FerroPropertyMetadata, FerroPropertyRegistry, PropertyValue, TypeInfo, UnsetValueType,
9};
10use std::any::Any;
11use std::cell::OnceCell;
12use std::ops::Deref;
13use std::rc::Rc;
14
15pub(crate) trait DirectPropertyAccessor<T> {
17 fn invoke_getter(&self, instance: &FerroObject) -> T;
18 fn invoke_setter(&self, instance: &FerroObject, value: BindingValue<T>);
19}
20
21pub struct DirectPropertyBase<T: PropertyValue> {
26 base: FerroProperty,
27 owner: &'static TypeInfo,
28 metadata: MetadataTable<DirectPropertyMetadata<T>>,
29 accessor: OnceCell<&'static dyn DirectPropertyAccessor<T>>,
30}
31
32impl<T: PropertyValue> Deref for DirectPropertyBase<T> {
33 type Target = FerroProperty;
34 #[inline]
35 fn deref(&self) -> &FerroProperty {
36 &self.base
37 }
38}
39
40impl<T: PropertyValue> DirectPropertyBase<T> {
41 fn convert_untyped(value: &dyn Any) -> Option<T> {
46 let converted = crate::data::core::ValueTypes::try_convert_implicit(value, std::any::TypeId::of::<T>())?;
47 let converted: &dyn crate::AnyValue = &*converted;
48 converted.downcast_ref::<T>().cloned()
49 }
50
51 #[inline]
53 pub fn as_property(&'static self) -> &'static FerroProperty {
54 &self.base
55 }
56
57 #[inline]
59 pub fn owner(&self) -> &'static TypeInfo {
60 self.owner
61 }
62
63 #[inline]
64 fn accessor(&self) -> &'static dyn DirectPropertyAccessor<T> {
65 *self.accessor.get().expect("property is not fully registered")
66 }
67
68 #[inline]
70 pub(crate) fn invoke_getter(&self, instance: &FerroObject) -> T {
71 self.accessor().invoke_getter(instance)
72 }
73
74 #[inline]
76 pub(crate) fn invoke_setter(&self, instance: &FerroObject, value: BindingValue<T>) {
77 self.accessor().invoke_setter(instance, value)
78 }
79
80 pub fn get_unset_value(&self, type_: &'static TypeInfo) -> T {
82 self.get_metadata(type_).unset_value().clone()
83 }
84
85 pub fn get_unset_value_for(&self, owner: &FerroObject) -> T {
87 self.get_unset_value(owner.get_type())
88 }
89
90 pub fn get_metadata(&self, type_: &'static TypeInfo) -> Rc<DirectPropertyMetadata<T>> {
92 self.metadata.get(type_)
93 }
94
95 pub fn get_metadata_for(&self, owner: &FerroObject) -> Rc<DirectPropertyMetadata<T>> {
97 self.metadata.get(owner.get_type())
98 }
99
100 pub fn override_metadata<TOwner: ObjectType>(&self, metadata: DirectPropertyMetadata<T>) {
102 self.override_metadata_for(TOwner::TYPE, metadata);
103 }
104
105 pub fn override_metadata_for(&self, type_: &'static TypeInfo, metadata: DirectPropertyMetadata<T>) {
107 self.metadata.override_metadata(self.name(), type_, metadata);
108 }
109
110 pub fn unregister(&self, type_: &'static TypeInfo) {
112 self.metadata.unregister(type_);
113 }
114
115 #[track_caller]
116 fn invalid_value(&self) -> ! {
117 self.base.invalid_value_type()
118 }
119}
120
121impl<T: PropertyValue> PropertyRoutes for DirectPropertyBase<T> {
122 fn create_effective_value(&'static self, _o: &FerroObject) -> Rc<dyn EffectiveValueDyn> {
123 panic!("direct properties have no effective value");
124 }
125
126 fn route_clear_value(&'static self, o: &FerroObject) {
127 o.clear_direct_value(self);
128 }
129
130 fn route_coerce_default_value(&'static self, _o: &FerroObject) {
131 }
133
134 fn route_get_value(&'static self, o: &FerroObject) -> BoxedValue {
135 Rc::new(o.get_direct_value(self))
136 }
137
138 fn route_get_base_value(&'static self, o: &FerroObject) -> BoxedValue {
139 Rc::new(o.get_direct_value(self))
140 }
141
142 fn route_get_default_value(&'static self, type_: &'static TypeInfo) -> BoxedValue {
143 Rc::new(self.get_unset_value(type_))
144 }
145
146 fn route_set_value(
147 &'static self,
148 o: &FerroObject,
149 value: &dyn Any,
150 _priority: BindingPriority,
151 ) -> Option<Rc<dyn IDisposable>> {
152 let this = self;
153 if value.is::<UnsetValueType>() {
154 o.clear_direct_value(this);
155 } else if value.is::<DoNothingType>() {
156 } else if let Some(v) = value.downcast_ref::<T>() {
157 o.set_direct_value(this, v.clone());
158 } else if let Some(v) = Self::convert_untyped(value) {
159 o.set_direct_value(this, v);
160 } else {
161 self.invalid_value();
162 }
163 None
164 }
165
166 fn route_set_current_value(&'static self, o: &FerroObject, value: &dyn Any) {
167 self.route_set_value(o, value, BindingPriority::LocalValue);
168 }
169
170 fn route_set_direct_value_unchecked(&'static self, o: &FerroObject, entry: &dyn IValueEntry) {
171 let mut value: Option<T> = None;
172 if entry.try_get_value(&mut value) {
173 if let Some(value) = value {
174 o.set_direct_value_unchecked(self, BindingValue::new(value));
175 }
176 } else if let Some(boxed) = entry.get_value_boxed() {
177 if let Some(value) = boxed.downcast_ref::<T>() {
181 self.invoke_setter(o, BindingValue::new(value.clone()));
182 } else if boxed.is::<UnsetValueType>() {
183 self.invoke_setter(o, BindingValue::new(self.get_unset_value_for(o)));
184 }
185 }
186 }
187
188 fn route_bind(
189 &'static self,
190 o: &FerroObject,
191 source: Rc<dyn IObservable<BoxedValue>>,
192 _priority: BindingPriority,
193 ) -> Rc<dyn IDisposable> {
194 o.bind_direct_untyped(self, source)
195 }
196
197 fn route_get_metadata(&'static self, type_: &'static TypeInfo) -> FerroPropertyMetadata {
198 let metadata = self.get_metadata(type_);
199 let base: &FerroPropertyMetadata = &metadata;
200 base.clone()
201 }
202
203 fn route_unregister(&'static self, type_: &'static TypeInfo) {
204 self.unregister(type_)
205 }
206
207 fn route_is_valid_value(&'static self, value: &dyn Any) -> bool {
208 value.is::<T>()
209 }
210
211 fn as_any(&'static self) -> &'static dyn Any {
212 self
213 }
214
215 fn route_copy_value(&'static self, value: &dyn Any, out: &mut dyn Any) -> bool {
216 match (value.downcast_ref::<T>(), out.downcast_mut::<Option<T>>()) {
217 (Some(value), Some(out)) => {
218 *out = Some(value.clone());
219 true
220 }
221 _ => false,
222 }
223 }
224
225 fn route_box_value(&'static self, value: &dyn Any) -> Option<BoxedValue> {
226 value.downcast_ref::<T>().map(|value| Rc::new(value.clone()) as BoxedValue)
227 }
228
229 fn route_create_binding_entry(
230 &'static self,
231 _o: &FerroObject,
232 _frame: std::rc::Weak<dyn crate::property_store::ValueFrame>,
233 _source: Rc<dyn IObservable<BoxedValue>>,
234 ) -> Rc<dyn IValueEntry> {
235 panic!("direct properties have no value entries");
236 }
237}
238
239pub struct DirectProperty<TOwner: ObjectType, T: PropertyValue> {
242 base: DirectPropertyBase<T>,
243 getter: fn(&TOwner) -> T,
244 setter: Option<fn(&TOwner, T)>,
245}
246
247impl<TOwner: ObjectType, T: PropertyValue> Deref for DirectProperty<TOwner, T> {
248 type Target = DirectPropertyBase<T>;
249 #[inline]
250 fn deref(&self) -> &DirectPropertyBase<T> {
251 &self.base
252 }
253}
254
255impl<TOwner: ObjectType, T: PropertyValue> DirectProperty<TOwner, T> {
256 pub(crate) fn create(
257 name: &str,
258 getter: fn(&TOwner) -> T,
259 setter: Option<fn(&TOwner, T)>,
260 metadata: DirectPropertyMetadata<T>,
261 ) -> &'static Self {
262 let base = FerroProperty::new::<T>(
263 name,
264 TOwner::TYPE,
265 FerroPropertyInit { inherits: false, is_attached: false, is_direct: true, is_read_only: setter.is_none() },
266 None,
267 );
268 Self::leak(Self {
269 base: DirectPropertyBase {
270 base,
271 owner: TOwner::TYPE,
272 metadata: MetadataTable::new(TOwner::TYPE, metadata),
273 accessor: OnceCell::new(),
274 },
275 getter,
276 setter,
277 })
278 }
279
280 fn leak(property: Self) -> &'static Self {
281 let property: &'static Self = Box::leak(Box::new(property));
282 let _ = property.base.accessor.set(property);
283 property.base.base.set_routes(&property.base);
284 property
285 }
286
287 #[inline]
289 pub fn getter(&self) -> fn(&TOwner) -> T {
290 self.getter
291 }
292
293 #[inline]
295 pub fn setter(&self) -> Option<fn(&TOwner, T)> {
296 self.setter
297 }
298
299 pub fn add_owner<TNewOwner: ObjectType>(
302 &'static self,
303 getter: fn(&TNewOwner) -> T,
304 setter: Option<fn(&TNewOwner, T)>,
305 metadata: Option<DirectPropertyMetadata<T>>,
306 ) -> &'static DirectProperty<TNewOwner, T> {
307 let metadata = metadata.map(|mut m| {
308 use crate::ferro_property_metadata::PropertyMetadata;
309 m.merge(&self.base.metadata.get(TOwner::TYPE));
310 m.freeze();
311 m
312 });
313 let result = DirectProperty::<TNewOwner, T>::leak(DirectProperty {
314 base: DirectPropertyBase {
315 base: FerroProperty::for_added_owner(&self.base.base, TNewOwner::TYPE, setter.is_none()),
316 owner: TNewOwner::TYPE,
317 metadata: MetadataTable::for_added_owner(&self.base.metadata, TNewOwner::TYPE, metadata),
318 accessor: OnceCell::new(),
319 },
320 getter,
321 setter,
322 });
323 FerroPropertyRegistry::instance().register(TNewOwner::TYPE, result);
324 result
325 }
326
327 fn owner_of<'a>(&self, instance: &'a FerroObject) -> &'a TOwner {
328 instance.downcast_ref::<TOwner>().unwrap_or_else(|| {
329 panic!(
330 "property '{}' is registered on {}, which {} does not derive from",
331 self.name(),
332 TOwner::TYPE,
333 instance.get_type()
334 )
335 })
336 }
337}
338
339impl<TOwner: ObjectType, T: PropertyValue> DirectPropertyAccessor<T> for DirectProperty<TOwner, T> {
340 fn invoke_getter(&self, instance: &FerroObject) -> T {
341 (self.getter)(self.owner_of(instance))
342 }
343
344 fn invoke_setter(&self, instance: &FerroObject, value: BindingValue<T>) {
345 let Some(setter) = self.setter else {
346 panic!("The property {} is readonly.", self.name());
347 };
348 if let Some(value) = value.into_option() {
349 setter(self.owner_of(instance), value);
350 }
351 }
352}
353
354pub(crate) trait DirectPropertyDyn: 'static {
356 fn as_property(&self) -> &FerroProperty;
357 fn as_any(&self) -> &dyn Any;
358}
359
360impl<T: PropertyValue> DirectPropertyDyn for DirectPropertyBase<T> {
361 fn as_property(&self) -> &FerroProperty {
362 &self.base
363 }
364
365 fn as_any(&self) -> &dyn Any {
366 self
367 }
368}