ferroui_splat/application_performance_monitoring/
mod.rs1mod default_feature_usage_tracking;
5mod guid;
6
7pub use default_feature_usage_tracking::{
8 DefaultFeatureUsageTrackingManager, DefaultFeatureUsageTrackingSession,
9 FuncFeatureUsageTrackingManager,
10};
11pub use guid::Guid;
12
13use crate::service_location::{AppLocator, ReadonlyDependencyResolverExt};
14use ferroui_system::IDisposable;
15use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
16use std::rc::Rc;
17
18pub trait IFeatureUsageTrackingSession: IDisposable {
21 fn feature_name(&self) -> &str;
23
24 fn sub_feature(&self, description: &str) -> Rc<dyn IFeatureUsageTrackingSession>;
26
27 fn on_exception(&self, exception: &dyn std::error::Error);
29}
30
31pub trait IFeatureUsageTrackingSessionOf<R>: IFeatureUsageTrackingSession {
34 fn feature_reference(&self) -> R;
36
37 fn parent_reference(&self) -> R;
39}
40
41pub trait IFeatureUsageTrackingManager {
43 fn get_feature_usage_tracking_session(&self, feature_name: &str) -> Rc<dyn IFeatureUsageTrackingSession>;
45}
46
47pub trait IViewTracking {
49 fn on_view_navigation(&self, name: &str);
51}
52
53pub trait IEnableFeatureUsageTracking {}
56
57#[derive(Debug)]
60struct ActionPanicked(String);
61
62impl std::fmt::Display for ActionPanicked {
63 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64 f.write_str(&self.0)
65 }
66}
67
68impl std::error::Error for ActionPanicked {}
69
70fn panic_message(payload: &(dyn std::any::Any + Send)) -> String {
71 if let Some(message) = payload.downcast_ref::<&str>() {
72 (*message).to_owned()
73 } else if let Some(message) = payload.downcast_ref::<String>() {
74 message.clone()
75 } else {
76 "panic".to_owned()
77 }
78}
79
80fn run_in_session(
83 session: Rc<dyn IFeatureUsageTrackingSession>,
84 action: impl FnOnce(&dyn IFeatureUsageTrackingSession),
85) {
86 let result = catch_unwind(AssertUnwindSafe(|| action(&*session)));
87 if let Err(payload) = result {
88 session.on_exception(&ActionPanicked(panic_message(payload.as_ref())));
89 session.dispose();
90 resume_unwind(payload);
91 }
92 session.dispose();
93}
94
95pub trait EnableFeatureUsageTrackingExt: IEnableFeatureUsageTracking {
99 fn feature_usage_tracking_session(&self, feature_name: &str) -> Rc<dyn IFeatureUsageTrackingSession> {
104 match AppLocator::current().get_service::<dyn IFeatureUsageTrackingManager>() {
105 None => panic!(
106 "Feature Usage Tracking Manager is null. This should never happen, your dependency resolver is broken"
107 ),
108 Some(manager) => manager.get_feature_usage_tracking_session(feature_name),
109 }
110 }
111
112 fn with_feature_usage_tracking_session(
115 &self,
116 feature_name: &str,
117 action: impl FnOnce(&dyn IFeatureUsageTrackingSession),
118 ) {
119 run_in_session(self.feature_usage_tracking_session(feature_name), action);
120 }
121}
122
123impl<T: IEnableFeatureUsageTracking + ?Sized> EnableFeatureUsageTrackingExt for T {}
124
125pub trait FeatureUsageTrackingSessionExt: IFeatureUsageTrackingSession {
127 fn with_sub_feature_usage_tracking_session(
130 &self,
131 feature_name: &str,
132 action: impl FnOnce(&dyn IFeatureUsageTrackingSession),
133 ) {
134 run_in_session(self.sub_feature(feature_name), action);
135 }
136}
137
138impl<T: IFeatureUsageTrackingSession + ?Sized> FeatureUsageTrackingSessionExt for T {}