use super::{IReactiveUIBindingBuilder, IReactiveUIBindingInstance, ReactiveUIBindingModule, RxBindingBuilder};
use crate::binding::interfaces::{ICreatesCommandBinding, IViewLocator};
use crate::binding::view::{DefaultViewLocator, ViewMappingBuilder};
use crate::{register_default_converters, BindingConverters, ConverterService, IBindingFallbackConverter, IBindingTypeConverter, ISetMethodBindingConverter};
use ferroui_splat::{
AppBuilder, IAppBuilder, IAppInstance, IModule, IMutableDependencyResolver, IReadonlyDependencyResolver, MutableDependencyResolverExt,
ServiceLocationInitializationExt,
};
use std::cell::{Cell, RefCell};
use std::fmt;
use std::rc::{Rc, Weak};
ferroui_system::ferro_thread_local! {
static BUILDERS: RefCell<Vec<Weak<ReactiveUIBindingBuilder>>> = const { RefCell::new(Vec::new()) };
}
pub struct ReactiveUIBindingBuilder {
inner: AppBuilder,
core_registered: Cell<bool>,
converter_service: Rc<ConverterService>,
}
impl fmt::Debug for ReactiveUIBindingBuilder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ReactiveUIBindingBuilder").field("core_registered", &self.core_registered.get()).finish_non_exhaustive()
}
}
impl ReactiveUIBindingBuilder {
pub fn new(resolver: Rc<dyn IMutableDependencyResolver>, current: Option<Rc<dyn IReadonlyDependencyResolver>>) -> Rc<Self> {
let inner = AppBuilder::with_current(resolver.clone(), current);
let converter_service = Rc::new(ConverterService::new());
resolver.initialize_splat();
resolver.register_constant::<ConverterService>(converter_service.clone());
let builder = Rc::new(Self { inner, core_registered: Cell::new(false), converter_service });
BUILDERS.with(|builders| {
let mut builders = builders.borrow_mut();
builders.retain(|registered| registered.strong_count() > 0);
builders.push(Rc::downgrade(&builder));
});
builder
}
pub fn find(app_builder: &dyn IAppBuilder) -> Option<Rc<ReactiveUIBindingBuilder>> {
let address = app_builder as *const dyn IAppBuilder;
BUILDERS.with(|builders| {
builders
.borrow()
.iter()
.find(|registered| std::ptr::addr_eq(registered.as_ptr(), address))
.and_then(Weak::upgrade)
})
}
pub fn converter_service(&self) -> &Rc<ConverterService> {
&self.converter_service
}
pub fn resolver(&self) -> Rc<dyn IMutableDependencyResolver> {
IAppInstance::current_mutable(&self.inner)
}
pub fn with_core_services(self: &Rc<Self>) -> Rc<Self> {
self.register_core_services();
self.clone()
}
pub fn with_platform_module<M: IModule + 'static>(self: &Rc<Self>, module: M) -> Rc<Self> {
let _ = self.inner.using_module(Rc::new(module));
self.clone()
}
pub fn with_registration(self: &Rc<Self>, configure_action: impl FnOnce(&dyn IMutableDependencyResolver)) -> Rc<Self> {
configure_action(&*self.resolver());
self.clone()
}
pub fn with_converter(self: &Rc<Self>, converter: Rc<dyn IBindingTypeConverter>) -> Rc<Self> {
self.converter_service.typed_converters().register(converter);
self.clone()
}
pub fn with_fallback_converter(self: &Rc<Self>, converter: Rc<dyn IBindingFallbackConverter>) -> Rc<Self> {
self.converter_service.fallback_converters().register(converter);
self.clone()
}
pub fn with_set_method_converter(self: &Rc<Self>, converter: Rc<dyn ISetMethodBindingConverter>) -> Rc<Self> {
self.converter_service.set_method_converters().register(converter);
self.clone()
}
pub fn with_command_binder(self: &Rc<Self>, binder: Rc<dyn ICreatesCommandBinding>) -> Rc<Self> {
self.resolver().register_lazy_singleton::<dyn ICreatesCommandBinding>(move || Some(binder.clone()));
self.clone()
}
pub fn configure_view_locator(self: &Rc<Self>, configure: impl FnOnce(&ViewMappingBuilder<'_>)) -> Rc<Self> {
let locator = Rc::new(DefaultViewLocator::new());
configure(&ViewMappingBuilder::new(&locator));
self.resolver().register_constant::<dyn IViewLocator>(locator);
self.clone()
}
pub fn build_app(self: &Rc<Self>) -> Rc<dyn IReactiveUIBindingInstance> {
self.build_app_core();
self.clone()
}
fn register_core_services(&self) {
if self.core_registered.get() {
return;
}
register_default_converters(&self.converter_service);
let _ = self.inner.using_module(Rc::new(ReactiveUIBindingModule));
self.resolver().register_lazy_singleton::<dyn IViewLocator>(|| Some(Rc::new(DefaultViewLocator::new()) as Rc<dyn IViewLocator>));
self.core_registered.set(true);
}
fn build_app_core(&self) {
let _ = IAppBuilder::build(self);
if IAppInstance::current(self).is_none() {
panic!("InvalidOperationException: Failed to create ReactiveUIBindingInstance instance");
}
BindingConverters::set_service(self.converter_service.clone());
RxBindingBuilder::mark_as_initialized();
}
}
impl IAppInstance for ReactiveUIBindingBuilder {
fn current(&self) -> Option<Rc<dyn IReadonlyDependencyResolver>> {
IAppInstance::current(&self.inner)
}
fn current_mutable(&self) -> Rc<dyn IMutableDependencyResolver> {
IAppInstance::current_mutable(&self.inner)
}
}
impl IReactiveUIBindingInstance for ReactiveUIBindingBuilder {}
impl IAppBuilder for ReactiveUIBindingBuilder {
fn build(&self) -> &dyn IAppInstance {
if AppBuilder::has_been_built() {
return self;
}
self.register_core_services();
let _ = self.inner.build();
self
}
fn use_current_splat_locator(&self) -> &dyn IAppBuilder {
let _ = self.inner.use_current_splat_locator();
self
}
fn using_module(&self, registration_module: Rc<dyn IModule>) -> &dyn IAppBuilder {
let _ = self.inner.using_module(registration_module);
self
}
fn with_core_services(&self) -> &dyn IAppBuilder {
self.register_core_services();
self
}
fn with_custom_registration(&self, configure_action: Rc<dyn Fn(&dyn IMutableDependencyResolver)>) -> &dyn IAppBuilder {
let _ = self.inner.with_custom_registration(configure_action);
self
}
}
impl IReactiveUIBindingBuilder for ReactiveUIBindingBuilder {
fn with_platform_module(&self, module: Rc<dyn IModule>) -> &dyn IReactiveUIBindingBuilder {
let _ = self.inner.using_module(module);
self
}
fn with_registration(&self, configure_action: &dyn Fn(&dyn IMutableDependencyResolver)) -> &dyn IReactiveUIBindingBuilder {
configure_action(&*self.resolver());
self
}
fn with_converter(&self, converter: Rc<dyn IBindingTypeConverter>) -> &dyn IReactiveUIBindingBuilder {
self.converter_service.typed_converters().register(converter);
self
}
fn with_fallback_converter(&self, converter: Rc<dyn IBindingFallbackConverter>) -> &dyn IReactiveUIBindingBuilder {
self.converter_service.fallback_converters().register(converter);
self
}
fn with_set_method_converter(&self, converter: Rc<dyn ISetMethodBindingConverter>) -> &dyn IReactiveUIBindingBuilder {
self.converter_service.set_method_converters().register(converter);
self
}
fn with_command_binder(&self, binder: Rc<dyn ICreatesCommandBinding>) -> &dyn IReactiveUIBindingBuilder {
self.resolver().register_lazy_singleton::<dyn ICreatesCommandBinding>(move || Some(binder.clone()));
self
}
fn configure_view_locator(&self, configure: &dyn Fn(&ViewMappingBuilder<'_>)) -> &dyn IReactiveUIBindingBuilder {
let locator = Rc::new(DefaultViewLocator::new());
configure(&ViewMappingBuilder::new(&locator));
self.resolver().register_constant::<dyn IViewLocator>(locator);
self
}
fn build_app(&self) -> &dyn IReactiveUIBindingInstance {
self.build_app_core();
self
}
}