statsig_rust/user/
statsig_user_internal.rs

1use super::StatsigUserLoggable;
2use crate::evaluation::dynamic_value::DynamicValue;
3use crate::hashing::djb2_number;
4use crate::StatsigUser;
5use crate::{evaluation::dynamic_string::DynamicString, Statsig};
6
7pub type FullUserKey = (
8    u64,      // app_version
9    u64,      // country
10    u64,      // email
11    u64,      // ip
12    u64,      // locale
13    u64,      // user_agent
14    u64,      // user_id
15    Vec<u64>, // custom_ids
16    Vec<u64>, // custom
17    Vec<u64>, // private_attributes
18    Vec<u64>, // statsig_env
19);
20
21#[derive(Clone)]
22pub struct StatsigUserInternal<'statsig, 'user> {
23    pub user_ref: &'user StatsigUser,
24    pub statsig_instance: Option<&'statsig Statsig>,
25}
26
27impl<'statsig, 'user> StatsigUserInternal<'statsig, 'user> {
28    pub fn new(user: &'user StatsigUser, statsig_instance: Option<&'statsig Statsig>) -> Self {
29        Self {
30            user_ref: user,
31            statsig_instance,
32        }
33    }
34
35    pub fn get_unit_id(&self, id_type: &DynamicString) -> Option<&DynamicValue> {
36        self.user_ref.get_unit_id(id_type)
37    }
38
39    pub fn get_user_value(&self, field: &Option<DynamicString>) -> Option<&DynamicValue> {
40        let field = field.as_ref()?;
41
42        let lowered_field = &field.lowercased_value;
43
44        let str_value = match lowered_field as &str {
45            "userid" => &self.user_ref.data.user_id,
46            "email" => &self.user_ref.data.email,
47            "ip" => &self.user_ref.data.ip,
48            "country" => &self.user_ref.data.country,
49            "locale" => &self.user_ref.data.locale,
50            "appversion" => &self.user_ref.data.app_version,
51            "useragent" => &self.user_ref.data.user_agent,
52            _ => &None,
53        };
54
55        if let Some(value) = str_value {
56            if let Some(str_val) = &value.string_value {
57                if !str_val.value.is_empty() {
58                    return Some(value);
59                }
60            }
61        }
62
63        if let Some(custom) = &self.user_ref.data.custom {
64            if let Some(found) = custom.get(field.value.as_str()) {
65                return Some(found);
66            }
67            if let Some(lowered_found) = custom.get(lowered_field.as_str()) {
68                return Some(lowered_found);
69            }
70        }
71
72        if let Some(instance) = &self.statsig_instance {
73            if let Some(val) = instance.get_value_from_global_custom_fields(&field.value) {
74                return Some(val);
75            }
76
77            if let Some(val) = instance.get_value_from_global_custom_fields(&field.lowercased_value)
78            {
79                return Some(val);
80            }
81        }
82
83        if let Some(private_attributes) = &self.user_ref.data.private_attributes {
84            if let Some(found) = private_attributes.get(field.value.as_str()) {
85                return Some(found);
86            }
87            if let Some(lowered_found) = private_attributes.get(lowered_field.as_str()) {
88                return Some(lowered_found);
89            }
90        }
91
92        let str_value_alt = match lowered_field as &str {
93            "user_id" => &self.user_ref.data.user_id,
94            "app_version" => &self.user_ref.data.app_version,
95            "user_agent" => &self.user_ref.data.user_agent,
96            _ => &None,
97        };
98
99        if str_value_alt.is_some() {
100            return str_value_alt.as_ref();
101        }
102
103        None
104    }
105
106    pub fn get_value_from_environment(
107        &self,
108        field: &Option<DynamicString>,
109    ) -> Option<DynamicValue> {
110        let field = field.as_ref()?;
111
112        if let Some(result) = self.statsig_instance?.get_from_statsig_env(&field.value) {
113            return Some(result);
114        }
115
116        self.statsig_instance?
117            .get_from_statsig_env(&field.lowercased_value)
118    }
119
120    pub fn to_loggable(&self) -> StatsigUserLoggable {
121        let (environment, global_custom) = match self.statsig_instance {
122            Some(statsig) => (
123                statsig.use_statsig_env(|e| e.cloned()),
124                statsig.use_global_custom_fields(|gc| gc.cloned()),
125            ),
126            None => (None, None),
127        };
128
129        StatsigUserLoggable::new(&self.user_ref.data, environment, global_custom)
130    }
131
132    pub fn get_hashed_private_attributes(&self) -> Option<String> {
133        let private_attributes = match &self.user_ref.data.private_attributes {
134            Some(attrs) => attrs,
135            None => return None,
136        };
137
138        if private_attributes.is_empty() {
139            return None;
140        }
141
142        let mut val: i64 = 0;
143        for (key, value) in private_attributes {
144            let hash_key = match value.string_value {
145                Some(ref s) => key.to_owned() + ":" + &s.value,
146                None => key.to_owned() + ":",
147            };
148            val += djb2_number(&hash_key);
149            val &= 0xFFFF_FFFF;
150        }
151        Some(val.to_string())
152    }
153}