1use super::account::{UserAccountArgs, UserAccountData};
3use crate::error::UserStateError;
4
5use candid::Principal;
6use ic_cdk::export::{candid::CandidType, serde::Deserialize};
7use std::collections::hash_map::DefaultHasher;
8use std::collections::HashMap;
9use std::hash::{Hash, Hasher};
10
11#[derive(Debug, CandidType, Deserialize, Default, Clone)]
12pub struct UserData {
13 password: Option<u64>,
14 profile: UserProfile,
15 pub name: String,
16 pub email: String,
17 pub balance: u128,
18 pub accounts: Vec<UserAccountData>,
19 pub settings: HashMap<String, UserDataSettingValue>,
20}
21
22#[derive(Debug, CandidType, Deserialize, Default, Clone)]
23pub struct UserProfile {
24 pub full_name: Option<String>,
25 pub address: Option<String>,
26 pub phone_number: Option<String>,
27 pub attributes: HashMap<String, String>,
28}
29
30impl UserData {
31 pub fn new(user_args: UserDataArgs, account_args: UserAccountArgs) -> Self {
33 let mut user_data = UserData::default();
34
35 user_data.update(user_args);
36
37 user_data.accounts = Vec::with_capacity(256);
38
39 let name = if let Some(name) = account_args.name {
40 name
41 } else {
42 "Account 0".to_string()
43 };
44
45 user_data
46 .accounts
47 .insert(0, UserAccountData::new(account_args.public_key, name));
48
49 user_data
50 }
51
52 pub fn update(&mut self, args: UserDataArgs) -> UserDataArgs {
55 if let Some(name) = args.name {
56 self.name = name;
57 }
58
59 if let Some(email) = args.email {
60 self.email = email;
61 }
62
63 if let Some(balance) = args.balance {
64 self.balance = balance;
65 }
66
67 if let Some(settings) = args.settings {
68 for (key, value) in settings {
69 self.settings.insert(key, value);
70 }
71 }
72
73 if let Some(profile) = args.profile {
74 self.update_profile(profile);
75 }
76
77 if let Some(password) = args.password {
78 self.password = Some(hash_password(&password));
79 }
80
81 UserDataArgs {
82 name: Some(self.name.clone()),
83 email: Some(self.email.clone()),
84 balance: Some(self.balance),
85 settings: Some(self.settings.clone()),
86 profile: Some(UserProfileArgs {
87 full_name: self.profile.full_name.clone(),
88 address: self.profile.address.clone(),
89 phone_number: self.profile.phone_number.clone(),
90 attributes: Some(self.profile.attributes.clone()),
91 }),
92 password: None,
93 }
94 }
95
96 pub fn update_profile(&mut self, args: UserProfileArgs) -> UserProfileArgs {
100 if let Some(full_name) = args.full_name {
101 self.profile.full_name = Some(full_name);
102 }
103
104 if let Some(account) = args.address {
105 self.profile.address = Some(account);
106 }
107
108 if let Some(phone_number) = args.phone_number {
109 self.profile.phone_number = Some(phone_number);
110 }
111
112 if let Some(attributes) = args.attributes {
113 for (key, value) in attributes {
114 self.profile.attributes.insert(key, value);
115 }
116 }
117
118 UserProfileArgs {
119 full_name: self.profile.full_name.clone(),
120 address: self.profile.address.clone(),
121 phone_number: self.profile.phone_number.clone(),
122 attributes: Some(self.profile.attributes.clone()),
123 }
124 }
125
126 pub fn create_account(
129 &mut self,
130 args: UserAccountArgs,
131 ) -> Result<UserAccountData, UserStateError> {
132 let key = self.get_new_key()?;
133
134 let public_key = args.public_key;
135
136 let name = if let Some(name) = args.name {
137 name
138 } else {
139 format!("Account {}", key)
140 };
141
142 let account_data = UserAccountData::new(public_key, name);
143
144 self.accounts.insert(key, account_data.clone());
145
146 Ok(account_data)
147 }
148
149 pub fn get_new_key(&self) -> Result<usize, UserStateError> {
152 if self.accounts.len() > 255 {
153 return Err(UserStateError::AccountLimitReached);
154 }
155
156 let key = self.accounts.len();
157
158 Ok(key)
159 }
160
161 pub fn get_derivation_path(
164 &self,
165 principal: Principal,
166 key: u8,
167 ) -> Result<Vec<u8>, UserStateError> {
168 if key as usize > self.accounts.len() {
169 return Err(UserStateError::InvalidAccountKey);
170 }
171
172 let mut derivation_path = principal.as_slice().to_vec();
173
174 derivation_path.push(key);
175
176 Ok(derivation_path)
177 }
178
179 pub fn get_key_is_valid(&self, key: usize) -> Result<(), UserStateError> {
182 if key >= self.accounts.len() {
183 return Err(UserStateError::InvalidAccountKey);
184 }
185
186 Ok(())
187 }
188
189 pub fn get_account(&self, key: usize) -> Result<UserAccountData, UserStateError> {
192 self.get_key_is_valid(key)?;
193
194 Ok(self.accounts[key].clone())
195 }
196
197 pub fn get_account_mut(&mut self, key: usize) -> Result<&mut UserAccountData, UserStateError> {
200 self.get_key_is_valid(key)?;
201
202 Ok(&mut self.accounts[key])
203 }
204
205 pub fn get_accounts(&self) -> &Vec<UserAccountData> {
208 &self.accounts
209 }
210
211 pub fn set_password(&mut self, password: &str) -> Result<(), UserStateError> {
213 let hashed_password = hash_password(password);
214
215 self.password = Some(hashed_password);
216
217 Ok(())
218 }
219
220 pub fn check_password(&self, password: &str) -> Result<bool, UserStateError> {
222 let input_hash = hash_password(password);
223
224 if let Some(password_hash) = self.password {
225 Ok(input_hash == password_hash)
226 } else {
227 Err(UserStateError::PasswordNotSet)
228 }
229 }
230
231 pub fn get_setting(&self, key: &str) -> Result<UserDataSettingValue, UserStateError> {
234 if let Some(value) = self.settings.get(key) {
235 Ok(value.clone())
236 } else {
237 Err(UserStateError::SettingNotFound)
238 }
239 }
240
241 pub fn get_setting_mut(
244 &mut self,
245 key: &str,
246 ) -> Result<&mut UserDataSettingValue, UserStateError> {
247 if let Some(value) = self.settings.get_mut(key) {
248 Ok(value)
249 } else {
250 Err(UserStateError::SettingNotFound)
251 }
252 }
253
254 pub fn get_settings(&self) -> &HashMap<String, UserDataSettingValue> {
257 &self.settings
258 }
259
260 pub fn set_setting(&mut self, key: String, value: UserDataSettingValue) -> () {
262 let value = value.into();
263 self.settings.insert(key, value);
264 }
265
266 pub fn remove_setting(&mut self, key: &str) -> Result<bool, UserStateError> {
269 if self.settings.remove(key).is_some() {
270 Ok(true)
271 } else {
272 Err(UserStateError::SettingNotFound)
273 }
274 }
275
276 pub fn update_settings(&mut self, new_settings: HashMap<String, UserDataSettingValue>) {
278 self.settings = new_settings;
279 }
280}
281
282fn hash_password(password: &str) -> u64 {
283 let mut hasher = DefaultHasher::new();
284 password.hash(&mut hasher);
285
286 hasher.finish()
287}
288
289#[derive(CandidType, Deserialize, Default, Debug, Clone, PartialEq)]
290pub struct UserDataArgs {
291 pub name: Option<String>,
292 pub email: Option<String>,
293 pub balance: Option<u128>,
294 pub password: Option<String>,
295 pub profile: Option<UserProfileArgs>,
296 pub settings: Option<HashMap<String, UserDataSettingValue>>,
297}
298
299#[derive(Debug, CandidType, Clone, Default, Deserialize, PartialEq)]
300pub struct UserProfileArgs {
301 pub full_name: Option<String>,
302 pub address: Option<String>,
303 pub phone_number: Option<String>,
304 pub attributes: Option<HashMap<String, String>>,
305}
306
307#[derive(Debug, CandidType, Deserialize, Clone, PartialEq)]
308pub enum UserDataSettingValue {
309 StringValue(String),
310 NumberValue(u128),
311 FloatValue(f64),
312 BoolValue(bool),
313}
314
315impl From<String> for UserDataSettingValue {
316 fn from(value: String) -> Self {
317 UserDataSettingValue::StringValue(value)
318 }
319}
320
321impl From<f64> for UserDataSettingValue {
322 fn from(value: f64) -> Self {
323 UserDataSettingValue::FloatValue(value)
324 }
325}
326
327impl From<u128> for UserDataSettingValue {
328 fn from(value: u128) -> Self {
329 UserDataSettingValue::NumberValue(value)
330 }
331}
332
333impl From<bool> for UserDataSettingValue {
334 fn from(value: bool) -> Self {
335 UserDataSettingValue::BoolValue(value)
336 }
337}
338
339#[cfg(test)]
340mod tests {
341 use super::*;
342 use proptest::prelude::*;
343
344 proptest! {
345
346 #[test]
347 fn test_user_data_arbitrary( user_args: UserDataArgs,account_args: UserAccountArgs) {
348 let user_data = UserData::new(user_args.clone(), account_args);
349
350 assert_eq!(user_data.name, user_args.name.unwrap_or_default());
351 assert_eq!(user_data.email, user_args.email.unwrap_or_default());
352 assert_eq!(user_data.balance, user_args.balance.unwrap_or_default());
353 }
354
355 #[test]
356 fn test_create_account(account_args: UserAccountArgs, account_args2: UserAccountArgs, profile: UserProfileArgs) {
357 let args = UserDataArgs {
358 name: Some("test".to_string()),
359 email: Some("test@example.com".to_string()),
360 balance: Some(0),
361 password: Some("password".to_string()),
362 profile: Some(profile),
363 settings: None,
364 };
365
366 let mut user_data = UserData::new(args, account_args);
367
368 let account_data = user_data.create_account(account_args2.clone()).unwrap();
369
370 assert_eq!(account_data.public_key, account_args2.public_key);
371 }
372
373 #[test]
374 fn test_get_account(user_args: UserDataArgs, account_args: UserAccountArgs, name: String) {
375 let mut user_data = UserData::new(user_args, account_args.clone());
376
377 let account_data = user_data.get_account(0).unwrap();
378
379 assert_eq!(account_data.public_key, account_args.public_key);
380
381 let mut account_data2 = user_data.get_account_mut(0).unwrap();
382
383 account_data2.name = name.clone();
384
385 assert_eq!(user_data.get_account(0).unwrap().name, name);
386 }
387
388 #[test]
389 fn test_update_account(user_args: UserDataArgs, account_args: UserAccountArgs, name: String) {
390 let mut user_data = UserData::new(user_args, account_args.clone());
391
392 let mut account_data = user_data.get_account_mut(0).unwrap();
393
394 account_data.name = name.clone();
395
396 assert_eq!(user_data.get_account(0).unwrap().name, name);
397 }
398
399 #[test]
400 fn test_update_settings(user_args: UserDataArgs, account_args: UserAccountArgs, name: String) {
401 let mut user_data = UserData::new(user_args, account_args.clone());
402
403 let mut settings = HashMap::new();
404 settings.insert("name".to_string(), name.clone().into());
405
406 user_data.update_settings(settings);
407
408 assert_eq!(user_data.settings.get("name").unwrap(), &name.into());
409 }
410
411 #[test]
412 fn test_update_password(user_args: UserDataArgs, account_args: UserAccountArgs, password: String) {
413 let mut user_data = UserData::new(user_args, account_args.clone());
414
415 user_data.set_password(&password.clone()).unwrap();
416
417
418 assert!(user_data.check_password(&password).unwrap());
419 }
420
421 #[test]
422 fn test_update_email(user_args: UserDataArgs, account_args: UserAccountArgs, email: String) {
423 let mut user_data = UserData::new(user_args, account_args.clone());
424
425 user_data.email = email.clone();
426
427 assert_eq!(user_data.email, email);
428 }
429
430 #[test]
431 fn test_update_name(user_args: UserDataArgs, account_args: UserAccountArgs, name: String) {
432 let mut user_data = UserData::new(user_args, account_args.clone());
433
434 user_data.name = name.clone();
435
436 assert_eq!(user_data.name, name);
437 }
438
439 #[test]
440 fn test_update_balance(user_args: UserDataArgs, account_args: UserAccountArgs, balance: u128) {
441 let mut user_data = UserData::new(user_args, account_args.clone());
442
443 user_data.balance = balance;
444
445 assert_eq!(user_data.balance, balance);
446 }
447
448 #[test]
449 fn test_update_profile(user_args: UserDataArgs, account_args: UserAccountArgs, name: String) {
450 let mut user_data = UserData::new(user_args, account_args.clone());
451
452 let mut profile = UserProfileArgs::default();
453 profile.full_name = Some(name.clone());
454 profile.address = Some("account".to_string());
455
456 user_data.update_profile(profile);
457
458 assert_eq!(user_data.profile.full_name.unwrap(), name);
459 }
460
461 #[test]
462 fn test_update_profile_attributes(user_args: UserDataArgs, account_args: UserAccountArgs, key: String, value: String) {
463 let mut user_data = UserData::new(user_args, account_args);
464
465 let mut profile = UserProfileArgs::default();
466
467 let mut attributes = HashMap::new();
468
469 attributes.insert(key.clone(), value.clone());
470
471 profile.attributes = Some(attributes);
472
473 user_data.update_profile(profile);
474
475 assert_eq!(user_data.profile.attributes.get(&key).unwrap(), &value);
476 }
477 }
478}