statsig_rust/user/
statsig_user_loggable.rs1use super::user_data::UserData;
2use crate::{DynamicValue, StatsigUser};
3use serde::{ser::SerializeStruct, Deserialize, Serialize};
4use serde_json::Value;
5use std::{collections::HashMap, sync::Arc};
6
7const TAG: &str = "StatsigUserLoggable";
8
9#[derive(Clone, Default)]
10pub struct StatsigUserLoggable {
11 pub data: Arc<UserData>,
12 pub environment: Option<HashMap<String, DynamicValue>>,
13 pub global_custom: Option<HashMap<String, DynamicValue>>,
14}
15
16impl StatsigUserLoggable {
17 pub fn new(
18 user_inner: &Arc<UserData>,
19 environment: Option<HashMap<String, DynamicValue>>,
20 global_custom: Option<HashMap<String, DynamicValue>>,
21 ) -> Self {
22 Self {
23 data: user_inner.clone(),
24 environment,
25 global_custom,
26 }
27 }
28
29 pub fn null() -> Self {
30 Self::default()
31 }
32
33 pub fn default_console_capture_user(
34 environment: Option<HashMap<String, DynamicValue>>,
35 global_custom: Option<HashMap<String, DynamicValue>>,
36 ) -> Self {
37 Self::new(
38 &StatsigUser::with_user_id("console-capture-user").data,
39 environment,
40 global_custom,
41 )
42 }
43}
44
45impl Serialize for StatsigUserLoggable {
48 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
49 where
50 S: serde::Serializer,
51 {
52 let mut state = serializer.serialize_struct(TAG, 10)?;
53
54 let data = self.data.as_ref();
55 serialize_data_field(&mut state, "userID", &data.user_id)?;
56 serialize_data_field(&mut state, "customIDs", &data.custom_ids)?;
57 serialize_data_field(&mut state, "email", &data.email)?;
58 serialize_data_field(&mut state, "ip", &data.ip)?;
59 serialize_data_field(&mut state, "userAgent", &data.user_agent)?;
60 serialize_data_field(&mut state, "country", &data.country)?;
61 serialize_data_field(&mut state, "locale", &data.locale)?;
62 serialize_data_field(&mut state, "appVersion", &data.app_version)?;
63
64 serialize_custom_field(&mut state, &data.custom, &self.global_custom)?;
65 serialize_data_field(&mut state, "statsigEnvironment", &self.environment)?;
66
67 state.end()
70 }
71}
72
73impl<'de> Deserialize<'de> for StatsigUserLoggable {
74 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
75 where
76 D: serde::Deserializer<'de>,
77 {
78 let mut value = Value::deserialize(deserializer)?;
79 let env = value["statsigEnvironment"].take();
80 let data = serde_json::from_value::<UserData>(value).map_err(|e| {
81 serde::de::Error::custom(format!("Error deserializing StatsigUserInner: {e}"))
82 })?;
83
84 let environment = serde_json::from_value::<Option<HashMap<String, DynamicValue>>>(env)
85 .map_err(|e| {
86 serde::de::Error::custom(format!("Error deserializing StatsigUserInner: {e}"))
87 })?;
88
89 Ok(StatsigUserLoggable {
90 data: Arc::new(data),
91 environment,
92 global_custom: None, })
94 }
95}
96
97fn serialize_data_field<S, T>(
98 state: &mut S,
99 field: &'static str,
100 value: &Option<T>,
101) -> Result<(), S::Error>
102where
103 S: SerializeStruct,
104 T: Serialize,
105{
106 if let Some(value) = value {
107 state.serialize_field(field, value)?;
108 }
109 Ok(())
110}
111
112fn serialize_custom_field<S>(
113 state: &mut S,
114 custom: &Option<HashMap<String, DynamicValue>>,
115 global_custom: &Option<HashMap<String, DynamicValue>>,
116) -> Result<(), S::Error>
117where
118 S: SerializeStruct,
119{
120 if global_custom.is_none() && custom.is_none() {
121 return Ok(());
122 }
123
124 let mut map = HashMap::new();
125
126 if let Some(global_custom) = global_custom.as_ref() {
127 for (k, v) in global_custom {
128 map.insert(k, v);
129 }
130 }
131
132 if let Some(custom) = custom.as_ref() {
133 for (k, v) in custom {
134 map.insert(k, v);
135 }
136 }
137
138 serialize_data_field(state, "custom", &Some(map))
139}