use crate::{BindingConverters, ConverterType, IBindingTypeConverter};
use std::any::{Any, TypeId};
use std::rc::Rc;
pub(crate) type Convert<F, T> = Rc<dyn Fn(F) -> Option<T>>;
const BUILT_IN_AFFINITY: i32 = 2;
pub(crate) fn registered_converter<F: 'static, T: 'static>() -> Option<Rc<dyn IBindingTypeConverter>> {
let converter = BindingConverters::current().typed_converters().try_get_converter(ConverterType::of::<F>(), ConverterType::of::<T>())?;
let same_type = TypeId::of::<F>() == TypeId::of::<T>();
(!same_type || converter.get_affinity_for_objects() > BUILT_IN_AFFINITY).then_some(converter)
}
pub(crate) fn converting<F: 'static, T: Clone + 'static>(converter_override: Option<Rc<dyn IBindingTypeConverter>>, conversion_hint: Option<Rc<dyn Any>>) -> Convert<F, T> {
let converter = converter_override.or_else(registered_converter::<F, T>);
Rc::new(move |value: F| {
if let Some(converter) = &converter {
if let Some(converted) = converter.try_convert_typed(Some(&value as &dyn Any), conversion_hint.as_deref()) {
return unshare(converted);
}
}
pass_through(value)
})
}
pub(crate) fn selecting<F: 'static, T: 'static>(selector: impl Fn(F) -> T + 'static) -> Convert<F, T> {
Rc::new(move |value: F| Some(selector(value)))
}
fn pass_through<F: 'static, T: 'static>(value: F) -> Option<T> {
let boxed: Box<dyn Any> = Box::new(value);
boxed.downcast::<T>().ok().map(|value| *value)
}
fn unshare<T: Clone + 'static>(converted: Rc<dyn Any>) -> Option<T> {
let converted = converted.downcast::<T>().ok()?;
Some(Rc::try_unwrap(converted).unwrap_or_else(|shared| (*shared).clone()))
}