diff options
Diffstat (limited to 'crates/rocie-server/src/storage/sql')
17 files changed, 449 insertions, 172 deletions
diff --git a/crates/rocie-server/src/storage/sql/barcode.rs b/crates/rocie-server/src/storage/sql/barcode.rs index 1c3c55a..f15d399 100644 --- a/crates/rocie-server/src/storage/sql/barcode.rs +++ b/crates/rocie-server/src/storage/sql/barcode.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use utoipa::ToSchema; -use crate::storage::sql::unit::UnitId; +use crate::storage::sql::unit::UnitAmount; #[derive(ToSchema, Debug, Clone, Serialize, Deserialize)] pub(crate) struct Barcode { @@ -41,10 +41,3 @@ impl From<BarcodeIdStub> for BarcodeId { Self { value: value.value } } } - -#[derive(ToSchema, Debug, Clone, Copy, Serialize, Deserialize)] -pub(crate) struct UnitAmount { - #[schema(minimum = 0)] - pub(crate) value: u32, - pub(crate) unit: UnitId, -} diff --git a/crates/rocie-server/src/storage/sql/get/barcode/mod.rs b/crates/rocie-server/src/storage/sql/get/barcode/mod.rs index 7b656b1..4eba105 100644 --- a/crates/rocie-server/src/storage/sql/get/barcode/mod.rs +++ b/crates/rocie-server/src/storage/sql/get/barcode/mod.rs @@ -1,8 +1,8 @@ use crate::{ app::App, storage::sql::{ - barcode::{Barcode, BarcodeId, UnitAmount}, - unit::UnitId, + barcode::{Barcode, BarcodeId}, + unit::{UnitAmount, UnitId}, }, }; diff --git a/crates/rocie-server/src/storage/sql/get/mod.rs b/crates/rocie-server/src/storage/sql/get/mod.rs index 048cb3d..62047b8 100644 --- a/crates/rocie-server/src/storage/sql/get/mod.rs +++ b/crates/rocie-server/src/storage/sql/get/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod product; pub(crate) mod product_amount; pub(crate) mod unit; +pub(crate) mod unit_property; pub(crate) mod barcode; diff --git a/crates/rocie-server/src/storage/sql/get/product/mod.rs b/crates/rocie-server/src/storage/sql/get/product/mod.rs index 541f388..01047b2 100644 --- a/crates/rocie-server/src/storage/sql/get/product/mod.rs +++ b/crates/rocie-server/src/storage/sql/get/product/mod.rs @@ -1,9 +1,10 @@ use crate::{ app::App, storage::sql::{ - barcode::{Barcode, BarcodeId, UnitAmount}, + barcode::{Barcode, BarcodeId}, product::{Product, ProductId}, - unit::UnitId, + unit::{UnitAmount, UnitId}, + unit_property::UnitPropertyId, }, }; @@ -13,7 +14,7 @@ impl Product { pub(crate) async fn from_id(app: &App, id: ProductId) -> Result<Option<Self>, from_id::Error> { let record = query!( " - SELECT name, description, parent + SELECT name, unit_property, description, parent FROM products WHERE id = ? ", @@ -25,6 +26,7 @@ impl Product { if let Some(record) = record { Ok(Some(Self { id, + unit_property: UnitPropertyId::from_db(&record.unit_property), name: record.name, description: record.description, associated_bar_codes: vec![], // todo @@ -37,7 +39,7 @@ impl Product { pub(crate) async fn get_all(app: &App) -> Result<Vec<Self>, get_all::Error> { let records = query!( " - SELECT id, name, description, parent + SELECT id, unit_property, name, description, parent FROM products " ) @@ -59,6 +61,7 @@ impl Product { all.push(Self { id: ProductId::from_db(&record.id), + unit_property: UnitPropertyId::from_db(&record.unit_property), name: record.name, description: record.description, associated_bar_codes: barcodes diff --git a/crates/rocie-server/src/storage/sql/get/product_amount/mod.rs b/crates/rocie-server/src/storage/sql/get/product_amount/mod.rs index 7700274..f82c2a0 100644 --- a/crates/rocie-server/src/storage/sql/get/product_amount/mod.rs +++ b/crates/rocie-server/src/storage/sql/get/product_amount/mod.rs @@ -1,8 +1,6 @@ use crate::{ app::App, - storage::sql::{ - barcode::UnitAmount, product::ProductId, product_amount::ProductAmount, unit::UnitId, - }, + storage::sql::{product::ProductId, product_amount::ProductAmount, unit::{UnitAmount, UnitId}}, }; use sqlx::query; @@ -20,6 +18,7 @@ SELECT FROM barcodes JOIN products ON products.id = ? JOIN buys ON buys.barcode_id = barcodes.id +WHERE barcodes.product_id = products.id GROUP BY barcodes.unit; "#, product_id diff --git a/crates/rocie-server/src/storage/sql/get/unit/mod.rs b/crates/rocie-server/src/storage/sql/get/unit/mod.rs index 6c2bbcc..6f5d297 100644 --- a/crates/rocie-server/src/storage/sql/get/unit/mod.rs +++ b/crates/rocie-server/src/storage/sql/get/unit/mod.rs @@ -1,6 +1,9 @@ use crate::{ app::App, - storage::sql::unit::{Unit, UnitId}, + storage::sql::{ + unit::{Unit, UnitId}, + unit_property::UnitPropertyId, + }, }; use sqlx::query; @@ -9,7 +12,7 @@ impl Unit { pub(crate) async fn get_all(app: &App) -> Result<Vec<Self>, get_all::Error> { let records = query!( " - SELECT id, full_name_singular, full_name_plural, short_name, description + SELECT id, unit_property, full_name_singular, full_name_plural, short_name, description FROM units " ) @@ -20,6 +23,7 @@ impl Unit { .into_iter() .map(|record| Self { id: UnitId::from_db(&record.id), + unit_property: UnitPropertyId::from_db(&record.unit_property), full_name_singular: record.full_name_singular, full_name_plural: record.full_name_plural, short_name: record.short_name, @@ -31,7 +35,7 @@ impl Unit { pub(crate) async fn from_id(app: &App, id: UnitId) -> Result<Option<Self>, from_id::Error> { let record = query!( " - SELECT full_name_singular, full_name_plural, short_name, description + SELECT full_name_singular, unit_property, full_name_plural, short_name, description FROM units WHERE id = ? ", @@ -43,6 +47,7 @@ impl Unit { if let Some(record) = record { Ok(Some(Self { id, + unit_property: UnitPropertyId::from_db(&record.unit_property), full_name_singular: record.full_name_singular, full_name_plural: record.full_name_plural, short_name: record.short_name, diff --git a/crates/rocie-server/src/storage/sql/get/unit_property/mod.rs b/crates/rocie-server/src/storage/sql/get/unit_property/mod.rs new file mode 100644 index 0000000..be24181 --- /dev/null +++ b/crates/rocie-server/src/storage/sql/get/unit_property/mod.rs @@ -0,0 +1,117 @@ +use crate::{ + app::App, + storage::sql::{ + unit::UnitId, + unit_property::{UnitProperty, UnitPropertyId}, + }, +}; + +use sqlx::query; + +impl UnitProperty { + pub(crate) async fn get_all(app: &App) -> Result<Vec<Self>, get_all::Error> { + let records = query!( + " + SELECT id, name, description + FROM unit_properties +" + ) + .fetch_all(&app.db) + .await?; + + let mut output = Vec::with_capacity(records.len()); + for record in records { + let units = query!( + " + SELECT id + FROM units + WHERE units.unit_property = ? + ", + record.id + ) + .fetch_all(&app.db) + .await?; + + let units = units + .into_iter() + .map(|record| UnitId::from_db(&record.id)) + .collect(); + + output.push(Self { + id: UnitPropertyId::from_db(&record.id), + units, + name: record.name, + description: record.description, + }) + } + + Ok(output) + } + + pub(crate) async fn from_id( + app: &App, + id: UnitPropertyId, + ) -> Result<Option<Self>, from_id::Error> { + let record = query!( + " + SELECT name, description + FROM unit_properties + WHERE id = ? +", + id + ) + .fetch_optional(&app.db) + .await?; + + if let Some(record) = record { + let units = query!( + " + SELECT id + FROM units + WHERE units.unit_property = ? + ", + id + ) + .fetch_all(&app.db) + .await?; + + let units = units + .into_iter() + .map(|record| UnitId::from_db(&record.id)) + .collect(); + + Ok(Some(Self { + id, + units, + name: record.name, + description: record.description, + })) + } else { + Ok(None) + } + } +} + +pub(crate) mod get_all { + use actix_web::ResponseError; + + #[derive(thiserror::Error, Debug)] + pub(crate) enum Error { + #[error("Failed to execute the sql query")] + SqlError(#[from] sqlx::Error), + } + + impl ResponseError for Error {} +} + +pub(crate) mod from_id { + use actix_web::ResponseError; + + #[derive(thiserror::Error, Debug)] + pub(crate) enum Error { + #[error("Failed to execute the sql query")] + SqlError(#[from] sqlx::Error), + } + + impl ResponseError for Error {} +} diff --git a/crates/rocie-server/src/storage/sql/insert/barcode/mod.rs b/crates/rocie-server/src/storage/sql/insert/barcode/mod.rs index 62a2e11..11707b9 100644 --- a/crates/rocie-server/src/storage/sql/insert/barcode/mod.rs +++ b/crates/rocie-server/src/storage/sql/insert/barcode/mod.rs @@ -9,9 +9,9 @@ use crate::{ storage::{ migrate::get_current_date, sql::{ - barcode::{Barcode, BarcodeId, UnitAmount}, + barcode::{Barcode, BarcodeId}, insert::{Operations, Transactionable}, - unit::Unit, + unit::{Unit, UnitAmount}, }, }, }; @@ -208,10 +208,7 @@ pub(crate) mod consume { use crate::storage::{ self, - sql::{ - barcode::UnitAmount, - unit::{Unit, UnitId}, - }, + sql::unit::{Unit, UnitAmount, UnitId}, }; #[derive(thiserror::Error, Debug)] diff --git a/crates/rocie-server/src/storage/sql/insert/mod.rs b/crates/rocie-server/src/storage/sql/insert/mod.rs index e6728d9..3b2d702 100644 --- a/crates/rocie-server/src/storage/sql/insert/mod.rs +++ b/crates/rocie-server/src/storage/sql/insert/mod.rs @@ -10,6 +10,7 @@ use sqlx::{SqliteConnection, query}; pub(crate) mod barcode; pub(crate) mod product; pub(crate) mod unit; +pub(crate) mod unit_property; pub(crate) trait Transactionable: Sized + std::fmt::Debug + Serialize + DeserializeOwned diff --git a/crates/rocie-server/src/storage/sql/insert/product/mod.rs b/crates/rocie-server/src/storage/sql/insert/product/mod.rs index e14d3f4..d762e9b 100644 --- a/crates/rocie-server/src/storage/sql/insert/product/mod.rs +++ b/crates/rocie-server/src/storage/sql/insert/product/mod.rs @@ -6,6 +6,7 @@ use crate::storage::sql::{ barcode::Barcode, insert::{Operations, Transactionable}, product::{Product, ProductId}, + unit_property::UnitPropertyId, }; #[derive(Debug, Deserialize, Serialize)] @@ -15,6 +16,7 @@ pub(crate) enum Operation { name: String, description: Option<String>, parent: Option<ProductId>, + unit_property: UnitPropertyId, }, AssociateBarcode { id: ProductId, @@ -33,13 +35,15 @@ impl Transactionable for Operation { name, description, parent, + unit_property, } => { query!( " - INSERT INTO products (id, name, description, parent) - VALUES (?,?,?,?) + INSERT INTO products (id, unit_property, name, description, parent) + VALUES (?,?,?,?,?) ", id, + unit_property, name, description, parent @@ -75,17 +79,19 @@ impl Transactionable for Operation { id, name, description, + unit_property, parent, } => { query!( " DELETE FROM products - WHERE id = ? AND name = ? AND description = ? AND parent = ?; + WHERE id = ? AND name = ? AND description = ? AND parent = ? AND unit_property = ?; ", id, name, description, - parent + parent, + unit_property ) .execute(txn) .await?; @@ -133,6 +139,7 @@ impl Product { name: String, description: Option<String>, parent: Option<ProductId>, + unit_property: UnitPropertyId, ops: &mut Operations<Operation>, ) -> Self { let id = ProductId::from(Uuid::new_v4()); @@ -141,6 +148,7 @@ impl Product { id, name: name.clone(), description: description.clone(), + unit_property, parent, }); @@ -148,6 +156,7 @@ impl Product { id, name, description, + unit_property, associated_bar_codes: vec![], } } diff --git a/crates/rocie-server/src/storage/sql/insert/unit/mod.rs b/crates/rocie-server/src/storage/sql/insert/unit/mod.rs index ba08487..815cb1e 100644 --- a/crates/rocie-server/src/storage/sql/insert/unit/mod.rs +++ b/crates/rocie-server/src/storage/sql/insert/unit/mod.rs @@ -2,7 +2,11 @@ use serde::{Deserialize, Serialize}; use sqlx::query; use uuid::Uuid; -use crate::storage::sql::{insert::{Operations, Transactionable}, unit::{Unit, UnitId}}; +use crate::storage::sql::{ + insert::{Operations, Transactionable}, + unit::{Unit, UnitId}, + unit_property::UnitPropertyId, +}; #[derive(Debug, Deserialize, Serialize)] pub(crate) enum Operation { @@ -11,6 +15,7 @@ pub(crate) enum Operation { full_name_singular: String, full_name_plural: String, short_name: String, + unit_property: UnitPropertyId, description: Option<String>, }, } @@ -27,13 +32,14 @@ impl Transactionable for Operation { full_name_plural, short_name, description, + unit_property, } => { query!( " - INSERT INTO units (id, full_name_singular, full_name_plural, short_name, description) - VALUES (?,?,?,?,?) + INSERT INTO units (id, unit_property, full_name_singular, full_name_plural, short_name, description) + VALUES (?,?,?,?,?,?) ", - id, full_name_singular, full_name_plural, short_name, description, + id, unit_property, full_name_singular, full_name_plural, short_name, description, ) .execute(txn) .await?; @@ -50,13 +56,14 @@ impl Transactionable for Operation { full_name_plural, short_name, description, + unit_property, } => { query!( " DELETE FROM units - WHERE id = ? AND full_name_singular = ? AND full_name_plural = ? AND short_name = ? AND description = ?; + WHERE id = ? AND full_name_singular = ? AND full_name_plural = ? AND short_name = ? AND description = ? AND unit_property = ?; ", - id, full_name_singular, full_name_plural, short_name, description, + id, full_name_singular, full_name_plural, short_name, description, unit_property ) .execute(txn) .await?; @@ -87,6 +94,7 @@ impl Unit { full_name_plural: String, short_name: String, description: Option<String>, + unit_property: UnitPropertyId, ops: &mut Operations<Operation>, ) -> Self { let id = UnitId::from(Uuid::new_v4()); @@ -97,6 +105,7 @@ impl Unit { full_name_plural: full_name_plural.clone(), short_name: short_name.clone(), description: description.clone(), + unit_property, }); Self { @@ -105,6 +114,7 @@ impl Unit { full_name_plural, short_name, description, + unit_property, } } } diff --git a/crates/rocie-server/src/storage/sql/insert/unit_property/mod.rs b/crates/rocie-server/src/storage/sql/insert/unit_property/mod.rs new file mode 100644 index 0000000..d340465 --- /dev/null +++ b/crates/rocie-server/src/storage/sql/insert/unit_property/mod.rs @@ -0,0 +1,106 @@ +use serde::{Deserialize, Serialize}; +use sqlx::query; +use uuid::Uuid; + +use crate::storage::sql::{ + insert::{Operations, Transactionable}, + unit_property::{UnitProperty, UnitPropertyId}, +}; + +#[derive(Debug, Deserialize, Serialize)] +pub(crate) enum Operation { + RegisterUnitProperty { + id: UnitPropertyId, + name: String, + description: Option<String>, + }, +} + +impl Transactionable for Operation { + type ApplyError = apply::Error; + type UndoError = undo::Error; + + async fn apply(self, txn: &mut sqlx::SqliteConnection) -> Result<(), apply::Error> { + match self { + Operation::RegisterUnitProperty { + id, + name, + description, + } => { + query!( + " + INSERT INTO unit_properties (id, name, description) + VALUES (?,?,?) +", + id, + name, + description, + ) + .execute(txn) + .await?; + } + } + Ok(()) + } + + async fn undo(self, txn: &mut sqlx::SqliteConnection) -> Result<(), undo::Error> { + match self { + Operation::RegisterUnitProperty { + id, + name, + description, + } => { + query!( + " + DELETE FROM unit_properties + WHERE id = ? AND name = ? AND description = ?; +", + id, + name, + description + ) + .execute(txn) + .await?; + } + } + Ok(()) + } +} + +pub(crate) mod undo { + #[derive(thiserror::Error, Debug)] + pub(crate) enum Error { + #[error("Failed to execute undo sql statments: {0}")] + SqlError(#[from] sqlx::Error), + } +} +pub(crate) mod apply { + #[derive(thiserror::Error, Debug)] + pub(crate) enum Error { + #[error("Failed to execute apply sql statments: {0}")] + SqlError(#[from] sqlx::Error), + } +} + +impl UnitProperty { + pub(crate) fn register( + name: String, + description: Option<String>, + ops: &mut Operations<Operation>, + ) -> Self { + let id = UnitPropertyId::from(Uuid::new_v4()); + + ops.push(Operation::RegisterUnitProperty { + id, + name: name.clone(), + description: description.clone(), + }); + + Self { + id, + units: vec![], + name, + description, + } + } +} diff --git a/crates/rocie-server/src/storage/sql/mod.rs b/crates/rocie-server/src/storage/sql/mod.rs index edce187..a44fbad 100644 --- a/crates/rocie-server/src/storage/sql/mod.rs +++ b/crates/rocie-server/src/storage/sql/mod.rs @@ -2,7 +2,100 @@ pub(crate) mod get; pub(crate) mod insert; // Types +pub(crate) mod barcode; pub(crate) mod product; pub(crate) mod product_amount; pub(crate) mod unit; -pub(crate) mod barcode; +pub(crate) mod unit_property; + +macro_rules! mk_id { + ($name:ident and $stub_name:ident) => { + mk_id!($name and $stub_name with uuid::Uuid, "uuid::Uuid"); + }; + ($name:ident and $stub_name:ident with $inner:path, $inner_string:literal $($args:meta)*) => { + #[derive( + serde::Deserialize, + serde::Serialize, + Debug, + Default, + utoipa::ToSchema, + Clone, + Copy, + PartialEq, + Eq, + PartialOrd, + Ord, + )] + pub(crate) struct $name { + value: $inner, + } + + #[derive(Deserialize, Serialize, Debug, Clone, Copy)] + #[serde(from = $inner_string)] + pub(crate) struct $stub_name { + value: $inner, + } + + impl $name { + pub(crate) fn from_db(id: &str) -> Self { + use std::str::FromStr; + + Self { + value: <$inner as FromStr>::from_str(id) + .expect( + concat!( + "We put an ", + $inner_string, + " into the db, it should also go out again" + ) + ), + } + } + } + + impl std::fmt::Display for $name { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.value) + } + } + + impl From<$inner> for $name { + fn from(value: $inner) -> Self { + Self { value } + } + } + impl From<$inner> for $stub_name { + fn from(value: $inner) -> Self { + Self { value } + } + } + impl From<$stub_name> for $name { + fn from(value: $stub_name) -> Self { + Self { value: value.value } + } + } + + impl<'q, DB: sqlx::Database> sqlx::Encode<'q, DB> for $name + where + String: sqlx::Encode<'q, DB>, + { + fn encode_by_ref( + &self, + buf: &mut <DB as sqlx::Database>::ArgumentBuffer<'q>, + ) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> { + let inner = self.value.to_string(); + sqlx::Encode::<DB>::encode_by_ref(&inner, buf) + } + } + impl<DB: sqlx::Database> sqlx::Type<DB> for $name + where + String: sqlx::Type<DB>, + { + fn type_info() -> DB::TypeInfo { + <String as sqlx::Type<DB>>::type_info() + } + } + }; +} + +pub(crate) use mk_id; diff --git a/crates/rocie-server/src/storage/sql/product.rs b/crates/rocie-server/src/storage/sql/product.rs index c94fcce..2575b59 100644 --- a/crates/rocie-server/src/storage/sql/product.rs +++ b/crates/rocie-server/src/storage/sql/product.rs @@ -1,82 +1,32 @@ -use std::{fmt::Display, str::FromStr}; - use serde::{Deserialize, Serialize}; -use sqlx::{Database, Encode, Type}; use utoipa::ToSchema; -use uuid::Uuid; -use crate::storage::sql::barcode::Barcode; +use crate::storage::sql::{barcode::Barcode, mk_id, unit_property::UnitPropertyId}; +/// The base of rocie. +/// +/// Products can be bought and consumed and represent, what you actually have in storage. +/// Not every product is bought, as some can also be obtained by cooking a recipe. #[derive(Clone, ToSchema, Serialize, Deserialize)] pub(crate) struct Product { + /// The id of the product. pub(crate) id: ProductId, - pub(super) name: String, - pub(super) description: Option<String>, - pub(super) associated_bar_codes: Vec<Barcode>, -} - -#[derive( - Deserialize, Serialize, Debug, Default, ToSchema, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, -)] -pub(crate) struct ProductId { - value: Uuid, -} - -#[derive(Deserialize, Serialize, Debug, Clone, Copy)] -#[serde(from = "Uuid")] -pub(crate) struct ProductIdStub { - value: Uuid, -} -impl ProductId { - pub(crate) fn from_db(id: &str) -> ProductId { - Self { - value: Uuid::from_str(id) - .expect("We put an uuid into the db, it should also go out again"), - } - } -} + /// The property this product is measured in. + /// + /// (This is probably always either Mass, Volume or Quantity). + pub(crate) unit_property: UnitPropertyId, -impl Display for ProductId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.value) - } -} + /// The name of the product. + /// + /// This should be globally unique, to make searching easier for the user. + pub(super) name: String, -impl From<Uuid> for ProductId { - fn from(value: Uuid) -> Self { - Self { value } - } -} + /// An optional description of this product. + pub(super) description: Option<String>, -impl From<Uuid> for ProductIdStub { - fn from(value: Uuid) -> Self { - Self { value } - } -} -impl From<ProductIdStub> for ProductId { - fn from(value: ProductIdStub) -> Self { - Self { value: value.value } - } + /// Which barcodes are associated with this product. + pub(super) associated_bar_codes: Vec<Barcode>, } -impl<'q, DB: Database> Encode<'q, DB> for ProductId -where - String: Encode<'q, DB>, -{ - fn encode_by_ref( - &self, - buf: &mut <DB as Database>::ArgumentBuffer<'q>, - ) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> { - let inner = self.value.to_string(); - Encode::<DB>::encode_by_ref(&inner, buf) - } -} -impl<DB: Database> Type<DB> for ProductId -where - String: Type<DB>, -{ - fn type_info() -> DB::TypeInfo { - <String as Type<DB>>::type_info() - } -} +mk_id!(ProductId and ProductIdStub); diff --git a/crates/rocie-server/src/storage/sql/product_amount.rs b/crates/rocie-server/src/storage/sql/product_amount.rs index 232c5db..0f19afc 100644 --- a/crates/rocie-server/src/storage/sql/product_amount.rs +++ b/crates/rocie-server/src/storage/sql/product_amount.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use utoipa::ToSchema; -use crate::storage::sql::{barcode::UnitAmount, product::ProductId}; +use crate::storage::sql::{product::ProductId, unit::UnitAmount}; #[derive(Clone, ToSchema, Serialize, Deserialize)] pub(crate) struct ProductAmount { diff --git a/crates/rocie-server/src/storage/sql/unit.rs b/crates/rocie-server/src/storage/sql/unit.rs index 77e7a2e..d16e783 100644 --- a/crates/rocie-server/src/storage/sql/unit.rs +++ b/crates/rocie-server/src/storage/sql/unit.rs @@ -1,80 +1,50 @@ -use std::{fmt::Display, str::FromStr}; - use serde::{Deserialize, Serialize}; -use sqlx::{Database, Encode, Type}; use utoipa::ToSchema; -use uuid::Uuid; + +use crate::storage::sql::{mk_id, unit_property::UnitPropertyId}; #[derive(ToSchema, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize)] pub(crate) struct Unit { + /// Unique id for this unit. pub(crate) id: UnitId, + + /// The singular version of this unit's name. + /// E.g.: + /// Kilogram + /// Gram pub(crate) full_name_singular: String, - pub(crate) full_name_plural: String, - pub(crate) short_name: String, - pub(crate) description: Option<String>, -} -#[derive( - Deserialize, Serialize, Debug, Default, ToSchema, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, -)] -pub(crate) struct UnitId { - value: Uuid, -} + /// The plural version of this unit's name. Is used by a value of two and more. + /// (We do not support Slovenian dual numerus versions) + /// E.g.: + /// Kilogram -> Kilograms + /// gram -> meters + pub(crate) full_name_plural: String, -#[derive(Deserialize, Serialize, Debug, Clone, Copy)] -#[serde(from = "Uuid")] -pub(crate) struct UnitIdStub { - value: Uuid, -} + /// Short name or abbreviation of this unit. + /// E.g.: + /// kg for Kilogram + /// g for gram + /// m for meter + pub(crate) short_name: String, -impl UnitId { - pub(crate) fn from_db(id: &str) -> UnitId { - Self { - value: Uuid::from_str(id) - .expect("We put an uuid into the db, it should also go out again"), - } - } -} + /// Description of this unit. + pub(crate) description: Option<String>, -impl Display for UnitId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.value) - } + /// Which property is described by this unit. + /// E.g.: + /// kg -> Mass + /// L -> Volume + /// m/s -> Speed + /// and so forth + pub(crate) unit_property: UnitPropertyId, } -impl From<Uuid> for UnitId { - fn from(value: Uuid) -> Self { - Self { value } - } -} -impl From<Uuid> for UnitIdStub { - fn from(value: Uuid) -> Self { - Self { value } - } -} -impl From<UnitIdStub> for UnitId { - fn from(value: UnitIdStub) -> Self { - Self { value: value.value } - } +#[derive(ToSchema, Debug, Clone, Copy, Serialize, Deserialize)] +pub(crate) struct UnitAmount { + #[schema(minimum = 0)] + pub(crate) value: u32, + pub(crate) unit: UnitId, } -impl<'q, DB: Database> Encode<'q, DB> for UnitId -where - String: Encode<'q, DB>, -{ - fn encode_by_ref( - &self, - buf: &mut <DB as Database>::ArgumentBuffer<'q>, - ) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> { - let inner = self.value.to_string(); - Encode::<DB>::encode_by_ref(&inner, buf) - } -} -impl<DB: Database> Type<DB> for UnitId -where - String: Type<DB>, -{ - fn type_info() -> DB::TypeInfo { - <String as Type<DB>>::type_info() - } -} +mk_id!(UnitId and UnitIdStub); diff --git a/crates/rocie-server/src/storage/sql/unit_property.rs b/crates/rocie-server/src/storage/sql/unit_property.rs new file mode 100644 index 0000000..9da2d2e --- /dev/null +++ b/crates/rocie-server/src/storage/sql/unit_property.rs @@ -0,0 +1,23 @@ +use serde::{Deserialize, Serialize}; +use utoipa::ToSchema; + +use crate::storage::sql::{mk_id, unit::UnitId}; + +/// An unit property describes a property that can be measured by units. +/// For example velocity, mass or volume. +#[derive(ToSchema, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize)] +pub(crate) struct UnitProperty { + /// The unique ID for this unit property. + pub(crate) id: UnitPropertyId, + + /// The user-displayed name of this property. + pub(crate) name: String, + + /// The units with are measuring this property. + pub(crate) units: Vec<UnitId>, + + /// An description of this property. + pub(crate) description: Option<String>, +} + +mk_id!(UnitPropertyId and UnitPropertyIdStub); |
