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 }