Skip to main content

tauri_plugin_telemetry/
builder.rs

1use std::{panic::PanicHookInfo, sync::Arc};
2
3use tauri::{
4    Manager, RunEvent, Runtime,
5    plugin::{self, TauriPlugin},
6};
7
8use crate::{InitOptions, client::TelemetryClient, commands, config::Config};
9
10/// The telemetry plugin builder.
11pub struct Builder {
12    app_key: String,
13    panic_hook: Option<PanicHook>,
14    options: InitOptions,
15}
16
17pub type PanicHook =
18    Box<dyn Fn(&TelemetryClient, &PanicHookInfo<'_>, String) + 'static + Sync + Send>;
19
20fn get_panic_message(info: &PanicHookInfo) -> String {
21    let payload = info.payload();
22    if let Some(s) = payload.downcast_ref::<&str>() {
23        return s.to_string();
24    } else if let Some(s) = payload.downcast_ref::<String>() {
25        return s.to_string();
26    }
27
28    format!("{:?}", payload)
29}
30
31impl Builder {
32    /// Creates a new builder.
33    pub fn new(app_key: &str) -> Self {
34        Self {
35            app_key: app_key.into(),
36            panic_hook: None,
37            options: Default::default(),
38        }
39    }
40
41    /// Sets custom options to use for the telemetry client.
42    pub fn with_options(mut self, opts: InitOptions) -> Self {
43        self.options = opts;
44        self
45    }
46
47    /// Sets a custom panic hook.
48    pub fn with_panic_hook(mut self, hook: PanicHook) -> Self {
49        self.panic_hook = Some(hook);
50        self
51    }
52
53    /// Builds and initializes the plugin.
54    pub fn build<R: Runtime>(self) -> TauriPlugin<R> {
55        plugin::Builder::new("telemetry")
56            .invoke_handler(tauri::generate_handler![commands::track_event])
57            .setup(|app, _api| {
58                let cfg = Config::new(self.app_key, self.options);
59                let app_version = app.package_info().version.to_string();
60                let client = Arc::new(TelemetryClient::new(&cfg, app_version));
61
62                client.start_polling(cfg.flush_interval);
63
64                if let Some(hook) = self.panic_hook {
65                    install_panic_hook(hook, client.clone());
66                }
67
68                app.manage(client);
69                Ok(())
70            })
71            .on_event(move |app, event| {
72                if let RunEvent::Exit = event {
73                    let client = app.state::<Arc<TelemetryClient>>();
74                    client.flush_blocking();
75                }
76            })
77            .build()
78    }
79}
80
81/// Installs a custom panic hook that flushes telemetry events before the
82/// default panic handler runs.
83fn install_panic_hook(hook: PanicHook, client: Arc<TelemetryClient>) {
84    let default_panic = std::panic::take_hook();
85    std::panic::set_hook(Box::new(move |info| {
86        let msg = get_panic_message(info);
87        hook(&client, info, msg);
88        client.flush_blocking();
89        default_panic(info);
90    }));
91}