This commit is contained in:
JMARyA 2024-06-22 02:05:22 +02:00
parent b8caa43972
commit 4295bdcb8d
Signed by: jmarya
GPG key ID: 901B2ADDF27C2263
7 changed files with 109 additions and 36 deletions

View file

@ -1,6 +1,6 @@
use mongodb::bson::doc;
use crate::get_mongo;
use crate::{get_mongo, id_of};
pub struct InventoryCache {}
@ -18,7 +18,7 @@ impl InventoryCache {
db.database("cdb")
.collection::<mongodb::bson::Document>("cache")
.find_one_and_update(doc! { "_id": "inventory"}, update, options)
.find_one_and_update(id_of!("inventory"), update, options)
.await
.unwrap();
}
@ -28,7 +28,7 @@ impl InventoryCache {
let inventorycache = db
.database("cdb")
.collection::<mongodb::bson::Document>("cache")
.find_one(doc! {"_id": "inventory"}, None)
.find_one(id_of!("inventory"), None)
.await
.unwrap()
.unwrap();
@ -52,7 +52,7 @@ impl InventoryCache {
db.database("cdb")
.collection::<mongodb::bson::Document>("cache")
.find_one_and_update(doc! { "_id": "inventory"}, update, options)
.find_one_and_update(id_of!("inventory"), update, options)
.await
.unwrap();
}

View file

@ -1,5 +1,6 @@
use crate::item::{Item, ItemEntry};
use crate::item::Item;
/// Collect database results into a `Vec<_>`
#[macro_export]
macro_rules! collect_results {
($res:expr) => {{
@ -14,6 +15,7 @@ macro_rules! collect_results {
}};
}
/// Get a database collection
#[macro_export]
macro_rules! cdb_col {
($db:expr, $col:expr) => {
@ -22,6 +24,7 @@ macro_rules! cdb_col {
};
}
/// Get a MongoDB Client from the environment
#[macro_export]
macro_rules! get_mongo {
() => {
@ -31,6 +34,7 @@ macro_rules! get_mongo {
};
}
/// MongoDB filter for the `_id` field.
#[macro_export]
macro_rules! id_of {
($id:expr) => {
@ -38,18 +42,22 @@ macro_rules! id_of {
};
}
/// Item database
pub struct ItemDB {
index: mdq::Index,
}
impl ItemDB {
/// Create a new item database using `dir` as the base.
///
/// The directory should contain markdown documents with valid frontmatter to be parsed into `Item`s
pub async fn new(dir: &str) -> Self {
// scan for markdown item entries
let index = mdq::Index::new(dir, true);
let mongodb = get_mongo!();
for item in &index.documents {
let item = ItemEntry::new(item);
let item = Item::new(item);
item.init_db(&mongodb).await;
}
@ -58,20 +66,20 @@ impl ItemDB {
/// Retrieves an item by name
pub fn get_item(&self, item: &str) -> Option<Item> {
Some(Item::new(
Some(
self.index
.documents
.iter()
.map(ItemEntry::new)
.map(Item::new) // <-- todo : performance?
.find(|x| x.name == item)?,
))
)
}
/// Get all items
pub fn items(&self) -> Vec<String> {
let mut ret = vec![];
for item in &self.index.documents {
let item = ItemEntry::new(item);
let item = Item::new(item);
ret.push(item.name);
}
ret

View file

@ -2,6 +2,7 @@ use std::collections::HashMap;
use crate::id_of;
use mongodb::{bson::doc, Collection};
use serde_json::json;
use crate::variant::Variant;
@ -25,13 +26,23 @@ fn hashmap_to_bson_document(
document
}
pub struct ItemEntry {
/// Represents a single item in a collection of physical goods.
///
/// This struct serves as a blueprint for describing individual items within
/// a larger inventory or database of physical goods. It includes fields for
/// the name of the item and its category.
pub struct Item {
/// The name of the Item
pub name: String,
/// Category of the Item
pub category: String,
/// The variants of an Item.
/// Each key of the `HashMap<_>` is the name of a variant and contains a `Variant`
pub variants: HashMap<String, Variant>,
}
impl ItemEntry {
impl Item {
/// Creates a new `Item` from a parsed markdown document
pub fn new(doc: &mdq::Document) -> Self {
let name = std::path::Path::new(&doc.path)
.file_stem()
@ -72,6 +83,7 @@ impl ItemEntry {
}
}
/// Adds the `Item` to the database
pub async fn init_db(&self, mongodb: &mongodb::Client) {
log::info!("Adding item {} to DB", self.name);
let items: Collection<mongodb::bson::Document> =
@ -100,25 +112,28 @@ impl ItemEntry {
}
}
/// Represents a single item in a collection of physical goods.
///
/// This struct serves as a blueprint for describing individual items within
/// a larger inventory or database of physical goods. It includes fields for
/// the name of the item and its category.
pub struct Item {
pub item: ItemEntry,
}
impl Item {
pub const fn new(item: ItemEntry) -> Self {
Self { item }
}
/// Get this items variant names
pub fn get_variants(&self) -> Vec<String> {
self.item.variants.keys().cloned().collect()
self.variants.keys().cloned().collect()
}
/// Get a `Variant` of an `Item`
pub fn variant(&self, variant: &str) -> Option<Variant> {
self.item.variants.get(variant).cloned()
self.variants.get(variant).cloned()
}
pub fn api_json(&self) -> serde_json::Value {
let variants: HashMap<String, serde_json::Value> = self
.variants
.iter()
.map(|(key, value)| (key.clone(), value.api_json()))
.collect();
json!({
"item": self.name,
"category": self.category,
"variants": variants
})
}
}

View file

@ -45,11 +45,13 @@ async fn rocket() -> _ {
"/",
route![
routes::item::get_items_route,
routes::item::item_route,
routes::item::item_variants_page,
routes::item::supply_log_route,
routes::item::demand_log_route,
routes::item::supply_route,
routes::item::demand_route,
routes::item::transaction_route
],
)
.manage(itemdb)

View file

@ -5,6 +5,7 @@ use serde::Deserialize;
use serde_json::json;
use crate::db::ItemDB;
use crate::transaction::Transaction;
use crate::variant::Variant;
use super::{api_error, ApiError, FallibleApiResponse};
@ -77,6 +78,14 @@ pub fn get_items_route(itemdb: &State<ItemDB>) -> serde_json::Value {
json!({"items": items})
}
#[get("/item/<item_id>")]
pub fn item_route(item_id: &str, itemdb: &State<ItemDB>) -> FallibleApiResponse {
let item = itemdb
.get_item(item_id)
.ok_or_else(item_does_not_exist_error)?;
Ok(item.api_json())
}
#[get("/item/<item_id>/variants")]
pub fn item_variants_page(item_id: &str, itemdb: &State<ItemDB>) -> FallibleApiResponse {
let item = itemdb
@ -123,3 +132,11 @@ pub async fn demand_log_route(
Ok(json!(transactions))
}
#[get("/transaction/<transaction>")]
pub async fn transaction_route(transaction: &str) -> FallibleApiResponse {
let t = Transaction::get(transaction)
.await
.ok_or_else(|| api_error("No transaction with this UUID"))?;
Ok(t.api_json())
}

View file

@ -1,3 +1,9 @@
use mongodb::bson::doc;
use serde::Serialize;
use serde_json::json;
use crate::{cdb_col, get_mongo, id_of};
pub enum TransactionType {
Batch(BatchTransaction),
Normal(Transaction),
@ -35,6 +41,24 @@ impl Transaction {
}
}
pub async fn get(uuid: &str) -> Option<Self> {
let db = get_mongo!();
let transactions = cdb_col!(db, "transactions");
let doc = transactions.find_one(id_of!(uuid), None).await.ok()??;
Some(doc.into())
}
pub fn api_json(&self) -> serde_json::Value {
json!({
"uuid": self.uuid,
"item": self.item,
"variant": self.variant,
"price": self.price,
"origin": self.origin,
"timestamp": self.timestamp
})
}
pub fn as_doc(&self) -> mongodb::bson::Document {
mongodb::bson::doc! {
"_id": &self.uuid,
@ -49,12 +73,12 @@ impl Transaction {
impl From<mongodb::bson::Document> for Transaction {
fn from(b: mongodb::bson::Document) -> Self {
let uuid = b.get_str("oid").unwrap().to_string();
let uuid = b.get_str("_id").unwrap().to_string();
let item = b.get_str("item").unwrap().to_string();
let variant = b.get_str("variant").unwrap().to_string();
let origin = b.get_str("origin").unwrap().to_string();
let timestamp = b.get_i64("timestamp").unwrap();
let price = Price::from(b);
let price = b.get_document("price").unwrap().clone().into();
Self {
uuid,
item,
@ -117,7 +141,7 @@ impl From<BatchTransaction> for mongodb::bson::Document {
}
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize)]
pub struct Price {
pub value: f64,
pub currency: String,
@ -153,7 +177,7 @@ impl From<Price> for mongodb::bson::Bson {
impl From<mongodb::bson::Document> for Price {
fn from(value: mongodb::bson::Document) -> Self {
Self {
value: value.get_f64("value").unwrap(),
value: value.get_f64("value").unwrap_or(0.0),
currency: value.get_str("currency").unwrap().to_string(),
}
}

View file

@ -1,8 +1,9 @@
use mongodb::bson::doc;
use serde_json::json;
use crate::{
cache::InventoryCache,
cdb_col, collect_results, get_mongo,
cdb_col, collect_results, get_mongo, id_of,
transaction::{BatchTransaction, Price, Transaction},
};
@ -103,10 +104,7 @@ impl Variant {
// check if supply transaction exists
let supply_t = cdb_col!(db, "supply");
supply_t
.find_one(doc! { "_id": uuid}, None)
.await
.unwrap()?;
supply_t.find_one(id_of!(uuid), None).await.unwrap()?;
// todo : demand batch
@ -183,6 +181,15 @@ impl Variant {
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,