ferroui_reactiveui/
rx_state.rs1use crate::{RxSchedulers, UnhandledErrorException};
4use ferroui_reactive::concurrency::SchedulerExt;
5use ferroui_reactive::Observer;
6use ferroui_system::{IObserver, ObservableError};
7use std::cell::RefCell;
8use std::rc::Rc;
9
10ferroui_system::ferro_thread_local! {
11 static DEFAULT_EXCEPTION_HANDLER: RefCell<Option<Rc<dyn IObserver<ObservableError>>>> = const { RefCell::new(None) };
13}
14
15const UNHANDLED_ERROR_MESSAGE: &str = "An object implementing IHandleObservableErrors (often a ReactiveCommand or ObservableAsPropertyHelper) has errored, thereby breaking its observable pipeline. To prevent this, ensure the pipeline does not error, or Subscribe to the ThrownExceptions property of the object in question to handle the erroneous case.";
17
18pub struct RxState;
24
25impl RxState {
26 pub fn default_exception_handler() -> Rc<dyn IObserver<ObservableError>> {
34 if let Some(handler) = DEFAULT_EXCEPTION_HANDLER.with(|slot| slot.borrow().clone()) {
35 return handler;
36 }
37 Self::initialize_default_exception_handler()
38 }
39
40 pub fn initialize_exception_handler(exception_handler: Rc<dyn IObserver<ObservableError>>) {
44 DEFAULT_EXCEPTION_HANDLER.with(|slot| {
45 let mut slot = slot.borrow_mut();
46 if slot.is_none() {
47 *slot = Some(exception_handler);
48 }
49 });
50 }
51
52 #[doc(hidden)]
55 pub fn reset_for_testing() {
56 DEFAULT_EXCEPTION_HANDLER.with(|slot| *slot.borrow_mut() = None);
57 }
58
59 fn initialize_default_exception_handler() -> Rc<dyn IObserver<ObservableError>> {
60 let handler = Observer::create(|exception: ObservableError| {
61 let unhandled = UnhandledErrorException::with_inner(UNHANDLED_ERROR_MESSAGE, &*exception);
64 let _ = RxSchedulers::main_thread_scheduler().schedule_action(move || {
65 std::panic::panic_any(unhandled);
66 });
67 });
68 DEFAULT_EXCEPTION_HANDLER.with(|slot| slot.borrow_mut().get_or_insert(handler).clone())
69 }
70}