statsig_rust/
global_configs.rs

1use crate::{log_e, DynamicValue};
2use lazy_static::lazy_static;
3use std::{
4    collections::HashMap,
5    sync::{Arc, RwLock, Weak},
6};
7
8const TAG: &str = stringify!(GlobalConfigs);
9
10pub const MAX_SAMPLING_RATE: f64 = 10000.0;
11
12lazy_static! {
13    static ref GLOBAL_CONFIG_INSTANCES: RwLock<HashMap<String, Weak<GlobalConfigs>>> =
14        RwLock::new(HashMap::new());
15}
16
17struct Configs {
18    sdk_configs: HashMap<String, DynamicValue>,
19    diagnostics_sampling_rates: HashMap<String, f64>,
20}
21
22pub struct GlobalConfigs {
23    configs: RwLock<Configs>,
24}
25
26impl GlobalConfigs {
27    pub fn get_instance(sdk_key: &str) -> Arc<GlobalConfigs> {
28        match GLOBAL_CONFIG_INSTANCES.read() {
29            Ok(read_guard) => {
30                if let Some(instance) = read_guard.get(sdk_key) {
31                    if let Some(instance) = instance.upgrade() {
32                        return instance.clone();
33                    }
34                }
35            }
36            Err(e) => {
37                log_e!(TAG, "Failed to get read guard: {}", e);
38            }
39        }
40
41        let instance = Arc::new(GlobalConfigs {
42            configs: RwLock::new(Configs {
43                sdk_configs: HashMap::new(),
44                diagnostics_sampling_rates: HashMap::from([
45                    ("initialize".to_string(), 10000.0),
46                    ("config_sync".to_string(), 1000.0),
47                    ("dcs".to_string(), 1000.0),
48                    ("get_id_list".to_string(), 100.0), // default sampling rates
49                ]),
50            }),
51        });
52
53        match GLOBAL_CONFIG_INSTANCES.write() {
54            Ok(mut write_guard) => {
55                write_guard.insert(sdk_key.into(), Arc::downgrade(&instance));
56            }
57            Err(e) => {
58                log_e!(TAG, "Failed to get write guard: {}", e);
59            }
60        }
61
62        instance
63    }
64
65    pub fn set_sdk_configs(&self, new_configs: HashMap<String, DynamicValue>) {
66        match self.configs.write() {
67            Ok(mut configs_guard) => {
68                for (key, value) in new_configs {
69                    configs_guard.sdk_configs.insert(key, value);
70                }
71            }
72            Err(e) => {
73                log_e!(TAG, "Failed to get write guard: {}", e);
74            }
75        }
76    }
77
78    pub fn set_diagnostics_sampling_rates(&self, new_sampling_rate: HashMap<String, f64>) {
79        match self.configs.write() {
80            Ok(mut configs_guard) => {
81                for (key, rate) in new_sampling_rate {
82                    let clamped_rate = rate.clamp(0.0, MAX_SAMPLING_RATE);
83                    configs_guard
84                        .diagnostics_sampling_rates
85                        .insert(key, clamped_rate);
86                }
87            }
88            Err(e) => {
89                log_e!(TAG, "Failed to get write guard: {}", e);
90            }
91        }
92    }
93
94    pub fn use_sdk_config_value<T>(
95        &self,
96        key: &str,
97        f: impl FnOnce(Option<&DynamicValue>) -> T,
98    ) -> T {
99        match self.configs.read() {
100            Ok(configs_guard) => f(configs_guard.sdk_configs.get(key)),
101            Err(e) => {
102                log_e!(TAG, "Failed to get read guard: {}", e);
103                f(None)
104            }
105        }
106    }
107
108    pub fn use_diagnostics_sampling_rate<T>(
109        &self,
110        key: &str,
111        f: impl FnOnce(Option<&f64>) -> T,
112    ) -> T {
113        match self.configs.read() {
114            Ok(configs_guard) => f(configs_guard.diagnostics_sampling_rates.get(key)),
115            Err(e) => {
116                log_e!(TAG, "Failed to get read guard: {}", e);
117                f(None)
118            }
119        }
120    }
121}