use std::sync::{Arc, RwLock, RwLockReadGuard};
#[cfg(feature = "online")]
use std::thread::{self, sleep};
use crate::{datafile::Datafile, DecideOptions};
#[cfg(feature = "online")]
use crate::event_api::{EventDispatcher, SimpleEventDispatcher};
pub use initialization::UninitializedClient;
pub use user_context::UserContext;
mod initialization;
mod user_context;
pub struct Client {
datafile_lock: Arc<RwLock<Datafile>>,
default_decide_options: DecideOptions,
#[cfg(feature = "online")]
event_dispatcher: Box<dyn EventDispatcher>,
}
type DatafileReadGuard<'a> = RwLockReadGuard<'a, Datafile>;
impl From<UninitializedClient> for Client {
fn from(options: UninitializedClient) -> Self {
#[cfg(feature = "online")]
let event_dispatcher = options
.event_dispatcher
.unwrap_or_else(|| Box::new(SimpleEventDispatcher::new(&options.datafile)));
let default_decide_options = options.default_decide_options.unwrap_or_default();
#[cfg(not(feature = "online"))]
let datafile_lock = Arc::new(RwLock::new(options.datafile));
#[cfg(feature = "online")]
let datafile_lock = {
let sdk_key = options.datafile.sdk_key().to_owned();
let mut current_revision = options.datafile.revision();
let datafile_lock = Arc::new(RwLock::new(options.datafile));
let datafile_lock_clone = datafile_lock.clone();
if let Some(interval) = options.update_interval {
thread::spawn(move || {
log::debug!("Starting thread for datafile polling");
loop {
log::debug!("Fetching latest datafile");
if let Ok(datafile) = Datafile::from_sdk_key(&sdk_key) {
let latest_revision = datafile.revision();
if current_revision < latest_revision {
log::info!("Updating datafile from {current_revision} to {latest_revision}");
if let Ok(mut lock_guard) = datafile_lock_clone.write() {
*lock_guard = datafile;
current_revision = latest_revision;
} else {
log::error!("Failed to acquire write lock on datafile")
}
}
}
sleep(interval);
}
});
}
datafile_lock
};
Client {
datafile_lock,
default_decide_options,
#[cfg(feature = "online")]
event_dispatcher,
}
}
}
impl Client {
pub fn create_user_context<'a>(&'a self, user_id: &'a str) -> UserContext<'a> {
UserContext::new(self, user_id)
}
pub fn datafile(&self) -> DatafileReadGuard<'_> {
let lock_result = self.datafile_lock.read();
lock_result.expect("The read/write lock on datafile should not be poisoned.")
}
pub fn default_decide_options(&self) -> &DecideOptions {
&self.default_decide_options
}
#[cfg(feature = "online")]
pub fn event_dispatcher(&self) -> &dyn EventDispatcher {
&*self.event_dispatcher
}
}