/ src / backend / src / item.rs
item.rs
  1  use super::{ITEMS, MARKETS, STORES, TAGS};
  2  use candid::{CandidType, Decode, Deserialize, Encode};
  3  use ic_stable_structures::{storable::Bound, Storable};
  4  pub use interface::item::{
  5      attr::AttrKeys, ItemCoreDataArgs, ItemCoreDataResult, ItemPageArgs, ItemPageResult,
  6  };
  7  use interface::unit::Currency;
  8  use std::borrow::Cow;
  9  
 10  pub mod attr;
 11  use attr::{AttrCoreSpecificDataResult, AttrSpecificDataResult, ItemAttrs};
 12  pub mod image;
 13  use image::ItemImages;
 14  pub mod spec;
 15  use spec::ItemSpecs;
 16  
 17  #[derive(CandidType, Deserialize, Debug, Clone)]
 18  pub struct Item {
 19      pub name: String,
 20      pub descriptions: Vec<String>,
 21      pub store_id: u128,
 22      pub tag_ids: Vec<u128>,
 23      pub images: ItemImages,
 24      pub specs: ItemSpecs,
 25      pub attrs: ItemAttrs,
 26  }
 27  
 28  impl Storable for Item {
 29      fn to_bytes(&self) -> std::borrow::Cow<[u8]> {
 30          Cow::Owned(Encode!(self).unwrap())
 31      }
 32  
 33      fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self {
 34          Decode!(bytes.as_ref(), Self).unwrap()
 35      }
 36  
 37      const BOUND: Bound = Bound::Bounded {
 38          max_size: 32768,
 39          is_fixed_size: false,
 40      };
 41  }
 42  
 43  impl Item {
 44      pub fn get_tags(&self) -> Vec<interface::item::Tag> {
 45          self.tag_ids
 46              .iter()
 47              .map(|tag_id| {
 48                  let tag = TAGS.with(|p| p.borrow().get(tag_id));
 49                  let tag = match tag {
 50                      Some(tag) => tag,
 51                      None => return None, // TODO: error handling
 52                  };
 53                  Some(interface::item::Tag {
 54                      id: *tag_id,
 55                      name: tag.name.clone(),
 56                  })
 57              })
 58              .filter(|tag| tag.is_some())
 59              .map(|tag| tag.unwrap())
 60              .collect()
 61      }
 62  
 63      pub fn get_attr_data(
 64          &self,
 65          attr_keys: &AttrKeys,
 66          currency: &Currency,
 67      ) -> Option<AttrSpecificDataResult> {
 68          let attr_data = self.attrs.map.get(attr_keys)?;
 69          let price = *attr_data.price.get(currency)?;
 70          let image_vec = self.images.get_image_vec(&attr_data.image_vec_key)?;
 71          let specs = self.specs.get_specs(&attr_data.spec_keys);
 72  
 73          let res = AttrSpecificDataResult {
 74              stock: attr_data.stock,
 75              price,
 76              image_vec,
 77              specs,
 78              sale: attr_data.sale,
 79          };
 80  
 81          Some(res)
 82      }
 83  
 84      pub fn get_attr_core_data(
 85          &self,
 86          attr_keys: &AttrKeys,
 87          currency: &Currency,
 88      ) -> Option<AttrCoreSpecificDataResult> {
 89          let attr_data = self.attrs.map.get(attr_keys)?;
 90          let price = *attr_data.price.get(currency)?;
 91          let image = self
 92              .images
 93              .get_base_image(&attr_data.image_vec_key)?
 94              .clone();
 95  
 96          let res = AttrCoreSpecificDataResult {
 97              stock: attr_data.stock,
 98              price,
 99              image,
100              sale: attr_data.sale,
101          };
102  
103          Some(res)
104      }
105  }
106  
107  pub fn get_item_page_info(arg: &ItemPageArgs) -> Option<ItemPageResult> {
108      let item = ITEMS.with(|p| p.borrow().get(&arg.item_id))?;
109  
110      let store = STORES.with(|p| p.borrow().get(&item.store_id))?;
111  
112      let market = match &arg.market_id {
113          Some(market_id) => MARKETS.with(|p| p.borrow().get(market_id)),
114          None => None,
115      };
116  
117      let market_name = match market {
118          Some(market) => Some(market.name.clone()),
119          None => None,
120      };
121  
122      let attr_data = item.get_attr_data(&arg.attr_keys, &arg.currency)?;
123  
124      let res = interface::item::ItemPageResult {
125          name: item.name.clone(),
126          price: attr_data.price,
127          descriptions: item.descriptions.clone(),
128          tags: item.get_tags(),
129          image_vec: attr_data.image_vec,
130          stock: attr_data.stock,
131          store_id: item.store_id,
132          store_name: store.name.clone(),
133          market_name,
134          specs: attr_data.specs,
135      };
136  
137      Some(res)
138  }
139  
140  pub fn get_item_core_data_vec(args: Vec<ItemCoreDataArgs>) -> Vec<Option<ItemCoreDataResult>> {
141      let mut res = Vec::new();
142  
143      for arg in args {
144          res.push(get_item_core_data(arg));
145      }
146  
147      res
148  }
149  
150  fn get_item_core_data(arg: ItemCoreDataArgs) -> Option<ItemCoreDataResult> {
151      let item = ITEMS.with(|p| p.borrow().get(&arg.item.item_id))?;
152  
153      let store = STORES.with(|p| p.borrow().get(&item.store_id))?;
154  
155      let attr_core_data = item.get_attr_core_data(&arg.item.attr_keys, &arg.currency)?;
156  
157      let image = attr_core_data.image;
158  
159      let item_ommited_data = ItemCoreDataResult {
160          id: arg.item.item_id,
161          name: item.name.clone(),
162          price: attr_core_data.price,
163          tags: item.get_tags(),
164          image,
165          stock: attr_core_data.stock,
166          store_id: item.store_id,
167          store_name: store.name.clone(),
168      };
169  
170      Some(item_ommited_data)
171  }