1use super::{IPropertyInfo, ValueType, ValueTypes};
2use crate::data::BindingError;
3use crate::{AnyValue, BoxedValue, PropertyValue};
4use std::marker::PhantomData;
5use std::rc::Rc;
6
7pub type PropertyGetter = Rc<dyn Fn(&dyn AnyValue) -> Option<BoxedValue>>;
9
10pub type FalliblePropertyGetter = Rc<dyn Fn(&dyn AnyValue) -> Result<Option<BoxedValue>, BindingError>>;
13
14pub type PropertySetter = Rc<dyn Fn(&dyn AnyValue, Option<&BoxedValue>) -> Result<(), BindingError>>;
16
17pub struct ClrPropertyInfo {
20 name: Box<str>,
21 getter: Option<PropertyGetter>,
22 fallible_getter: Option<FalliblePropertyGetter>,
24 setter: Option<PropertySetter>,
25 property_type: ValueType,
26 owner: Option<(&'static str, fn(&dyn AnyValue) -> bool)>,
29 boxed_getter: Option<BoxedPropertyGetter>,
32 boxed_setter: Option<BoxedPropertySetter>,
33}
34
35pub type BoxedPropertyGetter = Rc<dyn Fn(&BoxedValue) -> Result<Option<BoxedValue>, BindingError>>;
38
39pub type BoxedPropertySetter = Rc<dyn Fn(&BoxedValue, Option<&BoxedValue>) -> Result<(), BindingError>>;
42
43pub trait PropertyKind: 'static {
46 type Typed;
48
49 fn value_type() -> ValueType;
51
52 fn register() {}
54
55 fn to_untyped(value: Self::Typed) -> Option<BoxedValue>;
56
57 fn from_untyped(value: Option<&BoxedValue>) -> Option<Self::Typed>;
58}
59
60pub struct Value<T>(PhantomData<T>);
62
63impl<T: PropertyValue> PropertyKind for Value<T> {
64 type Typed = T;
65
66 fn value_type() -> ValueType {
67 ValueType::of::<T>()
68 }
69
70 #[inline]
71 fn to_untyped(value: T) -> Option<BoxedValue> {
72 Some(Rc::new(value))
73 }
74
75 fn from_untyped(value: Option<&BoxedValue>) -> Option<T> {
76 let value = value?;
77 if let Some(v) = value.downcast_ref::<T>() {
78 return Some(v.clone());
79 }
80 let converted = ValueTypes::try_convert(Some(value), ValueType::of::<T>())??;
81 converted.downcast_ref::<T>().cloned()
82 }
83}
84
85pub struct Maybe<T>(PhantomData<T>);
87
88impl<T: PropertyValue> PropertyKind for Maybe<T> {
89 type Typed = Option<T>;
90
91 fn value_type() -> ValueType {
92 ValueType::of::<Option<T>>()
93 }
94
95 fn register() {
96 ValueTypes::register_nullable::<T>();
97 }
98
99 #[inline]
100 fn to_untyped(value: Option<T>) -> Option<BoxedValue> {
101 value.map(|v| Rc::new(v) as BoxedValue)
102 }
103
104 fn from_untyped(value: Option<&BoxedValue>) -> Option<Option<T>> {
105 let Some(value) = value else { return Some(None) };
106 if let Some(v) = value.downcast_ref::<Option<T>>() {
107 return Some(v.clone());
108 }
109 Value::<T>::from_untyped(Some(value)).map(Some)
110 }
111}
112
113pub struct ModelRef<M>(PhantomData<M>);
116
117impl<M: PartialEq + 'static> PropertyKind for ModelRef<M> {
118 type Typed = Option<Rc<M>>;
119
120 fn value_type() -> ValueType {
121 ValueType::of::<M>()
122 }
123
124 fn register() {
125 ValueTypes::register_reference::<M>();
126 }
127
128 #[inline]
129 fn to_untyped(value: Option<Rc<M>>) -> Option<BoxedValue> {
130 value.map(|v| v as BoxedValue)
131 }
132
133 fn from_untyped(value: Option<&BoxedValue>) -> Option<Option<Rc<M>>> {
134 let Some(value) = value else { return Some(None) };
135 if let Some(v) = value.downcast_ref::<Option<Rc<M>>>() {
136 return Some(v.clone());
137 }
138 let any: Rc<dyn std::any::Any> = value.clone();
139 any.downcast::<M>().ok().map(Some)
140 }
141}
142
143pub struct Untyped;
146
147impl PropertyKind for Untyped {
148 type Typed = Option<BoxedValue>;
149
150 fn value_type() -> ValueType {
151 ValueType::object()
152 }
153
154 #[inline]
155 fn to_untyped(value: Option<BoxedValue>) -> Option<BoxedValue> {
156 value
157 }
158
159 fn from_untyped(value: Option<&BoxedValue>) -> Option<Option<BoxedValue>> {
160 match value {
161 None => Some(None),
162 Some(v) => match v.downcast_ref::<Option<BoxedValue>>() {
163 Some(inner) => Some(inner.clone()),
164 None => Some(Some(v.clone())),
165 },
166 }
167 }
168}
169
170fn invalid_cast(name: &str, value: Option<&BoxedValue>, target: ValueType) -> BindingError {
171 BindingError::message(format!(
172 "Object of type '{}' cannot be converted to type '{}' for property '{}'.",
173 value.map_or("null", |v| (**v).type_name()),
174 target,
175 name
176 ))
177}
178
179impl ClrPropertyInfo {
180 pub fn new(
182 name: &str,
183 getter: Option<PropertyGetter>,
184 setter: Option<PropertySetter>,
185 property_type: ValueType,
186 ) -> Self {
187 Self { name: name.into(), getter, fallible_getter: None, setter, property_type, owner: None, boxed_getter: None, boxed_setter: None }
188 }
189
190 pub fn new_fallible(
193 name: &str,
194 getter: Option<FalliblePropertyGetter>,
195 setter: Option<PropertySetter>,
196 property_type: ValueType,
197 ) -> Self {
198 Self { name: name.into(), getter: None, fallible_getter: getter, setter, property_type, owner: None, boxed_getter: None, boxed_setter: None }
199 }
200
201 pub fn with_boxed_accessors(
208 mut self,
209 getter: Option<BoxedPropertyGetter>,
210 setter: Option<BoxedPropertySetter>,
211 ) -> Self {
212 self.boxed_getter = getter;
213 self.boxed_setter = setter;
214 self
215 }
216
217 fn with_owner<O: 'static>(mut self) -> Self {
218 fn is_owner<O: 'static>(value: &dyn AnyValue) -> bool {
219 value.downcast_ref::<O>().is_some()
220 }
221 self.owner = Some((ValueType::of::<O>().name(), is_owner::<O>));
222 self
223 }
224
225 fn typed_getter<O: 'static, K: PropertyKind>(get: impl Fn(&O) -> K::Typed + 'static) -> PropertyGetter {
226 Rc::new(move |o: &dyn AnyValue| o.downcast_ref::<O>().and_then(|o| K::to_untyped(get(o))))
227 }
228
229 fn typed_fallible_getter<O: 'static, K: PropertyKind>(
230 get: impl Fn(&O) -> Result<K::Typed, BindingError> + 'static,
231 ) -> FalliblePropertyGetter {
232 Rc::new(move |o: &dyn AnyValue| match o.downcast_ref::<O>() {
233 Some(o) => get(o).map(K::to_untyped),
234 None => Ok(None),
235 })
236 }
237
238 fn typed_setter<O: 'static, K: PropertyKind>(
239 name: &str,
240 set: impl Fn(&O, K::Typed) -> Result<(), BindingError> + 'static,
241 ) -> PropertySetter {
242 let owned_name: Box<str> = name.into();
243 Rc::new(move |o: &dyn AnyValue, v: Option<&BoxedValue>| {
244 let Some(o) = o.downcast_ref::<O>() else {
245 return Err(BindingError::message("The property owner is of the wrong type."));
246 };
247 match K::from_untyped(v) {
248 Some(v) => set(o, v),
249 None => Err(invalid_cast(&owned_name, v, K::value_type())),
250 }
251 })
252 }
253
254 pub fn read_only_fallible<O: 'static, K: PropertyKind>(
257 name: &str,
258 get: impl Fn(&O) -> Result<K::Typed, BindingError> + 'static,
259 ) -> Self {
260 K::register();
261 Self::new_fallible(name, Some(Self::typed_fallible_getter::<O, K>(get)), None, K::value_type())
262 .with_owner::<O>()
263 }
264
265 pub fn read_write_fallible<O: 'static, K: PropertyKind>(
268 name: &str,
269 get: impl Fn(&O) -> Result<K::Typed, BindingError> + 'static,
270 set: impl Fn(&O, K::Typed) -> Result<(), BindingError> + 'static,
271 ) -> Self {
272 K::register();
273 Self::new_fallible(
274 name,
275 Some(Self::typed_fallible_getter::<O, K>(get)),
276 Some(Self::typed_setter::<O, K>(name, set)),
277 K::value_type(),
278 )
279 .with_owner::<O>()
280 }
281
282 pub fn read_only<O: 'static, K: PropertyKind>(name: &str, get: impl Fn(&O) -> K::Typed + 'static) -> Self {
284 K::register();
285 Self::new(name, Some(Self::typed_getter::<O, K>(get)), None, K::value_type()).with_owner::<O>()
286 }
287
288 pub fn read_write<O: 'static, K: PropertyKind>(
290 name: &str,
291 get: impl Fn(&O) -> K::Typed + 'static,
292 set: impl Fn(&O, K::Typed) + 'static,
293 ) -> Self {
294 Self::read_write_validated::<O, K>(name, get, move |o, v| {
295 set(o, v);
296 Ok(())
297 })
298 }
299
300 pub fn read_write_validated<O: 'static, K: PropertyKind>(
302 name: &str,
303 get: impl Fn(&O) -> K::Typed + 'static,
304 set: impl Fn(&O, K::Typed) -> Result<(), BindingError> + 'static,
305 ) -> Self {
306 K::register();
307 let setter = Self::typed_setter::<O, K>(name, set);
308 Self::new(name, Some(Self::typed_getter::<O, K>(get)), Some(setter), K::value_type()).with_owner::<O>()
309 }
310}
311
312impl IPropertyInfo for ClrPropertyInfo {
313 fn as_any(&self) -> Option<&dyn std::any::Any> {
314 Some(self)
315 }
316
317 fn name(&self) -> &str {
318 &self.name
319 }
320
321 fn get(&self, target: &dyn AnyValue) -> Option<BoxedValue> {
322 match (&self.getter, &self.fallible_getter) {
323 (Some(getter), _) => getter(target),
324 (None, Some(getter)) => getter(target).ok().flatten(),
327 (None, None) => panic!("Property {} doesn't have a getter.", self.name),
328 }
329 }
330
331 fn try_get(&self, target: &dyn AnyValue) -> Result<Option<BoxedValue>, BindingError> {
332 if let Some((owner_name, is_owner)) = self.owner {
333 if !is_owner(target) {
334 return Err(BindingError::message(format!(
335 "Unable to cast object of type '{}' to type '{}'.",
336 target.type_name(),
337 owner_name
338 )));
339 }
340 }
341 match &self.fallible_getter {
342 Some(getter) if self.getter.is_none() => getter(target),
343 _ => Ok(self.get(target)),
344 }
345 }
346
347 fn set(&self, target: &dyn AnyValue, value: Option<&BoxedValue>) -> Result<(), BindingError> {
348 match &self.setter {
349 Some(setter) => setter(target, value),
350 None => panic!("Property {} doesn't have a setter.", self.name),
351 }
352 }
353
354 fn get_boxed(&self, target: &BoxedValue) -> Option<BoxedValue> {
355 match &self.boxed_getter {
356 Some(getter) => getter(target).ok().flatten(),
357 None => self.get(&**target),
358 }
359 }
360
361 fn try_get_boxed(&self, target: &BoxedValue) -> Result<Option<BoxedValue>, BindingError> {
362 match &self.boxed_getter {
363 Some(getter) => getter(target),
364 None => self.try_get(&**target),
365 }
366 }
367
368 fn set_boxed(&self, target: &BoxedValue, value: Option<&BoxedValue>) -> Result<(), BindingError> {
369 match &self.boxed_setter {
370 Some(setter) => setter(target, value),
371 None => self.set(&**target, value),
372 }
373 }
374
375 fn can_set(&self) -> bool {
376 self.setter.is_some() || self.boxed_setter.is_some()
377 }
378
379 fn can_get(&self) -> bool {
380 self.getter.is_some() || self.fallible_getter.is_some() || self.boxed_getter.is_some()
381 }
382
383 fn property_type(&self) -> ValueType {
384 self.property_type
385 }
386}