devcycle_bucketing_rs/user/
user.rs

1use crate::config::platform_data::PlatformData;
2use crate::config::{Environment, Project};
3use crate::feature::{Feature, FeatureVariation, ReadOnlyVariable};
4use chrono::{DateTime, Utc};
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7use std::sync::Arc;
8
9#[derive(Serialize, Deserialize, Clone)]
10#[serde(rename_all = "camelCase")]
11pub struct User {
12    // Unique id to identify the user
13    pub user_id: String,
14    // User's email used to identify the user on the dashboard / target audiences
15    pub email: String,
16    // User's name used to identify the user on the dashboard / target audiences
17    pub name: String,
18    // User's language in ISO 639-1 format
19    pub language: String,
20    // User's country in ISO 3166 alpha-2 format
21    pub country: String,
22    // App Version of the running application
23    pub app_version: String,
24    // App Build number of the running application
25    pub app_build: String,
26    // User's custom data to target the user with, data will be logged to DevCycle for use in dashboard.
27    pub custom_data: HashMap<String, serde_json::Value>,
28    // User's custom data to target the user with, data will not be logged to DevCycle only used for feature bucketing.
29    pub private_custom_data: HashMap<String, serde_json::Value>,
30    // User's device model
31    pub device_model: String,
32    // Date the user was created, Unix epoch timestamp format
33    #[serde(default = "Utc::now")]
34    pub last_seen_date: DateTime<Utc>,
35}
36
37impl User {
38    pub fn get_populated_user(&self, sdk_key: &str) -> PopulatedUser {
39        self.get_populated_user_with_platform_data_and_time(sdk_key, None, Utc::now())
40    }
41
42    pub fn get_populated_user_with_platform_data_and_time(
43        &self,
44        sdk_key: &str,
45        platform_data: Option<Arc<PlatformData>>,
46        create_date: DateTime<Utc>,
47    ) -> PopulatedUser {
48        let platform_data = platform_data
49            .unwrap_or_else(|| crate::config::platform_data::get_platform_data(sdk_key).unwrap());
50
51        PopulatedUser {
52            user_id: self.user_id.clone(),
53            email: self.email.clone(),
54            name: self.name.clone(),
55            private_custom_data: self.private_custom_data.clone(),
56            custom_data: self.custom_data.clone(),
57            language: self.language.clone(),
58            country: self.country.clone(),
59            app_version: self.app_version.clone(),
60            app_build: self.app_build.clone(),
61            device_model: self.device_model.clone(),
62            last_seen_date: self.last_seen_date.clone(),
63            platform_data,
64            created_date: create_date,
65        }
66    }
67}
68
69pub struct UserFeatureData<'a> {
70    user: &'a User,
71    feature_vars: &'a HashMap<String, String>,
72}
73
74#[derive(Clone, Serialize, Deserialize)]
75#[serde(rename_all = "camelCase")]
76pub struct PopulatedUser {
77    #[serde(rename = "userId")]
78    pub user_id: String,
79    // User's email used to identify the user on the dashboard / target audiences
80    pub email: String,
81    // User's name used to identify the user on the dashboard / target audiences
82    pub name: String,
83    // User's language in ISO 639-1 format
84    pub language: String,
85    // User's country in ISO 3166 alpha-2 format
86    pub country: String,
87    // App Version of the running application
88    #[serde(rename = "appVersion")]
89    pub app_version: String,
90    // App Build number of the running application
91    #[serde(rename = "appBuild")]
92    pub app_build: String,
93    // User's custom data to target the user with, data will be logged to DevCycle for use in dashboard.
94    #[serde(rename = "customData")]
95    pub custom_data: HashMap<String, serde_json::Value>,
96    // User's custom data to target the user with, data will not be logged to DevCycle only used for feature bucketing.
97    #[serde(rename = "privateCustomData")]
98    pub private_custom_data: HashMap<String, serde_json::Value>,
99    // User's device model
100    #[serde(rename = "deviceModel")]
101    pub device_model: String,
102    // Date the user was created, Unix epoch timestamp format
103    #[serde(rename = "lastSeenDate")]
104    pub last_seen_date: DateTime<Utc>,
105    // Platform data of the instance (Arc for efficient sharing across threads)
106    #[serde(rename = "platformData")]
107    pub platform_data: Arc<PlatformData>,
108    // Date the user was created, Unix epoch timestamp format
109    #[serde(rename = "createdDate")]
110    pub created_date: DateTime<Utc>,
111}
112
113impl PopulatedUser {
114    pub fn merge_client_custom_data(
115        mut self,
116        client_custom_data: HashMap<String, serde_json::Value>,
117    ) {
118        for (k, v) in client_custom_data {
119            if !self.custom_data.contains_key(&k) && !self.private_custom_data.contains_key(&k) {
120                self.custom_data.insert(k, v);
121            }
122        }
123    }
124
125    pub fn combined_custom_data(&self) -> HashMap<String, serde_json::Value> {
126        let mut ret = HashMap::new();
127        if !self.custom_data.is_empty() {
128            ret.extend(self.custom_data.clone());
129        }
130        if !self.private_custom_data.is_empty() {
131            ret.extend(self.private_custom_data.clone());
132        }
133        ret
134    }
135    pub fn new(
136        user: User,
137        platform_data: Arc<PlatformData>,
138        client_custom_data: HashMap<String, serde_json::Value>,
139    ) -> PopulatedUser {
140        let mut popuser = PopulatedUser {
141            user_id: user.user_id.clone(),
142            email: user.email.clone(),
143            name: user.name.clone(),
144            private_custom_data: user.private_custom_data.clone(),
145            custom_data: user.custom_data.clone(),
146            language: user.language,
147            country: user.country.clone(),
148            app_version: user.app_version.clone(),
149            app_build: user.app_build.clone(),
150            device_model: user.device_model.clone(),
151            last_seen_date: user.last_seen_date.clone(),
152            platform_data,
153            created_date: Utc::now(),
154        };
155        for (k, v) in client_custom_data {
156            if !popuser.custom_data.contains_key(&k)
157                && !popuser.private_custom_data.contains_key(&k)
158            {
159                popuser.custom_data.insert(k, v);
160            }
161        }
162
163        return popuser;
164    }
165}
166
167#[derive(Serialize)]
168#[serde(rename_all = "camelCase")]
169pub struct BucketedUserConfig {
170    pub(crate) project: Project,
171    pub(crate) environment: Environment,
172    pub(crate) features: HashMap<String, Feature>,
173    pub(crate) feature_variation_map: HashMap<String, String>,
174    pub(crate) variable_variation_map: HashMap<String, FeatureVariation>,
175    pub(crate) variables: HashMap<String, ReadOnlyVariable>,
176    #[serde(skip_serializing)]
177    pub(crate) user: PopulatedUser,
178}