/ src / actor / user.rs
user.rs
  1  use super::ActorData;
  2  use crate::ACTORS;
  3  use candid::{CandidType, Deserialize};
  4  use ic_cdk::api;
  5  use shared::{
  6      actor::account::{AccountImage, SetUserProfileError, SetUserProfileRequest},
  7      item::ItemKey,
  8      market::MarketPrincipal,
  9      store::StorePrincipal,
 10  };
 11  use std::{collections::BTreeMap, mem};
 12  
 13  pub mod basket;
 14  
 15  use basket::Basket;
 16  
 17  nest! {
 18      #[derive(CandidType, Deserialize, Debug, Clone)]
 19      pub struct UserActor {
 20          #>[derive(CandidType, Deserialize, Debug, Clone)]
 21          pub version: pub enum UserActorVersion {
 22              V1 {
 23                  profile: UserProfileVersion,
 24                  following_markets: BTreeMap<MarketPrincipal, DataAboutFollowingMarketV1>,
 25                  favorite_markets: Vec<MarketPrincipal>,
 26                  favorite_stores: Vec<StorePrincipal>,
 27                  favorite_items: Vec<(StorePrincipal, ItemKey)>,
 28                  basket: Basket,
 29              },
 30          },
 31      }
 32  }
 33  
 34  nest! {
 35      #[derive(CandidType, Deserialize, Debug, Clone, PartialEq)]
 36      pub enum UserProfileVersion {
 37          V1(
 38              #>[derive(CandidType, Deserialize, Debug, Clone, PartialEq)]
 39              pub struct UserProfileV1 {
 40                  pub name: String,
 41                  pub birth_name: String,
 42                  pub mail_address: String,
 43                  pub image: AccountImage,
 44              }
 45          ),
 46      }
 47  }
 48  
 49  #[derive(CandidType, Deserialize, Debug, Clone)]
 50  pub struct DataAboutFollowingMarketV1 {
 51      pub followed_time: u64,
 52      pub last_bought_time: Option<u64>,
 53  }
 54  
 55  impl DataAboutFollowingMarketV1 {
 56      pub fn builder() -> DataAboutFollowingMarketV1Builder {
 57          DataAboutFollowingMarketV1Builder {
 58              followed_time: None,
 59              last_bought_time: None,
 60          }
 61      }
 62  }
 63  
 64  pub struct DataAboutFollowingMarketV1Builder {
 65      followed_time: Option<u64>,
 66      last_bought_time: Option<u64>,
 67  }
 68  
 69  impl DataAboutFollowingMarketV1Builder {
 70      pub fn followed_time(mut self, followed_time: u64) -> Self {
 71          self.followed_time = Some(followed_time);
 72          self
 73      }
 74  
 75      pub fn last_bought_time(mut self, last_bought_time: Option<u64>) -> Self {
 76          self.last_bought_time = last_bought_time;
 77          self
 78      }
 79  
 80      pub fn build(self) -> DataAboutFollowingMarketV1 {
 81          DataAboutFollowingMarketV1 {
 82              followed_time: self.followed_time.expect("followed_time is required"),
 83              last_bought_time: self.last_bought_time,
 84          }
 85      }
 86  }
 87  
 88  pub fn set_user_profile(request: SetUserProfileRequest) -> Result<(), SetUserProfileError> {
 89      let caller = api::caller();
 90  
 91      let mut actor_borrowed = ACTORS.with(|actors| actors.borrow().get(&caller.into()));
 92      if let Some(actor) = actor_borrowed {
 93          if let ActorData::User(mut user) = actor {
 94              match &mut user.version {
 95                  UserActorVersion::V1 {
 96                      profile,
 97                      ref mut following_markets,
 98                      ref mut favorite_markets,
 99                      ref mut favorite_stores,
100                      ref mut favorite_items,
101                      ref mut basket,
102                      ..
103                  } => {
104                      let new_profile = UserProfileVersion::V1(UserProfileV1 {
105                          name: request.name,
106                          birth_name: request.birth_name,
107                          mail_address: request.mail_address,
108                          image: request.image,
109                      });
110  
111                      if profile != &new_profile {
112                          // Move existing values to a new instance
113                          actor_borrowed = Some(ActorData::User(UserActor {
114                              version: UserActorVersion::V1 {
115                                  profile: new_profile,
116                                  // Reusing existing values with move semantics
117                                  following_markets: mem::take(following_markets),
118                                  favorite_markets: mem::take(favorite_markets),
119                                  favorite_stores: mem::take(favorite_stores),
120                                  favorite_items: mem::take(favorite_items),
121                                  basket: mem::take(basket),
122                              },
123                          }));
124  
125                          let _ = ACTORS.with(|actors| {
126                              actors
127                                  .borrow_mut()
128                                  .insert(caller.into(), actor_borrowed.unwrap())
129                          });
130                      }
131                  }
132              }
133          } else {
134              return Err(SetUserProfileError::AccountIsNotUser(caller));
135          }
136      } else {
137          return Err(SetUserProfileError::AccountNotFound(caller));
138      }
139  
140      Ok(())
141  }