diff --git a/.sqlx/query-9390910c71001ef77313e594dea0e4f0682f740778e483a2db91d6c73ac6bc6d.json b/.sqlx/query-c091fbfdc77afd15d73933c5092be72fda7a14b38462b52a8a4d6e8eee00f7c6.json similarity index 63% rename from .sqlx/query-9390910c71001ef77313e594dea0e4f0682f740778e483a2db91d6c73ac6bc6d.json rename to .sqlx/query-c091fbfdc77afd15d73933c5092be72fda7a14b38462b52a8a4d6e8eee00f7c6.json index d70d210..bcc402b 100644 --- a/.sqlx/query-9390910c71001ef77313e594dea0e4f0682f740778e483a2db91d6c73ac6bc6d.json +++ b/.sqlx/query-c091fbfdc77afd15d73933c5092be72fda7a14b38462b52a8a4d6e8eee00f7c6.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO cqrs_inventory_product_query (\n version,\n name,\n description,\n image,\n product_id,\n category_id,\n price_major,\n price_minor,\n price_currency,\n sku_able,\n quantity_unit,\n quantity_number,\n deleted\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13\n );", + "query": "INSERT INTO cqrs_inventory_product_query (\n version,\n name,\n description,\n image,\n product_id,\n category_id,\n price_major,\n price_minor,\n price_currency,\n sku_able,\n quantity_minor_unit,\n quantity_minor_number,\n quantity_major_unit,\n quantity_major_number,\n\n deleted\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15\n );", "describe": { "columns": [], "parameters": { @@ -17,10 +17,12 @@ "Bool", "Text", "Int4", + "Text", + "Int4", "Bool" ] }, "nullable": [] }, - "hash": "9390910c71001ef77313e594dea0e4f0682f740778e483a2db91d6c73ac6bc6d" + "hash": "c091fbfdc77afd15d73933c5092be72fda7a14b38462b52a8a4d6e8eee00f7c6" } diff --git a/.sqlx/query-f55fe530202bb369fdea3baaf91f86bbc866218e4146b6d608255c1b929073b4.json b/.sqlx/query-e2f9f291a20aac77851774ba8cd37325143a4d98e0980632f097c5885cc71094.json similarity index 70% rename from .sqlx/query-f55fe530202bb369fdea3baaf91f86bbc866218e4146b6d608255c1b929073b4.json rename to .sqlx/query-e2f9f291a20aac77851774ba8cd37325143a4d98e0980632f097c5885cc71094.json index b95a37b..d8268b4 100644 --- a/.sqlx/query-f55fe530202bb369fdea3baaf91f86bbc866218e4146b6d608255c1b929073b4.json +++ b/.sqlx/query-e2f9f291a20aac77851774ba8cd37325143a4d98e0980632f097c5885cc71094.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "UPDATE\n cqrs_inventory_product_query\n SET\n version = $1,\n name = $2,\n description = $3,\n image = $4,\n product_id = $5,\n category_id = $6,\n price_major = $7,\n price_minor = $8,\n price_currency = $9,\n sku_able = $10,\n quantity_unit = $11,\n quantity_number = $12,\n deleted = $13;", + "query": "UPDATE\n cqrs_inventory_product_query\n SET\n version = $1,\n name = $2,\n description = $3,\n image = $4,\n product_id = $5,\n category_id = $6,\n price_major = $7,\n price_minor = $8,\n price_currency = $9,\n sku_able = $10,\n quantity_minor_unit = $11,\n quantity_minor_number = $12,\n quantity_major_unit = $13,\n quantity_major_number = $14,\n deleted = $15;", "describe": { "columns": [], "parameters": { @@ -17,10 +17,12 @@ "Bool", "Text", "Int4", + "Text", + "Int4", "Bool" ] }, "nullable": [] }, - "hash": "f55fe530202bb369fdea3baaf91f86bbc866218e4146b6d608255c1b929073b4" + "hash": "e2f9f291a20aac77851774ba8cd37325143a4d98e0980632f097c5885cc71094" } diff --git a/.sqlx/query-3f83167782a2de1be7d87d35f63a3e530373595ce83cd36612ee48d3c36c81f3.json b/.sqlx/query-f40df2534cbf3a44afdd27bb19cff0c62d56d9236356287d56e965b5e2a7b3d2.json similarity index 69% rename from .sqlx/query-3f83167782a2de1be7d87d35f63a3e530373595ce83cd36612ee48d3c36c81f3.json rename to .sqlx/query-f40df2534cbf3a44afdd27bb19cff0c62d56d9236356287d56e965b5e2a7b3d2.json index bfc6121..ef2e753 100644 --- a/.sqlx/query-3f83167782a2de1be7d87d35f63a3e530373595ce83cd36612ee48d3c36c81f3.json +++ b/.sqlx/query-f40df2534cbf3a44afdd27bb19cff0c62d56d9236356287d56e965b5e2a7b3d2.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT \n name,\n description,\n image,\n product_id,\n category_id,\n price_major,\n price_minor,\n price_currency,\n sku_able,\n quantity_unit,\n quantity_number,\n deleted\n FROM\n cqrs_inventory_product_query\n WHERE\n product_id = $1;", + "query": "SELECT \n name,\n description,\n image,\n product_id,\n category_id,\n price_major,\n price_minor,\n price_currency,\n sku_able,\n quantity_minor_unit,\n quantity_minor_number,\n quantity_major_unit,\n quantity_major_number,\n deleted\n FROM\n cqrs_inventory_product_query\n WHERE\n product_id = $1;", "describe": { "columns": [ { @@ -50,16 +50,26 @@ }, { "ordinal": 9, - "name": "quantity_unit", + "name": "quantity_minor_unit", "type_info": "Text" }, { "ordinal": 10, - "name": "quantity_number", + "name": "quantity_minor_number", "type_info": "Int4" }, { "ordinal": 11, + "name": "quantity_major_unit", + "type_info": "Text" + }, + { + "ordinal": 12, + "name": "quantity_major_number", + "type_info": "Int4" + }, + { + "ordinal": 13, "name": "deleted", "type_info": "Bool" } @@ -81,8 +91,10 @@ false, false, false, + false, + false, false ] }, - "hash": "3f83167782a2de1be7d87d35f63a3e530373595ce83cd36612ee48d3c36c81f3" + "hash": "f40df2534cbf3a44afdd27bb19cff0c62d56d9236356287d56e965b5e2a7b3d2" } diff --git a/migrations/20240715113708_cqrs_inventory_product_query.sql b/migrations/20240715113708_cqrs_inventory_product_query.sql index 8fdc3fd..5dd3bd4 100644 --- a/migrations/20240715113708_cqrs_inventory_product_query.sql +++ b/migrations/20240715113708_cqrs_inventory_product_query.sql @@ -17,8 +17,10 @@ CREATE TABLE IF NOT EXISTS cqrs_inventory_product_query price_major INTEGER NOT NULL, price_currency TEXT NOT NULL, - quantity_number INTEGER NOT NULL, - quantity_unit TEXT NOT NULL, + quantity_major_number INTEGER NOT NULL, + quantity_minor_number INTEGER NOT NULL, + quantity_major_unit TEXT NOT NULL, + quantity_minor_unit TEXT NOT NULL, category_id UUID NOT NULL, diff --git a/src/inventory/adapters/output/db/postgres/product_id_exists.rs b/src/inventory/adapters/output/db/postgres/product_id_exists.rs index 2738e55..99294e2 100644 --- a/src/inventory/adapters/output/db/postgres/product_id_exists.rs +++ b/src/inventory/adapters/output/db/postgres/product_id_exists.rs @@ -84,11 +84,13 @@ pub mod tests { price_minor, price_currency, sku_able, - quantity_unit, - quantity_number, + quantity_minor_unit, + quantity_minor_number, + quantity_major_unit, + quantity_major_number, deleted ) VALUES ( - $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13 + $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15 );", 1, p.name(), @@ -100,8 +102,10 @@ pub mod tests { p.price().minor().clone() as i32, p.price().currency().to_string(), p.sku_able().clone(), - p.quantity().unit().to_string(), - p.quantity().number().clone() as i32, + p.quantity().major().unit().to_string(), + p.quantity().major().number().clone() as i32, + p.quantity().minor().unit().to_string(), + p.quantity().minor().number().clone() as i32, p.deleted().clone(), ) .execute(&db.pool) diff --git a/src/inventory/adapters/output/db/postgres/product_view.rs b/src/inventory/adapters/output/db/postgres/product_view.rs index 38f7e5a..ecafd4e 100644 --- a/src/inventory/adapters/output/db/postgres/product_view.rs +++ b/src/inventory/adapters/output/db/postgres/product_view.rs @@ -14,8 +14,9 @@ use super::errors::*; use super::InventoryDBPostgresAdapter; use crate::inventory::domain::events::InventoryEvent; use crate::inventory::domain::product_aggregate::{ - Currency, PriceBuilder, Product, ProductBuilder, QuantityBuilder, QuantityUnit, + Currency, PriceBuilder, Product, ProductBuilder, }; +use crate::types::quantity::*; use crate::utils::parse_aggregate_id::parse_aggregate_id; pub const NEW_PRODUCT_NON_UUID: &str = "new_product_non_uuid-asdfa"; @@ -34,8 +35,10 @@ pub struct ProductView { price_major: i32, price_currency: String, - quantity_unit: String, - quantity_number: i32, + quantity_major_number: i32, + quantity_minor_number: i32, + quantity_major_unit: String, + quantity_minor_unit: String, category_id: Uuid, @@ -52,8 +55,20 @@ impl From for Product { .unwrap(); let quantity = QuantityBuilder::default() - .number(v.quantity_number as usize) - .unit(QuantityUnit::from_str(&v.quantity_unit).unwrap()) + .minor( + QuantityPartBuilder::default() + .number(v.quantity_minor_number as usize) + .unit(QuantityUnit::from_str(&v.quantity_minor_unit).unwrap()) + .build() + .unwrap(), + ) + .major( + QuantityPartBuilder::default() + .number(v.quantity_major_number as usize) + .unit(QuantityUnit::from_str(&v.quantity_major_unit).unwrap()) + .build() + .unwrap(), + ) .build() .unwrap(); @@ -91,8 +106,10 @@ impl View for ProductView { self.price_major = *val.price().major() as i32; self.price_currency = val.price().currency().to_string(); - self.quantity_number = *val.quantity().number() as i32; - self.quantity_unit = val.quantity().unit().to_string(); + self.quantity_major_number = *val.quantity().major().number() as i32; + self.quantity_minor_number = *val.quantity().minor().number() as i32; + self.quantity_major_unit = val.quantity().major().unit().to_string(); + self.quantity_minor_unit = val.quantity().minor().unit().to_string(); self.deleted = false; } @@ -121,8 +138,10 @@ impl ViewRepository for InventoryDBPostgresAdapter { price_minor, price_currency, sku_able, - quantity_unit, - quantity_number, + quantity_minor_unit, + quantity_minor_number, + quantity_major_unit, + quantity_major_number, deleted FROM cqrs_inventory_product_query @@ -158,8 +177,10 @@ impl ViewRepository for InventoryDBPostgresAdapter { price_minor, price_currency, sku_able, - quantity_unit, - quantity_number, + quantity_minor_unit, + quantity_minor_number, + quantity_major_unit, + quantity_major_number, deleted FROM cqrs_inventory_product_query @@ -214,11 +235,14 @@ impl ViewRepository for InventoryDBPostgresAdapter { price_minor, price_currency, sku_able, - quantity_unit, - quantity_number, + quantity_minor_unit, + quantity_minor_number, + quantity_major_unit, + quantity_major_number, + deleted ) VALUES ( - $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13 + $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15 );", version, view.name, @@ -230,8 +254,10 @@ impl ViewRepository for InventoryDBPostgresAdapter { view.price_minor, view.price_currency, view.sku_able, - view.quantity_unit, - view.quantity_number, + view.quantity_minor_unit, + view.quantity_minor_number, + view.quantity_major_unit, + view.quantity_major_number, view.deleted, ) .execute(&self.pool) @@ -255,9 +281,11 @@ impl ViewRepository for InventoryDBPostgresAdapter { price_minor = $8, price_currency = $9, sku_able = $10, - quantity_unit = $11, - quantity_number = $12, - deleted = $13;", + quantity_minor_unit = $11, + quantity_minor_number = $12, + quantity_major_unit = $13, + quantity_major_number = $14, + deleted = $15;", version, view.name, view.description, @@ -268,8 +296,10 @@ impl ViewRepository for InventoryDBPostgresAdapter { view.price_minor, view.price_currency, view.sku_able, - view.quantity_unit, - view.quantity_number, + view.quantity_minor_unit, + view.quantity_minor_number, + view.quantity_major_unit, + view.quantity_major_number, view.deleted, ) .execute(&self.pool) diff --git a/src/inventory/domain/add_product_command.rs b/src/inventory/domain/add_product_command.rs index f5ef74a..3f63d35 100644 --- a/src/inventory/domain/add_product_command.rs +++ b/src/inventory/domain/add_product_command.rs @@ -8,7 +8,8 @@ use derive_more::{Display, Error}; use serde::{Deserialize, Serialize}; use uuid::Uuid; -use super::product_aggregate::{Price, Quantity}; +use super::product_aggregate::Price; +use crate::types::quantity::*; #[derive(Debug, Error, Display, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub enum AddProductCommandError { @@ -88,9 +89,7 @@ pub mod tests { use super::*; use crate::{ - inventory::domain::product_aggregate::{ - Currency, PriceBuilder, QuantityBuilder, QuantityUnit, - }, + inventory::domain::product_aggregate::{Currency, PriceBuilder}, utils::uuid::tests::UUID, }; @@ -110,8 +109,20 @@ pub mod tests { .unwrap(); let quantity = QuantityBuilder::default() - .number(1) - .unit(QuantityUnit::DiscreteNumber) + .minor( + QuantityPartBuilder::default() + .number(0) + .unit(QuantityUnit::DiscreteNumber) + .build() + .unwrap(), + ) + .major( + QuantityPartBuilder::default() + .number(1) + .unit(QuantityUnit::DiscreteNumber) + .build() + .unwrap(), + ) .build() .unwrap(); @@ -143,11 +154,8 @@ pub mod tests { .currency(Currency::INR) .build() .unwrap(); - let quantity = QuantityBuilder::default() - .number(1) - .unit(QuantityUnit::DiscreteNumber) - .build() - .unwrap(); + + let quantity = Quantity::default(); // description = None let cmd = UnvalidatedAddProductCommandBuilder::default() @@ -188,11 +196,8 @@ pub mod tests { .currency(Currency::INR) .build() .unwrap(); - let quantity = QuantityBuilder::default() - .number(1) - .unit(QuantityUnit::DiscreteNumber) - .build() - .unwrap(); + + let quantity = Quantity::default(); let cmd = UnvalidatedAddProductCommandBuilder::default() .name(name.into()) @@ -233,11 +238,8 @@ pub mod tests { .currency(Currency::INR) .build() .unwrap(); - let quantity = QuantityBuilder::default() - .number(1) - .unit(QuantityUnit::DiscreteNumber) - .build() - .unwrap(); + + let quantity = Quantity::default(); let cmd = UnvalidatedAddProductCommandBuilder::default() .name("".into()) diff --git a/src/inventory/domain/product_added_event.rs b/src/inventory/domain/product_added_event.rs index 6c74e41..c642526 100644 --- a/src/inventory/domain/product_added_event.rs +++ b/src/inventory/domain/product_added_event.rs @@ -7,7 +7,8 @@ use derive_getters::Getters; use serde::{Deserialize, Serialize}; use uuid::Uuid; -use super::product_aggregate::{Price, Quantity}; +use super::product_aggregate::Price; +use crate::types::quantity::Quantity; #[derive( Clone, Debug, Builder, Serialize, Deserialize, Getters, Eq, PartialEq, Ord, PartialOrd, diff --git a/src/inventory/domain/product_aggregate.rs b/src/inventory/domain/product_aggregate.rs index b687109..b67ae4f 100644 --- a/src/inventory/domain/product_aggregate.rs +++ b/src/inventory/domain/product_aggregate.rs @@ -15,54 +15,7 @@ use uuid::Uuid; use super::{commands::InventoryCommand, events::InventoryEvent}; use crate::inventory::application::services::errors::*; use crate::inventory::application::services::InventoryServicesInterface; - -#[derive(Clone, Display, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd)] -pub enum QuantityUnit { - #[display(fmt = "{}", KILO_GRAM)] - Kilogram, - #[display(fmt = "{}", GRAM)] - Gram, - #[display(fmt = "{}", DISCRETE_NUMBER)] - DiscreteNumber, // example: 1 sofa, 2 bed, etc. - #[display(fmt = "{}", MILLI_LITER)] - MilliLiter, - #[display(fmt = "{}", LITER)] - Liter, -} - -impl Default for QuantityUnit { - fn default() -> Self { - Self::DiscreteNumber - } -} - -const KILO_GRAM: &str = "kg"; -const GRAM: &str = "g"; -const DISCRETE_NUMBER: &str = "discrete_number"; -const MILLI_LITER: &str = "ml"; -const LITER: &str = "l"; - -impl FromStr for QuantityUnit { - type Err = String; - fn from_str(s: &str) -> Result { - match s.trim() { - KILO_GRAM => Ok(Self::Kilogram), - GRAM => Ok(Self::Gram), - DISCRETE_NUMBER => Ok(Self::DiscreteNumber), - MILLI_LITER => Ok(Self::MilliLiter), - LITER => Ok(Self::Liter), - _ => Err("Currency unsupported".into()), - } - } -} - -#[derive( - Clone, Debug, Serialize, Default, Deserialize, Eq, PartialEq, Ord, PartialOrd, Builder, Getters, -)] -pub struct Quantity { - number: usize, - unit: QuantityUnit, -} +use crate::types::quantity::Quantity; #[derive(Clone, Display, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd)] pub enum Currency { @@ -250,29 +203,4 @@ mod aggregate_tests { fn currency_to_string_from_str() { assert!(test_helper(Currency::INR, INR)); } - - #[test] - fn quantity_unit_kilogram() { - assert!(test_helper(QuantityUnit::Kilogram, KILO_GRAM)); - } - - #[test] - fn quantity_unit_gram() { - assert!(test_helper(QuantityUnit::Gram, GRAM)); - } - - #[test] - fn quantity_unit_discrete_number() { - assert!(test_helper(QuantityUnit::DiscreteNumber, DISCRETE_NUMBER)); - } - - #[test] - fn quantity_unit_milli_liter() { - assert!(test_helper(QuantityUnit::MilliLiter, MILLI_LITER)); - } - - #[test] - fn quantity_unit_liter() { - assert!(test_helper(QuantityUnit::Liter, LITER)); - } } diff --git a/src/inventory/domain/stock_aggregate.rs b/src/inventory/domain/stock_aggregate.rs index cda309f..8017f39 100644 --- a/src/inventory/domain/stock_aggregate.rs +++ b/src/inventory/domain/stock_aggregate.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; use time::OffsetDateTime; use uuid::Uuid; -use super::product_aggregate::Quantity; +use crate::types::quantity::Quantity; // stock keeping unit // TODO: will implement later, have to figure out how to print SKU label and during billing. diff --git a/src/inventory/domain/update_product_command.rs b/src/inventory/domain/update_product_command.rs index d5938ec..313c18a 100644 --- a/src/inventory/domain/update_product_command.rs +++ b/src/inventory/domain/update_product_command.rs @@ -10,7 +10,8 @@ use derive_more::{Display, Error}; use serde::{Deserialize, Serialize}; use uuid::Uuid; -use super::product_aggregate::{Price, Product, Quantity}; +use super::product_aggregate::{Price, Product}; +use crate::types::quantity::Quantity; #[derive(Debug, Error, Display, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub enum UpdateProductCommandError { @@ -92,10 +93,9 @@ impl UnvalidatedUpdateProductCommand { pub mod tests { use super::*; + use crate::types::quantity::*; use crate::{ - inventory::domain::product_aggregate::{ - Currency, PriceBuilder, QuantityBuilder, QuantityUnit, - }, + inventory::domain::product_aggregate::{Currency, PriceBuilder}, utils::uuid::tests::UUID, }; @@ -115,8 +115,20 @@ pub mod tests { .unwrap(); let quantity = QuantityBuilder::default() - .number(1) - .unit(QuantityUnit::DiscreteNumber) + .minor( + QuantityPartBuilder::default() + .number(0) + .unit(QuantityUnit::DiscreteNumber) + .build() + .unwrap(), + ) + .major( + QuantityPartBuilder::default() + .number(1) + .unit(QuantityUnit::DiscreteNumber) + .build() + .unwrap(), + ) .build() .unwrap(); @@ -149,11 +161,8 @@ pub mod tests { .currency(Currency::INR) .build() .unwrap(); - let quantity = QuantityBuilder::default() - .number(1) - .unit(QuantityUnit::DiscreteNumber) - .build() - .unwrap(); + + let quantity = Quantity::default(); // description = None let cmd = UnvalidatedUpdateProductCommandBuilder::default() @@ -196,11 +205,8 @@ pub mod tests { .currency(Currency::INR) .build() .unwrap(); - let quantity = QuantityBuilder::default() - .number(1) - .unit(QuantityUnit::DiscreteNumber) - .build() - .unwrap(); + + let quantity = Quantity::default(); let cmd = UnvalidatedUpdateProductCommandBuilder::default() .name(name.into()) @@ -242,11 +248,8 @@ pub mod tests { .currency(Currency::INR) .build() .unwrap(); - let quantity = QuantityBuilder::default() - .number(1) - .unit(QuantityUnit::DiscreteNumber) - .build() - .unwrap(); + + let quantity = Quantity::default(); let cmd = UnvalidatedUpdateProductCommandBuilder::default() .name("".into()) diff --git a/src/main.rs b/src/main.rs index 8976208..d595908 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,6 +17,7 @@ mod ordering; mod settings; #[cfg(test)] mod tests; +mod types; mod utils; #[actix_web::main] diff --git a/src/types/mod.rs b/src/types/mod.rs new file mode 100644 index 0000000..ea1860a --- /dev/null +++ b/src/types/mod.rs @@ -0,0 +1 @@ +pub mod quantity; diff --git a/src/types/quantity.rs b/src/types/quantity.rs new file mode 100644 index 0000000..590efca --- /dev/null +++ b/src/types/quantity.rs @@ -0,0 +1,108 @@ +// SPDX-FileCopyrightText: 2024 Aravinth Manivannan +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::str::FromStr; + +use derive_builder::Builder; +use derive_getters::Getters; +use derive_more::Display; +use serde::{Deserialize, Serialize}; + +#[derive( + Clone, Default, Display, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, +)] +pub enum QuantityUnit { + #[display(fmt = "{}", KILO_GRAM)] + Kilogram, + #[display(fmt = "{}", GRAM)] + Gram, + #[default] + #[display(fmt = "{}", DISCRETE_NUMBER)] + DiscreteNumber, // example: 1 sofa, 2 bed, etc. + #[display(fmt = "{}", MILLI_LITER)] + MilliLiter, + #[display(fmt = "{}", LITER)] + Liter, +} + +const KILO_GRAM: &str = "kg"; +const GRAM: &str = "g"; +const DISCRETE_NUMBER: &str = "discrete_number"; +const MILLI_LITER: &str = "ml"; +const LITER: &str = "l"; + +impl FromStr for QuantityUnit { + type Err = String; + fn from_str(s: &str) -> Result { + match s.trim() { + KILO_GRAM => Ok(Self::Kilogram), + GRAM => Ok(Self::Gram), + DISCRETE_NUMBER => Ok(Self::DiscreteNumber), + MILLI_LITER => Ok(Self::MilliLiter), + LITER => Ok(Self::Liter), + _ => Err("Currency unsupported".into()), + } + } +} + +#[derive( + Clone, Debug, Default, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Builder, Getters, +)] +pub struct QuantityPart { + number: usize, + unit: QuantityUnit, +} + +#[derive( + Clone, Default, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Builder, Getters, +)] +pub struct Quantity { + minor: QuantityPart, + major: QuantityPart, +} + +#[cfg(test)] +mod tests { + use super::*; + + fn test_helper(t: T, str_value: &str) -> bool + where + T: ToString + FromStr + std::fmt::Debug + PartialEq, + ::Err: std::fmt::Debug, + { + println!("Testing type: {:?} against value {str_value}", t); + assert_eq!(t.to_string(), str_value.to_string()); + + assert_eq!(T::from_str(str_value).unwrap(), t); + + assert_eq!(T::from_str(t.to_string().as_str()).unwrap(), t,); + + true + } + + #[test] + fn quantity_unit_kilogram() { + assert!(test_helper(QuantityUnit::Kilogram, KILO_GRAM)); + } + + #[test] + fn quantity_unit_gram() { + assert!(test_helper(QuantityUnit::Gram, GRAM)); + } + + #[test] + fn quantity_unit_discrete_number() { + assert!(test_helper(QuantityUnit::DiscreteNumber, DISCRETE_NUMBER)); + } + + #[test] + fn quantity_unit_milli_liter() { + assert!(test_helper(QuantityUnit::MilliLiter, MILLI_LITER)); + } + + #[test] + fn quantity_unit_liter() { + assert!(test_helper(QuantityUnit::Liter, LITER)); + } +}