Skip to main content

aptabase_rs/
lib.rs

1mod client;
2mod config;
3mod dispatcher;
4mod sys;
5
6use std::{panic::PanicHookInfo, sync::Arc, time::Duration};
7
8pub use client::AptabaseClient;
9use config::Config;
10use serde_json::json;
11
12#[derive(Default, Debug, Clone)]
13pub struct InitOptions {
14    pub host: Option<String>,
15    pub flush_interval: Option<Duration>,
16}
17
18/// The Aptabase client builder
19pub struct Builder {
20    app_key: String,
21    app_version: String,
22    enable_polling: bool,
23    panic_hook: Option<PanicHook>,
24    options: InitOptions,
25}
26
27pub type PanicHook =
28    Box<dyn Fn(&AptabaseClient, &PanicHookInfo<'_>, String) + 'static + Sync + Send>;
29
30fn get_panic_message(info: &PanicHookInfo) -> String {
31    let payload = info.payload();
32    if let Some(s) = payload.downcast_ref::<&str>() {
33        return s.to_string();
34    } else if let Some(s) = payload.downcast_ref::<String>() {
35        return s.to_string();
36    }
37
38    format!("{:?}", payload)
39}
40
41impl Builder {
42    /// Creates a new builder.
43    pub fn new(app_key: &str, app_version: &str) -> Self {
44        Self {
45            app_key: app_key.into(),
46            app_version: app_version.into(),
47            enable_polling: false,
48            panic_hook: None,
49            options: Default::default(),
50        }
51    }
52
53    /// Sets custom options to use for the Aptabase client.
54    pub fn with_options(mut self, opts: InitOptions) -> Self {
55        self.options = opts;
56        self
57    }
58
59    /// Sets whether polling should be enabled.
60    pub fn with_polling(mut self, enable: bool) -> Self {
61        self.enable_polling = enable;
62        self
63    }
64
65    /// Sets a custom panic hook.
66    pub fn with_panic_hook(mut self, hook: PanicHook) -> Self {
67        self.panic_hook = Some(hook);
68        self
69    }
70
71    /// Enables the default panic hook.
72    pub fn with_default_panic_hook(self) -> Self {
73        self.with_panic_hook(Box::new(|client, info, message| {
74            let location = info
75                .location()
76                .map(|loc| format!("{}:{}:{}", loc.file(), loc.line(), loc.column()))
77                .unwrap_or_default();
78            let _ = client.track_event(
79                "panic",
80                Some(json!({
81                    "info": format!("{} ({})", message, location),
82                })),
83            );
84        }))
85    }
86
87    /// Builds and initializes the client
88    pub fn build(self) -> Arc<AptabaseClient> {
89        let cfg = Config::new(self.app_key, self.options);
90        let client = Arc::new(AptabaseClient::new(&cfg, self.app_version));
91
92        if self.enable_polling {
93            client.start_polling(cfg.flush_interval);
94        }
95
96        if let Some(hook) = self.panic_hook {
97            let default_panic = std::panic::take_hook();
98            let hook_client = client.clone();
99            std::panic::set_hook(Box::new(move |info| {
100                let msg = get_panic_message(info);
101                hook(&hook_client, info, msg);
102                hook_client.flush_blocking();
103                default_panic(info);
104            }));
105        }
106
107        client
108    }
109}