use crate::cache::EmptyConfigCache;
use crate::constants::{SDK_KEY_PREFIX, SDK_KEY_PROXY_PREFIX, SDK_KEY_SECTION_LENGTH};
use crate::errors::{ClientError, ErrorKind};
use crate::model::enums::DataGovernance;
use crate::modes::PollingMode;
use crate::r#override::{FlagOverrides, OptionalOverrides};
use crate::{Client, ConfigCache, OverrideBehavior, OverrideDataSource, User};
use std::borrow::Borrow;
use std::fmt::{Debug, Formatter};
use std::time::Duration;
pub struct Options {
sdk_key: String,
offline: bool,
base_url: Option<String>,
data_governance: DataGovernance,
http_timeout: Duration,
cache: Box<dyn ConfigCache>,
overrides: Option<FlagOverrides>,
polling_mode: PollingMode,
default_user: Option<User>,
}
impl Options {
pub(crate) fn sdk_key(&self) -> &str {
&self.sdk_key
}
pub(crate) fn offline(&self) -> bool {
self.offline
}
pub(crate) fn base_url(&self) -> Option<&String> {
self.base_url.as_ref()
}
pub(crate) fn data_governance(&self) -> &DataGovernance {
&self.data_governance
}
pub(crate) fn http_timeout(&self) -> &Duration {
&self.http_timeout
}
pub(crate) fn cache(&self) -> &dyn ConfigCache {
self.cache.borrow()
}
pub(crate) fn polling_mode(&self) -> &PollingMode {
&self.polling_mode
}
pub(crate) fn overrides(&self) -> Option<&FlagOverrides> {
self.overrides.as_ref()
}
pub(crate) fn default_user(&self) -> Option<&User> {
self.default_user.as_ref()
}
}
impl Debug for Options {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Options")
.field("sdk_key", &self.sdk_key)
.field("offline", &self.offline)
.field("base_url", &self.base_url)
.field("data_governance", &self.data_governance)
.field("http_timeout", &self.http_timeout)
.field("overrides", &self.overrides)
.field("polling_mode", &self.polling_mode)
.field("default_user", &self.default_user)
.finish_non_exhaustive()
}
}
pub struct ClientBuilder {
sdk_key: String,
base_url: Option<String>,
data_governance: Option<DataGovernance>,
http_timeout: Option<Duration>,
cache: Option<Box<dyn ConfigCache>>,
overrides: Option<FlagOverrides>,
offline: bool,
polling_mode: Option<PollingMode>,
default_user: Option<User>,
}
impl ClientBuilder {
pub(crate) fn new(sdk_key: &str) -> Self {
Self {
sdk_key: sdk_key.to_owned(),
offline: false,
http_timeout: None,
base_url: None,
cache: None,
polling_mode: None,
data_governance: None,
overrides: None,
default_user: None,
}
}
pub fn offline(mut self, offline: bool) -> Self {
self.offline = offline;
self
}
pub fn http_timeout(mut self, timeout: Duration) -> Self {
self.http_timeout = Some(timeout);
self
}
pub fn base_url(mut self, base_url: &str) -> Self {
self.base_url = Some(base_url.to_owned());
self
}
pub fn data_governance(mut self, data_governance: DataGovernance) -> Self {
self.data_governance = Some(data_governance);
self
}
pub fn cache(mut self, cache: Box<dyn ConfigCache>) -> Self {
self.cache = Some(cache);
self
}
pub fn polling_mode(mut self, polling_mode: PollingMode) -> Self {
self.polling_mode = Some(polling_mode);
self
}
pub fn default_user(mut self, user: User) -> Self {
self.default_user = Some(user);
self
}
pub fn overrides(
mut self,
source: Box<dyn OverrideDataSource>,
behavior: OverrideBehavior,
) -> Self {
self.overrides = Some(FlagOverrides::new(source, behavior));
self
}
pub fn build(self) -> Result<Client, ClientError> {
if self.sdk_key.is_empty() {
return Err(ClientError::new(
ErrorKind::InvalidSdkKey,
"SDK Key cannot be empty".to_owned(),
));
}
if !self.overrides.is_local()
&& !is_sdk_key_valid(self.sdk_key.as_str(), self.base_url.is_some())
{
return Err(ClientError::new(
ErrorKind::InvalidSdkKey,
format!("SDK Key '{}' is invalid.", self.sdk_key),
));
}
Client::with_options(self.build_options())
}
pub(crate) fn build_options(self) -> Options {
Options {
sdk_key: self.sdk_key,
offline: self.offline,
cache: self.cache.unwrap_or(Box::new(EmptyConfigCache::new())),
polling_mode: self
.polling_mode
.unwrap_or(PollingMode::AutoPoll(Duration::from_mins(1))),
base_url: self.base_url,
data_governance: self.data_governance.unwrap_or(DataGovernance::Global),
http_timeout: self.http_timeout.unwrap_or(Duration::from_secs(30)),
overrides: self.overrides,
default_user: self.default_user,
}
}
}
fn is_sdk_key_valid(sdk_key: &str, is_custom_url: bool) -> bool {
if is_custom_url
&& sdk_key.len() > SDK_KEY_PROXY_PREFIX.len()
&& sdk_key.starts_with(SDK_KEY_PROXY_PREFIX)
{
return true;
}
let comps: Vec<&str> = sdk_key.split('/').collect();
match comps.len() {
2 => comps[0].len() == SDK_KEY_SECTION_LENGTH && comps[1].len() == SDK_KEY_SECTION_LENGTH,
3 => {
comps[0] == SDK_KEY_PREFIX
&& comps[1].len() == SDK_KEY_SECTION_LENGTH
&& comps[2].len() == SDK_KEY_SECTION_LENGTH
}
_ => false,
}
}