use mongodb::bson::doc; use serde_json::json; use crate::{ cache::InventoryCache, cdb_col, collect_results, get_mongo, id_of, transaction::{BatchTransaction, Price, Transaction}, }; /// Represents a specific instance of an item with potential variations. /// /// This struct is used to describe a particular variation or instance of an item /// in the real world. It may include attributes or properties that deviate from /// the standard definition of the item. For example, different colors, sizes, or /// configurations. #[derive(Debug, Clone)] pub struct Variant { /// Associated Item pub item: String, /// Variant Name pub variant: String, /// Amount of items this variant represents pub amount: u64, /// Dependencies for the item variant. pub depends: Vec, } impl Variant { /// Create variant from itemdb yaml pub fn from_yml(json: &serde_yaml::Value, variant: &str, item: &str) -> Self { Self { item: item.to_string(), variant: variant.to_string(), amount: json .as_mapping() .unwrap() .get("amount") .map_or(1, |x| x.as_u64().unwrap()), depends: json .as_mapping() .unwrap() .get("depends") .map(|x| { x.as_sequence() .unwrap() .iter() .map(|x| x.as_str().unwrap().to_string()) .collect() }) .unwrap_or_default(), } } pub async fn supply_log(&self) -> Vec { let db = get_mongo!(); let supply = cdb_col!(db, "supply"); let filter = doc! { "item": &self.item, "variant": &self.variant }; let mut db_res = supply.find(filter, None).await.unwrap(); let result: Vec = collect_results!(db_res); let mut ret = Vec::new(); for doc in result { ret.push(doc.get("_id").unwrap().as_str().unwrap().to_string()); } ret } pub async fn demand_log(&self) -> Vec { // todo : add referenced supply let db = get_mongo!(); let demand = cdb_col!(db, "demand"); let filter = doc! { "item": &self.item, "variant": &self.variant }; let mut db_res = demand.find(filter, None).await.unwrap(); let result: Vec = collect_results!(db_res); let mut ret = Vec::new(); for doc in result { ret.push(doc.get("_id").unwrap().as_str().unwrap().to_string()); } ret } pub async fn demand(uuid: &str, price: Price, destination: &str) -> Option { let db = get_mongo!(); // check if supply transaction exists let supply_t = cdb_col!(db, "supply"); supply_t.find_one(id_of!(uuid), None).await.unwrap()?; // todo : demand batch // mark as used let demand_t = cdb_col!(db, "demand"); demand_t .insert_one( doc! { "_id": uuid, "destination": destination, "price": Into::::into(price) }, None, ) .await .unwrap(); // update cache InventoryCache::remove(uuid).await; Some(uuid.to_string()) } /// Records a supply transaction in the database. /// /// # Arguments /// /// * `amount` - The quantity of items supplied. /// * `price` - The price of the supplied items. /// * `origin` - The origin or source of the supplied items. /// /// # Returns /// /// Returns a UUID string representing the transaction. pub async fn supply(&self, amount: usize, price: Price, origin: &str) -> String { let db = get_mongo!(); let col: mongodb::Collection = db.database("cdb").collection("supply"); let mut transactions = vec![]; for _ in 0..amount { transactions.push(Transaction::new( &self.item, &self.variant, price.clone(), origin, )); } for transaction in &transactions { col.insert_one(transaction.as_doc(), None).await.unwrap(); // update cache InventoryCache::push(&transaction.uuid).await; } // batch transaction let ret_uuid = if amount == 1 { transactions.first().unwrap().uuid.clone() } else { let batch = BatchTransaction::new(transactions.iter().map(|x| x.uuid.clone()).collect()); let col: mongodb::Collection = db.database("cdb").collection("transactions_batch"); let batch_uuid = batch.uuid.clone(); col.insert_one(Into::::into(batch), None) .await .unwrap(); batch_uuid }; ret_uuid } pub fn api_json(&self) -> serde_json::Value { json!({ "item": self.item, "variant": self.variant, "amount": self.amount as u32, "depends": self.depends }) } pub fn as_bson(&self) -> mongodb::bson::Document { mongodb::bson::doc! { "item": &self.item, "variant": &self.variant, "amount": self.amount as u32, "depends": &self.depends } } }