use std::{panic::PanicHookInfo, sync::Arc};
use tauri::{
Manager, RunEvent, Runtime,
plugin::{self, TauriPlugin},
};
use crate::{InitOptions, client::TelemetryClient, commands, config::Config};
pub struct Builder {
app_key: String,
panic_hook: Option<PanicHook>,
options: InitOptions,
}
pub type PanicHook =
Box<dyn Fn(&TelemetryClient, &PanicHookInfo<'_>, String) + 'static + Sync + Send>;
fn get_panic_message(info: &PanicHookInfo) -> String {
let payload = info.payload();
if let Some(s) = payload.downcast_ref::<&str>() {
return s.to_string();
} else if let Some(s) = payload.downcast_ref::<String>() {
return s.to_string();
}
format!("{:?}", payload)
}
impl Builder {
pub fn new(app_key: &str) -> Self {
Self {
app_key: app_key.into(),
panic_hook: None,
options: Default::default(),
}
}
pub fn with_options(mut self, opts: InitOptions) -> Self {
self.options = opts;
self
}
pub fn with_host(mut self, host: impl Into<String>) -> Self {
self.options.host = Some(host.into());
self
}
pub fn with_panic_hook(mut self, hook: PanicHook) -> Self {
self.panic_hook = Some(hook);
self
}
pub fn build<R: Runtime>(self) -> TauriPlugin<R> {
plugin::Builder::new("telemetry")
.invoke_handler(tauri::generate_handler![commands::track_event])
.setup(|app, _api| {
let cfg = Config::new(self.app_key, self.options);
let app_version = app.package_info().version.to_string();
let client = Arc::new(TelemetryClient::new(&cfg, app_version));
client.start_polling(cfg.flush_interval);
if let Some(hook) = self.panic_hook {
install_panic_hook(hook, client.clone());
}
app.manage(client);
Ok(())
})
.on_event(move |app, event| {
if let RunEvent::Exit = event {
let client = app.state::<Arc<TelemetryClient>>();
client.flush_blocking();
}
})
.build()
}
}
fn install_panic_hook(hook: PanicHook, client: Arc<TelemetryClient>) {
let default_panic = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
let msg = get_panic_message(info);
hook(&client, info, msg);
client.flush_blocking();
default_panic(info);
}));
}