use ferroui_splat::{AppLocator, ILogManager, LogHost};
use ferroui_system::{IDisposable, IObservable, IObserver, ObservableError};
use std::error::Error;
use std::fmt;
use std::rc::Rc;
pub struct BindingErrors;
impl BindingErrors {
pub fn subscribe<T: 'static>(source: Rc<dyn IObservable<T>>, on_next: impl Fn(T) + 'static, binding_expression: impl Into<String>) -> Rc<dyn IDisposable> {
source.subscribe(Rc::new(BindingObserver { on_next: Box::new(on_next), binding_expression: binding_expression.into() }))
}
}
struct BindingObserver<T> {
on_next: Box<dyn Fn(T)>,
binding_expression: String,
}
impl<T> IObserver<T> for BindingObserver<T> {
fn on_next(&self, value: T) {
(self.on_next)(value);
}
fn on_error(&self, error: ObservableError) {
let message = format!("{} Binding received an Exception!", self.binding_expression);
if let Some(log_manager) = AppLocator::get_service::<dyn ILogManager>() {
log_manager.get_logger(std::any::type_name::<LogHost>()).error_exception(&*error, &message);
}
let Some(inner) = error.source() else {
return;
};
std::panic::panic_any(TargetInvocationException::with_inner(message, inner));
}
fn on_completed(&self) {}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TargetInvocationException {
message: String,
inner_exception: Option<String>,
}
impl TargetInvocationException {
pub fn with_inner(message: impl Into<String>, inner_exception: &dyn Error) -> Self {
Self { message: message.into(), inner_exception: Some(inner_exception.to_string()) }
}
pub fn message(&self) -> &str {
&self.message
}
pub fn inner_exception(&self) -> Option<&str> {
self.inner_exception.as_deref()
}
}
impl fmt::Display for TargetInvocationException {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.inner_exception {
Some(inner) => write!(f, "{} ({inner})", self.message),
None => f.write_str(&self.message),
}
}
}
impl Error for TargetInvocationException {}