use serde_json::{Value, json};
use std::{
sync::{Arc, Mutex as SyncMutex},
time::Duration,
};
use time::{OffsetDateTime, format_description::well_known::Rfc3339};
use crate::{
config::Config,
dispatcher::EventDispatcher,
session::TrackingSession,
sys::{self, SystemProperties},
};
pub struct TelemetryClient {
is_enabled: bool,
session: SyncMutex<TrackingSession>,
dispatcher: Arc<EventDispatcher>,
app_version: String,
sdk_name: String,
sys_info: SystemProperties,
}
impl TelemetryClient {
pub fn new(config: &Config, app_version: String) -> Self {
let sys_info = sys::get_info();
let is_enabled = !config.app_key.is_empty();
let dispatcher = Arc::new(EventDispatcher::new(config, &sys_info));
Self {
is_enabled,
dispatcher,
session: SyncMutex::new(TrackingSession::new()),
app_version,
sdk_name: config.sdk_name.clone(),
sys_info,
}
}
pub(crate) fn start_polling(&self, interval: Duration) {
let dispatcher = self.dispatcher.clone();
tauri::async_runtime::spawn(async move {
loop {
tokio::time::sleep(interval).await;
dispatcher.flush().await;
}
});
}
pub fn track_event(&self, name: &str, props: Option<Value>) -> Result<(), String> {
if !self.is_enabled {
return Ok(());
}
if let Some(props) = &props
&& !matches!(props, Value::Object(_))
{
return Err(
"props must be `None` or the `Object` variation of `serde_json::Value`".to_owned(),
);
}
let session_id = self
.session
.lock()
.expect("could not lock session")
.eval_id();
let ev = json!({
"timestamp": OffsetDateTime::now_utc().format(&Rfc3339).unwrap(),
"sessionId": session_id,
"eventName": name,
"systemProps": {
"isDebug": self.sys_info.is_debug,
"osName": self.sys_info.os_name,
"osVersion": self.sys_info.os_version,
"locale": self.sys_info.locale,
"engineName": self.sys_info.engine_name,
"engineVersion": self.sys_info.engine_version,
"appVersion": self.app_version,
"sdkVersion": self.sdk_name
},
"props": props
});
self.dispatcher.enqueue(ev);
Ok(())
}
pub async fn flush(&self) {
self.dispatcher.flush().await;
}
pub fn flush_blocking(&self) {
tauri::async_runtime::block_on(async {
self.flush().await;
});
}
}