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
18pub 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 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 pub fn with_options(mut self, opts: InitOptions) -> Self {
55 self.options = opts;
56 self
57 }
58
59 pub fn with_polling(mut self, enable: bool) -> Self {
61 self.enable_polling = enable;
62 self
63 }
64
65 pub fn with_panic_hook(mut self, hook: PanicHook) -> Self {
67 self.panic_hook = Some(hook);
68 self
69 }
70
71 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 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}