statsig_rust/
global_configs.rs

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