/ src / store.rs
store.rs
  1  use crate::{MARKETS, MARKETS_IN_ID, STORES, STORES_IN_ID};
  2  use anthol_store::item::Item;
  3  use candid::{CandidType, Decode, Deserialize, Encode};
  4  use ic_cdk::api::{
  5      call::{call, CallResult, RejectionCode},
  6      caller,
  7      management_canister::main::{
  8          create_canister, install_code, CanisterInstallMode, CreateCanisterArgument,
  9          InstallCodeArgument,
 10      },
 11  };
 12  use ic_stable_structures::{storable::Bound, Storable};
 13  use shared::{
 14      item::{
 15          get_item_page_response, ItemId, ItemKey, ItemPageError, ItemPageErrorCode,
 16          ItemPageFromStoreErrorCode, ItemPageRequest, ItemPageResponse,
 17          ItemPageResponseFromStoreCanister,
 18      },
 19      store::{StoreId, StoreInitArg, StoreName, StorePrincipal},
 20      util::scale::u128::TRILLION,
 21  };
 22  use std::borrow::Cow;
 23  
 24  #[derive(CandidType, Deserialize, Debug, Clone)]
 25  pub struct StoreData {
 26      pub id: StoreId,
 27      pub name: StoreName,
 28      pub details: StoreDetails,
 29  }
 30  
 31  impl Storable for StoreData {
 32      fn to_bytes(&self) -> std::borrow::Cow<[u8]> {
 33          Cow::Owned(Encode!(self).unwrap())
 34      }
 35  
 36      fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self {
 37          Decode!(bytes.as_ref(), Self).unwrap()
 38      }
 39  
 40      const BOUND: Bound = Bound::Unbounded;
 41  }
 42  
 43  #[derive(CandidType, Deserialize, Debug, Clone)]
 44  pub enum StoreDetails {
 45      None,
 46  }
 47  
 48  pub async fn update_store_data(
 49      canister_id: &StorePrincipal,
 50      id: &StoreId,
 51      name: &StoreName,
 52  ) -> CallResult<()> {
 53      call(
 54          (*canister_id).into(),
 55          "update_store_data",
 56          (caller(), id, name),
 57      )
 58      .await
 59  }
 60  
 61  pub async fn create_store_canister(
 62      id: &StoreId,
 63      name: &StoreName,
 64  ) -> Result<StorePrincipal, CreateStoreError> {
 65      STORES_IN_ID.with(|stores| {
 66          if stores.borrow().contains_key(id) {
 67              return Err(CreateStoreError(
 68                  CreateStoreRejectionCode::StoreAlreadyExists,
 69                  format!("Store with id {} already exists", id),
 70              ));
 71          }
 72          Ok(())
 73      })?;
 74  
 75      let canister_id = create_canister(CreateCanisterArgument::default(), TRILLION)
 76          .await?
 77          .0
 78          .canister_id;
 79  
 80      let wasm_module =
 81          include_bytes!("../../../target/wasm32-unknown-unknown/release/anthol_store.wasm").to_vec();
 82  
 83      let arg = Some(StoreInitArg {
 84          id: *id,
 85          name: name.clone(),
 86      });
 87  
 88      install_code(InstallCodeArgument {
 89          mode: CanisterInstallMode::Install,
 90          canister_id,
 91          wasm_module,
 92          arg: Encode!(&arg).unwrap(),
 93      })
 94      .await?;
 95  
 96      let canister_id: StorePrincipal = canister_id.into();
 97  
 98      STORES.with_borrow_mut(|stores| {
 99          stores.insert(
100              canister_id,
101              StoreData {
102                  id: *id,
103                  name: name.clone(),
104                  details: StoreDetails::None,
105              },
106          );
107      });
108  
109      STORES_IN_ID.with_borrow_mut(|stores| {
110          stores.insert(*id, canister_id);
111      });
112  
113      Ok(canister_id)
114  }
115  
116  #[derive(CandidType, Deserialize, Debug, Clone, PartialEq, PartialOrd, Eq, Hash)]
117  pub struct CreateStoreError(CreateStoreRejectionCode, String);
118  
119  #[derive(CandidType, Deserialize, Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Hash)]
120  pub enum CreateStoreRejectionCode {
121      StoreAlreadyExists,
122      CallRejectionCode(RejectionCode),
123  }
124  
125  impl From<(RejectionCode, String)> for CreateStoreError {
126      fn from((code, msg): (RejectionCode, String)) -> Self {
127          CreateStoreError(CreateStoreRejectionCode::CallRejectionCode(code), msg)
128      }
129  }
130  
131  pub async fn get_item_page_data(arg: ItemPageRequest) -> Result<ItemPageResponse, ItemPageError> {
132      let market_canister = MARKETS_IN_ID
133          .with(|markets| markets.borrow().get(&arg.market_id))
134          .ok_or(ItemPageError(
135              ItemPageErrorCode::MarketNotFound,
136              format!("Market with id {} not found", arg.market_id),
137          ))?;
138  
139      let market_name = MARKETS
140          .with(|markets| {
141              markets
142                  .borrow()
143                  .get(&market_canister)
144                  .map(|market| market.name.clone())
145          })
146          .ok_or(ItemPageError(
147              ItemPageErrorCode::MarketNotFound,
148              format!("Market with id {} not found", arg.market_id),
149          ))?;
150  
151      let store_canister = STORES_IN_ID
152          .with(|stores| stores.borrow().get(&arg.store_id))
153          .ok_or(ItemPageError(
154              ItemPageErrorCode::StoreNotFound,
155              format!("Store with id {} not found", arg.store_id),
156          ))?;
157  
158      let (res,): (Result<ItemPageResponseFromStoreCanister, (ItemPageFromStoreErrorCode, String)>,) =
159          call(
160              store_canister.into(),
161              "get_item_page_data_from_store",
162              (caller(), arg.request_to_store),
163          )
164          .await?;
165  
166      let res = get_item_page_response(res?, market_name);
167  
168      Ok(res)
169  }
170  
171  pub async fn insert_items(
172      canister_id: StorePrincipal,
173      vec: Vec<Item>,
174  ) -> CallResult<Vec<(ItemId, ItemKey)>> {
175      let (res,) = call(canister_id.into(), "insert_items_to_store", (caller(), vec)).await?;
176      Ok(res)
177  }