mod default_feature_usage_tracking;
mod guid;
pub use default_feature_usage_tracking::{
DefaultFeatureUsageTrackingManager, DefaultFeatureUsageTrackingSession,
FuncFeatureUsageTrackingManager,
};
pub use guid::Guid;
use crate::service_location::{AppLocator, ReadonlyDependencyResolverExt};
use ferroui_system::IDisposable;
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
use std::rc::Rc;
pub trait IFeatureUsageTrackingSession: IDisposable {
fn feature_name(&self) -> &str;
fn sub_feature(&self, description: &str) -> Rc<dyn IFeatureUsageTrackingSession>;
fn on_exception(&self, exception: &dyn std::error::Error);
}
pub trait IFeatureUsageTrackingSessionOf<R>: IFeatureUsageTrackingSession {
fn feature_reference(&self) -> R;
fn parent_reference(&self) -> R;
}
pub trait IFeatureUsageTrackingManager {
fn get_feature_usage_tracking_session(&self, feature_name: &str) -> Rc<dyn IFeatureUsageTrackingSession>;
}
pub trait IViewTracking {
fn on_view_navigation(&self, name: &str);
}
pub trait IEnableFeatureUsageTracking {}
#[derive(Debug)]
struct ActionPanicked(String);
impl std::fmt::Display for ActionPanicked {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
impl std::error::Error for ActionPanicked {}
fn panic_message(payload: &(dyn std::any::Any + Send)) -> String {
if let Some(message) = payload.downcast_ref::<&str>() {
(*message).to_owned()
} else if let Some(message) = payload.downcast_ref::<String>() {
message.clone()
} else {
"panic".to_owned()
}
}
fn run_in_session(
session: Rc<dyn IFeatureUsageTrackingSession>,
action: impl FnOnce(&dyn IFeatureUsageTrackingSession),
) {
let result = catch_unwind(AssertUnwindSafe(|| action(&*session)));
if let Err(payload) = result {
session.on_exception(&ActionPanicked(panic_message(payload.as_ref())));
session.dispose();
resume_unwind(payload);
}
session.dispose();
}
pub trait EnableFeatureUsageTrackingExt: IEnableFeatureUsageTracking {
fn feature_usage_tracking_session(&self, feature_name: &str) -> Rc<dyn IFeatureUsageTrackingSession> {
match AppLocator::current().get_service::<dyn IFeatureUsageTrackingManager>() {
None => panic!(
"Feature Usage Tracking Manager is null. This should never happen, your dependency resolver is broken"
),
Some(manager) => manager.get_feature_usage_tracking_session(feature_name),
}
}
fn with_feature_usage_tracking_session(
&self,
feature_name: &str,
action: impl FnOnce(&dyn IFeatureUsageTrackingSession),
) {
run_in_session(self.feature_usage_tracking_session(feature_name), action);
}
}
impl<T: IEnableFeatureUsageTracking + ?Sized> EnableFeatureUsageTrackingExt for T {}
pub trait FeatureUsageTrackingSessionExt: IFeatureUsageTrackingSession {
fn with_sub_feature_usage_tracking_session(
&self,
feature_name: &str,
action: impl FnOnce(&dyn IFeatureUsageTrackingSession),
) {
run_in_session(self.sub_feature(feature_name), action);
}
}
impl<T: IFeatureUsageTrackingSession + ?Sized> FeatureUsageTrackingSessionExt for T {}