From 6677f41af2d59f232f0bc8ec26d4c29dd1245f7b Mon Sep 17 00:00:00 2001 From: Sebastian Kuzminsky Date: Thu, 13 Mar 2025 15:58:47 -0600 Subject: [PATCH] move Quantity to its own module, add some Traits for convenience --- tools/src/lib.rs | 3 ++ tools/src/quantity.rs | 64 +++++++++++++++++++++++++++++++++++++++++++ tools/src/recipe.rs | 36 ++---------------------- tools/src/repo.rs | 2 +- tools/src/repos.rs | 9 ++---- 5 files changed, 73 insertions(+), 41 deletions(-) create mode 100644 tools/src/quantity.rs diff --git a/tools/src/lib.rs b/tools/src/lib.rs index c9b6ed3..1a93fbd 100644 --- a/tools/src/lib.rs +++ b/tools/src/lib.rs @@ -1,3 +1,6 @@ +pub mod quantity; +pub use quantity::*; + pub mod recipe; pub use recipe::Recipe; diff --git a/tools/src/quantity.rs b/tools/src/quantity.rs new file mode 100644 index 0000000..54a8f0c --- /dev/null +++ b/tools/src/quantity.rs @@ -0,0 +1,64 @@ +#[derive(Clone, Copy, Debug, PartialEq, serde::Deserialize)] +pub enum Unit { + Foot, + Gram, + Liter, + Meter, + USDollar, +} + +/// `Quantity` measures the amount of a resource (Input or Output). +#[derive(Clone, Copy, PartialEq, serde::Deserialize)] +pub struct Quantity { + pub amount: f32, + pub unit: Option, +} + +impl std::default::Default for Quantity { + fn default() -> Self { + Self { + amount: 1.0, + unit: None, + } + } +} + +impl std::fmt::Debug for Quantity { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.unit { + None => write!(f, "{:#}", self.amount), + Some(unit) => write!(f, "{:#} {:?}", self.amount, unit), + } + } +} + +impl std::ops::Mul for Quantity { + type Output = Self; + fn mul(self, rhs: f32) -> Self { + Self { + unit: self.unit, + amount: self.amount * rhs, + } + } +} + +impl std::ops::Mul for Quantity { + type Output = Self; + fn mul(self, rhs: usize) -> Self { + Self { + unit: self.unit, + amount: self.amount * (rhs as f32), + } + } +} + +impl std::ops::Add<&Quantity> for Quantity { + type Output = Self; + fn add(self, rhs: &Self) -> Self { + assert_eq!(self.unit, rhs.unit); + Self { + unit: self.unit, + amount: self.amount + rhs.amount, + } + } +} diff --git a/tools/src/recipe.rs b/tools/src/recipe.rs index 009b55c..a03d9f9 100644 --- a/tools/src/recipe.rs +++ b/tools/src/recipe.rs @@ -1,3 +1,5 @@ +use crate::quantity::*; + #[derive(Debug, thiserror::Error)] pub enum RecipeLoadError { #[error(transparent)] @@ -6,40 +8,6 @@ pub enum RecipeLoadError { TomlDeserializeError(#[from] toml::de::Error), } -#[derive(Clone, Copy, Debug, PartialEq, serde::Deserialize)] -pub enum Unit { - Foot, - Gram, - Liter, - Meter, - USDollar, -} - -/// `Quantity` measures the amount of a resource (Input or Output). -#[derive(Clone, Copy, PartialEq, serde::Deserialize)] -pub struct Quantity { - pub amount: f32, - pub unit: Option, -} - -impl std::default::Default for Quantity { - fn default() -> Self { - Self { - amount: 1.0, - unit: None, - } - } -} - -impl std::fmt::Debug for Quantity { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match &self.unit { - None => write!(f, "{:#}", self.amount), - Some(unit) => write!(f, "{:#} {:?}", self.amount, unit), - } - } -} - #[derive(Debug, serde::Deserialize, PartialEq)] pub struct Input { // Quantity defaults to "amount=1" if omitted. diff --git a/tools/src/repo.rs b/tools/src/repo.rs index bfb1d5f..0a8b795 100644 --- a/tools/src/repo.rs +++ b/tools/src/repo.rs @@ -134,7 +134,7 @@ impl Repo { .ok_or(anyhow::Error::msg(format!( "can't find input capital for {input_name}" )))?; - if input_capital.quantity.unit != Some(crate::recipe::Unit::USDollar) { + if input_capital.quantity.unit != Some(crate::quantity::Unit::USDollar) { return Err(anyhow::Error::msg(format!( "{} input capital does not have units USDollar", input_name diff --git a/tools/src/repos.rs b/tools/src/repos.rs index 7859070..c79491d 100644 --- a/tools/src/repos.rs +++ b/tools/src/repos.rs @@ -128,17 +128,14 @@ impl Repos { // A Recipe whose only input is Capital is a Vitamin. let cost = match input_recipe.is_vitamin() { true => { - let amount_needed = crate::recipe::Quantity { - unit: input_info.quantity.unit, - amount: quantity as f32 * input_info.quantity.amount, - }; + let amount_needed = input_info.quantity * quantity; // FIXME: for now Vitamins must have exactly one Input, and it must be Capital. assert_eq!(input_recipe.inputs.len(), 1); let input_capital = input_recipe.inputs.get("capital").ok_or_else(|| { RecipeCompileError::VitaminLacksCapital(input_name.into()) })?; - if input_capital.quantity.unit != Some(crate::recipe::Unit::USDollar) { + if input_capital.quantity.unit != Some(crate::quantity::Unit::USDollar) { return Err(RecipeCompileError::VitaminCapitalHasWrongUnit( input_name.into(), )); @@ -218,7 +215,7 @@ impl Repos { if !input_recipe.is_vitamin() { self.compile_inner( puml_file, - (quantity as f32 * input_info.quantity.amount) as usize, // FIXME: pretty dodgy + (input_info.quantity * quantity).amount as usize, // FIXME: pretty dodgy input_name, input_recipe, indent + 4,