statsig/
lib.rs

1extern crate core;
2
3use std::ops::Deref;
4use std::sync::{Arc, RwLock};
5
6use lazy_static::lazy_static;
7use serde::de::DeserializeOwned;
8use serde_json::Value;
9
10use statsig::internal::StatsigDriver;
11use statsig::statsig_error::StatsigError;
12//
13// re-export public objects to top level
14pub use statsig::statsig_datastore::StatsigDatastore;
15pub use statsig::statsig_event::StatsigEvent;
16pub use statsig::statsig_options::StatsigOptions;
17pub use statsig::statsig_user::StatsigUser;
18pub use statsig::internal::{DynamicConfig, FeatureGate, Layer};
19pub use crate::statsig::internal::{EvalDetails, EvaluationReason};
20use futures::future::Shared;
21use futures::FutureExt;
22use tokio::sync::futures::Notified;
23use tokio::sync::Notify;
24use tokio::task::spawn_blocking;
25
26use crate::statsig::internal::LayerLogData;
27
28mod statsig;
29
30lazy_static! {
31    static ref DRIVER: Arc<RwLock<Option<StatsigDriver>>> = Arc::from(RwLock::from(None));
32    static ref STATSIG_INIT_NOTIFY: Arc<Notify> = Arc::new(Notify::new());
33    static ref STATSIG_INIT_NOTIFIED_FUTURE: Shared<Notified<'static>> =
34        STATSIG_INIT_NOTIFY.notified().shared();
35}
36
37pub struct Statsig {}
38
39impl Statsig {
40    pub async fn initialize(secret: &str) -> Option<StatsigError> {
41        Self::initialize_with_options(secret, StatsigOptions::default()).await
42    }
43
44    pub async fn initialize_with_options(
45        secret: &str,
46        options: StatsigOptions,
47    ) -> Option<StatsigError> {
48        match DRIVER.read().ok() {
49            Some(read_guard) => {
50                if read_guard.is_some() {
51                    return Some(StatsigError::AlreadyInitialized);
52                }
53            }
54            None => {
55                return Some(StatsigError::SingletonLockFailure);
56            }
57        }
58
59        let driver = unwrap_or_return!(
60            StatsigDriver::new(secret, options).ok(),
61            Some(StatsigError::InstantiationFailure)
62        );
63
64        driver.initialize().await;
65
66        let mut write_guard = unwrap_or_return!(
67            DRIVER.write().ok(),
68            Some(StatsigError::SingletonLockFailure)
69        );
70
71        *write_guard = Some(driver);
72
73        STATSIG_INIT_NOTIFY.notify_waiters();
74
75        None
76    }
77
78    pub fn is_initialized() -> bool {
79        DRIVER.read().map_or(false, |guard| guard.is_some())
80    }
81
82    pub fn wait_for_initialization() -> Shared<Notified<'static>> {
83        STATSIG_INIT_NOTIFIED_FUTURE.clone()
84    }
85
86    pub async fn shutdown() -> Option<StatsigError> {
87        let driver_clone = Arc::clone(&DRIVER);
88        match spawn_blocking(move || {
89            let mut write_guard = unwrap_or_return!(
90                driver_clone.write().ok(),
91                Err(StatsigError::SingletonLockFailure)
92            );
93
94            if let Some(driver) = write_guard.take() {
95                driver.shutdown();
96            }
97            Ok(())
98        })
99        .await
100        {
101            Ok(_t) => None,
102            Err(_e) => Some(StatsigError::ShutdownFailure),
103        }
104    }
105
106    pub fn check_gate(user: &StatsigUser, gate_name: &str) -> Result<bool, StatsigError> {
107        Self::use_driver(|driver| Ok(driver.check_gate(user, gate_name)))
108    }
109
110    pub fn get_feature_gate(user: &StatsigUser, gate_name: &str) -> Result<FeatureGate, StatsigError> {
111        Self::use_driver(|driver| Ok(driver.get_feature_gate(user, gate_name)))
112    }
113
114    pub fn get_config<T: DeserializeOwned>(
115        user: &StatsigUser,
116        config_name: &str,
117    ) -> Result<DynamicConfig<T>, StatsigError> {
118        Self::use_driver(|driver| Ok(driver.get_config(user, config_name)))
119    }
120
121    pub fn get_experiment<T: DeserializeOwned>(
122        user: &StatsigUser,
123        experiment_name: &str,
124    ) -> Result<DynamicConfig<T>, StatsigError> {
125        Self::get_config(user, experiment_name)
126    }
127
128    pub fn get_layer(user: &StatsigUser, layer_name: &str) -> Result<Layer, StatsigError> {
129        Self::use_driver(|driver| Ok(driver.get_layer(user, layer_name)))
130    }
131
132    pub fn log_event(user: &StatsigUser, event: StatsigEvent) -> Option<StatsigError> {
133        let res = Self::use_driver(move |driver| {
134            driver.log_event(user, event);
135            Ok(())
136        });
137
138        match res {
139            Err(e) => Some(e),
140            _ => None,
141        }
142    }
143
144    pub fn get_client_initialize_response(user: &StatsigUser) -> Result<Value, StatsigError> {
145        Self::use_driver(|driver| Ok(driver.get_client_initialize_response(user)))
146    }
147
148    pub(crate) fn log_layer_parameter_exposure(
149        layer: &Layer,
150        parameter_name: &str,
151        log_data: &LayerLogData,
152    ) {
153        let _ = Self::use_driver(|driver| {
154            driver.log_layer_parameter_exposure(layer, parameter_name, log_data);
155            Ok(())
156        });
157    }
158
159    fn use_driver<T>(
160        func: impl FnOnce(&StatsigDriver) -> Result<T, StatsigError>,
161    ) -> Result<T, StatsigError> {
162        if let Ok(guard) = DRIVER.read() {
163            if let Some(driver) = guard.deref() {
164                return func(driver);
165            }
166            return Err(StatsigError::Uninitialized);
167        }
168        Err(StatsigError::SingletonLockFailure)
169    }
170
171    #[doc(hidden)]
172    #[cfg(statsig_kong)]
173    pub async fn __unsafe_reset() {
174        if let Some(mut guard) = DRIVER.write().ok() {
175            if let Some(driver) = guard.take() {
176                let _ = spawn_blocking(move || {
177                    driver.shutdown();
178                })
179                .await;
180            }
181        }
182    }
183}